github.com/profzone/eden-framework@v1.0.10/internal/generator/scanner/comment_scanner.go (about) 1 package scanner 2 3 import ( 4 "go/ast" 5 "go/token" 6 "sort" 7 "strings" 8 ) 9 10 func NewCommentScanner(fileSet *token.FileSet, file *ast.File) *CommentScanner { 11 commentMap := ast.NewCommentMap(fileSet, file, file.Comments) 12 13 return &CommentScanner{ 14 file: file, 15 CommentMap: commentMap, 16 } 17 } 18 19 type CommentScanner struct { 20 file *ast.File 21 CommentMap ast.CommentMap 22 } 23 24 func (scanner *CommentScanner) CommentsOf(targetNode ast.Node) string { 25 commentGroupList := scanner.CommentGroupListOf(targetNode) 26 return StringifyCommentGroup(commentGroupList...) 27 } 28 29 func (scanner *CommentScanner) CommentGroupListOf(targetNode ast.Node) (commentGroupList []*ast.CommentGroup) { 30 if targetNode == nil { 31 return 32 } 33 34 switch targetNode.(type) { 35 case *ast.File, *ast.Field, ast.Stmt, ast.Decl: 36 if comments, ok := scanner.CommentMap[targetNode]; ok { 37 commentGroupList = comments 38 } 39 case ast.Spec: 40 // Spec should merge with comments of its parent gen decl when empty 41 if comments, ok := scanner.CommentMap[targetNode]; ok { 42 commentGroupList = append(commentGroupList, comments...) 43 } 44 45 if len(commentGroupList) == 0 { 46 for node, comments := range scanner.CommentMap { 47 if genDecl, ok := node.(*ast.GenDecl); ok { 48 for _, spec := range genDecl.Specs { 49 if targetNode == spec { 50 commentGroupList = append(commentGroupList, comments...) 51 } 52 } 53 } 54 } 55 } 56 default: 57 // find nearest parent node which have comments 58 { 59 var deltaPos token.Pos 60 var parentNode ast.Node 61 62 deltaPos = -1 63 64 ast.Inspect(scanner.file, func(node ast.Node) bool { 65 switch node.(type) { 66 case *ast.Field, ast.Decl, ast.Spec, ast.Stmt: 67 if targetNode.Pos() >= node.Pos() && targetNode.End() <= node.End() { 68 nextDelta := targetNode.Pos() - node.Pos() 69 if deltaPos == -1 || (nextDelta <= deltaPos) { 70 deltaPos = nextDelta 71 parentNode = node 72 } 73 } 74 } 75 return true 76 }) 77 78 if parentNode != nil { 79 commentGroupList = scanner.CommentGroupListOf(parentNode) 80 } 81 } 82 } 83 84 sort.Sort(ByCommentPos(commentGroupList)) 85 return 86 } 87 88 type ByCommentPos []*ast.CommentGroup 89 90 func (a ByCommentPos) Len() int { 91 return len(a) 92 } 93 94 func (a ByCommentPos) Swap(i, j int) { 95 a[i], a[j] = a[j], a[i] 96 } 97 98 func (a ByCommentPos) Less(i, j int) bool { 99 return a[i].Pos() < a[j].Pos() 100 } 101 102 func StringifyCommentGroup(commentGroupList ...*ast.CommentGroup) (comments string) { 103 if len(commentGroupList) == 0 { 104 return "" 105 } 106 for _, commentGroup := range commentGroupList { 107 for _, line := range strings.Split(commentGroup.Text(), "\n") { 108 if strings.HasPrefix(line, "go:generate") { 109 continue 110 } 111 comments = comments + "\n" + line 112 } 113 } 114 return strings.TrimSpace(comments) 115 }