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  }