@@ -23,13 +23,15 @@ type DocEntry struct {
2323 HasSection bool
2424}
2525
26- //lion:implementation section="Extraction pipeline"
2726// Extraction pipeline:
2827// - Walks all .go files under the directory, skipping *_test.go.
29- // - Parses with comments and pulls lion markers from package doc, func doc, and type/const/var
30- // doc comments (first name in a const/var block is used as the entity).
28+ // - Parses with comments and pulls lion markers from package doc, func doc, type/const/var
29+ // doc comments, and inline comments inside functions and other declarations (first name in a
30+ // const/var block is used as the entity).
3131// - Supports single-line markers and block comment markers (marker at top of the doc block).
3232// - Aggregates snippets per topic across files; generator writes one file per topic.
33+ //
34+ //lion:implementation section="Extraction pipeline"
3335func Extract (dir string ) (map [string ][]DocEntry , error ) {
3436 docs := make (map [string ][]DocEntry )
3537 fset := token .NewFileSet ()
@@ -71,34 +73,55 @@ func Extract(dir string) (map[string][]DocEntry, error) {
7173}
7274
7375func extractFromFile (fset * token.FileSet , file * ast.File , filepath string , docs map [string ][]DocEntry ) error {
76+ processed := make (map [* ast.CommentGroup ]bool )
77+
7478 // Extract package-level comments
7579 if file .Doc != nil {
80+ processed [file .Doc ] = true
7681 if err := extractFromCommentGroup (fset , file .Doc , filepath , "package " + file .Name .Name , docs ); err != nil {
7782 return err
7883 }
7984 }
8085
81- var parseErr error
82- ast . Inspect ( file , func ( n ast.Node ) bool {
83- if parseErr != nil {
84- return false
85- }
86- switch decl := n .(type ) {
86+ var funcDecls [] * ast. FuncDecl
87+ var genDecls [] * ast.GenDecl
88+
89+ // Extract doc comments from declarations and record them as processed
90+ for _ , decl := range file . Decls {
91+ switch d := decl .(type ) {
8792 case * ast.FuncDecl :
88- if decl .Doc != nil {
89- parseErr = extractFromCommentGroup (fset , decl .Doc , filepath , decl .Name .Name , docs )
93+ funcDecls = append (funcDecls , d )
94+ if d .Doc != nil {
95+ processed [d .Doc ] = true
96+ if err := extractFromCommentGroup (fset , d .Doc , filepath , d .Name .Name , docs ); err != nil {
97+ return err
98+ }
9099 }
91100 case * ast.GenDecl :
92- if decl .Doc != nil {
93- // For type/const/var declarations
94- entityName := getEntityName (decl )
95- parseErr = extractFromCommentGroup (fset , decl .Doc , filepath , entityName , docs )
101+ genDecls = append (genDecls , d )
102+ if d .Doc != nil {
103+ processed [d .Doc ] = true
104+ entityName := getEntityName (d )
105+ if err := extractFromCommentGroup (fset , d .Doc , filepath , entityName , docs ); err != nil {
106+ return err
107+ }
96108 }
97109 }
98- return true
99- })
110+ }
111+
112+ // Extract inline lion comment groups that were not attached as doc comments
113+ for _ , cg := range file .Comments {
114+ if processed [cg ] {
115+ continue
116+ }
117+
118+ entityName := determineEntityForComment (cg , file , funcDecls , genDecls )
119+ if err := extractFromCommentGroup (fset , cg , filepath , entityName , docs ); err != nil {
120+ return err
121+ }
122+ }
100123
101- return parseErr
124+ return nil
102125}
103126
104127// getEntityName extracts the entity name from a general declaration.
@@ -116,8 +139,24 @@ func getEntityName(decl *ast.GenDecl) string {
116139 return ""
117140}
118141
119- //lion:supported-syntax section="Supported syntax"
120- //
142+ func determineEntityForComment (cg * ast.CommentGroup , file * ast.File , funcDecls []* ast.FuncDecl , genDecls []* ast.GenDecl ) string {
143+ for _ , fn := range funcDecls {
144+ if fn .Body != nil && cg .Pos () >= fn .Body .Pos () && cg .End () <= fn .Body .End () {
145+ return fn .Name .Name
146+ }
147+ }
148+
149+ for _ , gd := range genDecls {
150+ if cg .Pos () >= gd .Pos () && cg .End () <= gd .End () {
151+ if name := getEntityName (gd ); name != "" {
152+ return name
153+ }
154+ }
155+ }
156+
157+ return "package " + file .Name .Name
158+ }
159+
121160// Supported syntax formats:
122161//
123162// 1. Single-line marker first:
@@ -137,6 +176,7 @@ func getEntityName(decl *ast.GenDecl) string {
137176// All formats attach documentation to the next declaration (function, type, const, var).
138177//
139178//lion:supported-syntax section="Supported syntax"
179+ //lion:supported-syntax section="Supported syntax"
140180func extractFromCommentGroup (fset * token.FileSet , cg * ast.CommentGroup , filepath , entityName string , docs map [string ][]DocEntry ) error {
141181 topicGroups := make (map [string ][]string )
142182 topicOrder := []string {}
@@ -290,13 +330,13 @@ func parseLionCommentLine(text string) (string, string, metaInfo, error) {
290330}
291331
292332/*
293- lion:errors-and-validation section="Error handling"
294- Validation and error handling:
295- - Invalid or empty topics are ignored silently (no explicit validation yet).
296- - Conflicting topic/section titles within the same comment group fail extraction with file:line.
297- - Comments that do not attach to package/func/type/const/var doc groups are skipped.
298- - CLI exits non-zero only when extraction fails (parse/metadata error) or generation fails (write error).
299- - Bad markers inside otherwise valid files do not stop extraction; they are just skipped.
333+ lion:errors-and-validation section="Error handling"
334+ Validation and error handling:
335+ - Invalid or empty topics are ignored silently (no explicit validation yet).
336+ - Conflicting topic/section titles within the same comment group fail extraction with file:line.
337+ - Comments that do not attach to package/func/type/const/var doc groups are skipped.
338+ - CLI exits non-zero only when extraction fails (parse/metadata error) or generation fails (write error).
339+ - Bad markers inside otherwise valid files do not stop extraction; they are just skipped.
300340*/
301341func parseLionBlockComment (text string ) (string , string , metaInfo , error ) {
302342 // Remove "lion:" prefix
0 commit comments