Skip to content

Commit 0ae1ae2

Browse files
committed
Add consolidated events documentation
1 parent 057a6f5 commit 0ae1ae2

28 files changed

Lines changed: 535 additions & 66 deletions

lion/internal/extractor/extractor.go

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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"
3335
func 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

7375
func 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"
140180
func 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
*/
301341
func parseLionBlockComment(text string) (string, string, metaInfo, error) {
302342
// Remove "lion:" prefix

lion/internal/extractor/extractor_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,51 @@ func Function2() {}
158158
}
159159
}
160160

161+
func TestExtractInlineComments(t *testing.T) {
162+
tmpDir := t.TempDir()
163+
164+
// Create file with inline lion comments inside a function body
165+
file := filepath.Join(tmpDir, "inline.go")
166+
content := `package inline
167+
168+
func Execute() {
169+
//lion:inline-semantics section="execute"
170+
//lion:inline-semantics Describes runtime behavior inside the function body.
171+
helper()
172+
//lion:inline-semantics Additional details that should join the same entry.
173+
}
174+
`
175+
176+
if err := os.WriteFile(file, []byte(content), 0644); err != nil {
177+
t.Fatalf("failed to create inline file: %v", err)
178+
}
179+
180+
docs, err := Extract(tmpDir)
181+
if err != nil {
182+
t.Fatalf("Extract failed: %v", err)
183+
}
184+
185+
entries, ok := docs["inline-semantics"]
186+
if !ok {
187+
t.Fatalf("expected inline-semantics topic to be extracted")
188+
}
189+
190+
if len(entries) != 3 {
191+
t.Fatalf("expected 3 entries, got %d", len(entries))
192+
}
193+
194+
for _, entry := range entries {
195+
if entry.Entity != "Execute" {
196+
t.Fatalf("expected entity Execute, got %s", entry.Entity)
197+
}
198+
}
199+
200+
combined := entries[0].Content + entries[1].Content + entries[2].Content
201+
if !strings.Contains(combined, "runtime behavior") || !strings.Contains(combined, "Additional details") {
202+
t.Fatalf("unexpected combined content: %q", combined)
203+
}
204+
}
205+
161206
func TestExtractSkipsTestFiles(t *testing.T) {
162207
tmpDir := t.TempDir()
163208

@@ -424,7 +469,7 @@ func TestExtractMarkerAtTopWithFollowingComments(t *testing.T) {
424469

425470
// Create test Go file with marker-at-top format that gathers following lines
426471
testFile := filepath.Join(tmpDir, "test.go")
427-
content := `//lion:overview section="Overview section"
472+
content := `//lion:overview section="Overview section"
428473
//
429474
// This is a multi-line comment.
430475
// It describes the functionality.

tk/docs/event.project.alias.add.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Event.project.alias.add
2+
3+
## project.alias.add
4+
5+
*Source: `tk/internal/reducer/project.go:26`*
6+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Event.project.alias.remove
2+
3+
## project.alias.remove
4+
5+
*Source: `tk/internal/reducer/project.go:32`*
6+

tk/docs/event.project.created.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Event.project.created
2+
3+
## project.created
4+
5+
*Source: `tk/internal/reducer/project.go:20`*
6+

tk/docs/event.project.delete.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Event.project.delete
2+
3+
## project.delete
4+
5+
*Source: `tk/internal/reducer/project.go:38`*
6+

tk/docs/event.project.name.set.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Event.project.name.set
2+
3+
## project.name.set
4+
5+
*Source: `tk/internal/reducer/project.go:44`*
6+

tk/docs/event.relation.add.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Event.relation.add
2+
3+
## relation.add
4+
5+
*Source: `tk/internal/reducer/reducer.go:49`*
6+

tk/docs/event.relation.note.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Event.relation.note
2+
3+
## relation.note
4+
5+
*Source: `tk/internal/reducer/reducer.go:61`*
6+

tk/docs/event.relation.remove.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Event.relation.remove
2+
3+
## relation.remove
4+
5+
*Source: `tk/internal/reducer/reducer.go:55`*
6+

0 commit comments

Comments
 (0)