diff --git a/comment.go b/comment.go index c0bb539..8de9fc5 100644 --- a/comment.go +++ b/comment.go @@ -119,6 +119,21 @@ func (c *Comment) Tags() []string { return res } +func (c *Comment) Description() []string { + if c == nil { + return nil + } + + var res []string + for _, annot := range c.Annotations { + desc, ok := annot.(*annotation.DescriptionAnnotation) + if ok { + res = append(res, desc.Text) + } + } + return res +} + func (c *Comment) Ignore() bool { if c == nil { return false @@ -145,6 +160,9 @@ func (c *Comment) Summary() string { } func (c *Comment) ID() string { + if c == nil { + return "" + } for _, annot := range c.Annotations { id, ok := annot.(*annotation.IdAnnotation) if ok { @@ -192,10 +210,6 @@ func ParseComment(commentGroup *ast.CommentGroup, fSet *token.FileSet) *Comment } if annot != nil { c.Annotations = append(c.Annotations, annot) - desc, ok := annot.(*annotation.DescriptionAnnotation) - if ok { - lines = append(lines, strings.TrimSpace(desc.Text)) - } } else { line := strings.TrimPrefix(comment.Text, "//") lines = append(lines, strings.TrimSpace(line)) diff --git a/context.go b/context.go index ab13240..8200a77 100644 --- a/context.go +++ b/context.go @@ -133,8 +133,9 @@ func (c *Context) GetHeadingCommentOf(pos token.Pos) *ast.CommentGroup { position := c.Package().Fset.Position(pos) for _, commentGroup := range c.File().Comments { - commentPos := c.Package().Fset.Position(commentGroup.End()) - if commentPos.Line == position.Line-1 { + start := c.Package().Fset.Position(commentGroup.Pos()) + end := c.Package().Fset.Position(commentGroup.End()) + if end.Line == position.Line-1 && start.Column == position.Column { return commentGroup } } diff --git a/plugins/echo/echo.go b/plugins/echo/echo.go index bb5aa71..e391d2b 100644 --- a/plugins/echo/echo.go +++ b/plugins/echo/echo.go @@ -117,11 +117,11 @@ func (p *Plugin) callExpr(ctx *eapi.Context, callExpr *ast.CallExpr) { callExpr, callRule, func(call *ast.CallExpr, typeName, fnName string) { - comment := ctx.ParseComment(ctx.GetHeadingCommentOf(call.Lparen)) + comment := ctx.ParseComment(ctx.GetHeadingCommentOf(call.Pos())) if comment.Ignore() { return } - api := p.parseAPI(ctx, callExpr) + api := p.parseAPI(ctx, callExpr, comment) if api == nil { return } @@ -130,7 +130,7 @@ func (p *Plugin) callExpr(ctx *eapi.Context, callExpr *ast.CallExpr) { ) } -func (e *Plugin) parseAPI(ctx *eapi.Context, callExpr *ast.CallExpr) (api *eapi.API) { +func (e *Plugin) parseAPI(ctx *eapi.Context, callExpr *ast.CallExpr, comment *eapi.Comment) (api *eapi.API) { if len(callExpr.Args) < 2 { return } @@ -162,9 +162,14 @@ func (e *Plugin) parseAPI(ctx *eapi.Context, callExpr *ast.CallExpr) (api *eapi. fullPath := path.Join(prefix, e.normalizePath(strings.Trim(arg0.Value, "\""))) method := selExpr.Sel.Name api = eapi.NewAPI(method, fullPath) + api.Spec.LoadFromComment(ctx, comment) api.Spec.LoadFromFuncDecl(ctx, handlerFnDef.Decl) if api.Spec.OperationID == "" { - api.Spec.OperationID = handlerFnDef.Pkg().Name + "." + handlerFnDef.Decl.Name.Name + id := comment.ID() + if id == "" { + id = handlerFnDef.Pkg().Name + "." + handlerFnDef.Decl.Name.Name + } + api.Spec.OperationID = id } newHandlerAnalyzer( ctx.NewEnv().WithPackage(handlerFnDef.Pkg()).WithFile(handlerFnDef.File()), diff --git a/plugins/echo/handler_analyzer.go b/plugins/echo/handler_analyzer.go index 3f652ce..9a1bba8 100644 --- a/plugins/echo/handler_analyzer.go +++ b/plugins/echo/handler_analyzer.go @@ -200,7 +200,7 @@ func (p *handlerAnalyzer) parseFormData(call *ast.CallExpr, fieldType string, op p.spec.RequestBody.Value.Content[analyzer.MimeTypeFormData] = mediaType } - comment := p.ctx.ParseComment(p.ctx.GetHeadingCommentOf(call.Lparen)) + comment := p.ctx.ParseComment(p.ctx.GetHeadingCommentOf(call.Pos())) paramSchema.Description = comment.Text() if comment.Required() { schema.Value.Required = append(schema.Value.Required, name) diff --git a/plugins/gin/gin.go b/plugins/gin/gin.go index 93da152..11a5954 100644 --- a/plugins/gin/gin.go +++ b/plugins/gin/gin.go @@ -130,11 +130,11 @@ func (e *Plugin) callExpr(ctx *analyzer.Context, callExpr *ast.CallExpr) { callExpr, callRule, func(call *ast.CallExpr, typeName, fnName string) { - comment := ctx.ParseComment(ctx.GetHeadingCommentOf(call.Lparen)) + comment := ctx.ParseComment(ctx.GetHeadingCommentOf(call.Pos())) if comment.Ignore() { return } - api := e.parseAPI(ctx, callExpr) + api := e.parseAPI(ctx, callExpr, comment) if api == nil { return } @@ -143,7 +143,7 @@ func (e *Plugin) callExpr(ctx *analyzer.Context, callExpr *ast.CallExpr) { ) } -func (e *Plugin) parseAPI(ctx *analyzer.Context, callExpr *ast.CallExpr) (api *analyzer.API) { +func (e *Plugin) parseAPI(ctx *analyzer.Context, callExpr *ast.CallExpr, comment *analyzer.Comment) (api *analyzer.API) { if len(callExpr.Args) < 2 { return } @@ -175,9 +175,14 @@ func (e *Plugin) parseAPI(ctx *analyzer.Context, callExpr *ast.CallExpr) (api *a fullPath := path.Join(prefix, e.normalizePath(strings.Trim(arg0.Value, "\""))) method := selExpr.Sel.Name api = analyzer.NewAPI(method, fullPath) + api.Spec.LoadFromComment(ctx, comment) api.Spec.LoadFromFuncDecl(ctx, handlerFnDef.Decl) if api.Spec.OperationID == "" { - api.Spec.OperationID = handlerFnDef.Pkg().Name + "." + handlerFnDef.Decl.Name.Name + id := comment.ID() + if id == "" { + id = handlerFnDef.Pkg().Name + "." + handlerFnDef.Decl.Name.Name + } + api.Spec.OperationID = id } newHandlerParser( ctx.NewEnv().WithPackage(handlerFnDef.Pkg()).WithFile(handlerFnDef.File()), diff --git a/plugins/gin/handler_analyzer.go b/plugins/gin/handler_analyzer.go index d33ab29..6d87f7a 100644 --- a/plugins/gin/handler_analyzer.go +++ b/plugins/gin/handler_analyzer.go @@ -257,7 +257,7 @@ func (p *handlerAnalyzer) parseFormData(call *ast.CallExpr, fieldType string, op p.spec.RequestBody.Value.Content[analyzer.MimeTypeFormData] = mediaType } - comment := p.ctx.ParseComment(p.ctx.GetHeadingCommentOf(call.Lparen)) + comment := p.ctx.ParseComment(p.ctx.GetHeadingCommentOf(call.Pos())) paramSchema.Description = comment.Text() if comment.Required() { schema.Value.Required = append(schema.Value.Required, name) diff --git a/route.go b/route.go index 4041a54..249f0a3 100644 --- a/route.go +++ b/route.go @@ -77,17 +77,36 @@ func NewAPISpec() *APISpec { func (s *APISpec) LoadFromFuncDecl(ctx *Context, funcDecl *ast.FuncDecl) { cg := funcDecl.Doc comment := ParseComment(cg, ctx.Package().Fset) + s.LoadFromComment(ctx, comment) + if s.Description == "" { + // 使用注释里的普通文本作为描述 + s.Description = strings.TrimSpace(strings.TrimPrefix(comment.Text(), funcDecl.Name.Name)) + } +} + +func (s *APISpec) LoadFromComment(ctx *Context, comment *Comment) { if comment != nil { - s.Summary = comment.Summary() - s.Description = strings.TrimSpace(comment.TrimPrefix(funcDecl.Name.Name)) + if s.Description == "" { + s.Description = strings.Join(comment.Description(), "\n\n") + } if s.Summary == "" { - s.Summary = s.Description + s.Summary = comment.Summary() + } + if len(s.Tags) == 0 { + s.Tags = comment.Tags() + } + if s.OperationID == "" { + s.OperationID = comment.ID() + } + if len(s.Consumes) == 0 { + s.Consumes = append(s.Consumes, comment.Consumes()...) + } + if !s.Deprecated { + comment.Deprecated() + } + if s.Security == nil { + s.Security = comment.Security() } - s.Tags = comment.Tags() - s.OperationID = comment.ID() - s.Consumes = append(s.Consumes, comment.Consumes()...) - s.Deprecated = comment.Deprecated() - s.Security = comment.Security() } if len(s.Tags) == 0 { s.Tags = ctx.Env.LookupTags()