-
Notifications
You must be signed in to change notification settings - Fork 0
CLI code-docgen function #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # Core CLI | ||
|
|
||
| This directory contains the source code for the `core` command-line interface (CLI), a tool for managing and interacting with the Core framework. | ||
|
|
||
| ## Purpose | ||
|
|
||
| The CLI provides a set of commands to streamline common development tasks, such as: | ||
|
|
||
| - Building and running applications. | ||
| - Generating tests for the public API. | ||
| - Synchronizing API definitions. | ||
| - Generating missing documentation. | ||
|
|
||
| ## Key Packages | ||
|
|
||
| The CLI's user interface is built with the help of several key packages: | ||
|
|
||
| - **`github.com/charmbracelet/lipgloss`**: A library for styling terminal output. We use it to add color and formatting to make the CLI's output more readable and visually appealing. | ||
|
|
||
| - **`github.com/common-nighthawk/go-figure`**: Used to create ASCII art text banners, giving the CLI a distinct and recognizable startup message. | ||
|
|
||
| - **`github.com/leaanthony/clir`**: A simple and lightweight library for creating CLI applications, and our primary framework for specific, simpler command needs. | ||
|
|
||
| ## Examples | ||
|
|
||
| ```go | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "github.com/leaanthony/clir" | ||
| ) | ||
|
|
||
| func main() { | ||
| // Create new cli | ||
| cli := clir.NewCli("Flags", "A simple example", "v0.0.1") | ||
|
|
||
| // Name | ||
| name := "Anonymous" | ||
| cli.StringFlag("name", "Your name", &name) | ||
|
|
||
| // Define action for the command | ||
| cli.Action(func() error { | ||
| fmt.Printf("Hello %s!\n", name) | ||
| return nil | ||
| }) | ||
|
|
||
| if err := cli.Run(); err != nil { | ||
| fmt.Printf("Error encountered: %v\n", err) | ||
| } | ||
| } | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| version: '3' | ||
|
|
||
| tasks: | ||
|
|
||
| build: | ||
| summary: Builds the core executable | ||
| cmds: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| version: '3' | ||
|
|
||
| tasks: | ||
| docgen: | ||
| summary: Generates missing docstrings for the public API | ||
| deps: [build] | ||
| cmds: | ||
| - cmd: chmod +x ../../bin/core | ||
| platforms: [linux, darwin] | ||
| - "../../bin/core dev doc" | ||
|
|
||
| test-gen: | ||
| summary: Generates tests for the public API | ||
| deps: [ build ] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove extra spaces from array brackets. YAML linting flags the inconsistent spacing in Apply this diff to fix the formatting: - deps: [ build ]
+ deps: [build]
cmds:
- - cmd: chmod +x {{.TASKFILE_DIR}}/bin/core
- platforms: [ linux, darwin ]
+ - cmd: chmod +x {{.TASKFILE_DIR}}/../../bin/core
+ platforms: [linux, darwin]
- "{{.TASKFILE_DIR}}/bin/core dev test-gen"
🧰 Tools🪛 YAMLlint (1.37.1)[error] 14-14: too many spaces inside brackets (brackets) [error] 14-14: too many spaces inside brackets (brackets) 🤖 Prompt for AI Agents |
||
| cmds: | ||
| - cmd: chmod +x {{.TASKFILE_DIR}}/bin/core | ||
| platforms: [ linux, darwin ] | ||
| - "{{.TASKFILE_DIR}}/bin/core dev test-gen" | ||
|
|
||
| sync: | ||
| summary: Updates the public API Go files | ||
| deps: [ build ] | ||
| cmds: | ||
| - cmd: chmod +x {{.TASKFILE_DIR}}/bin/core | ||
| platforms: [ linux, darwin ] | ||
|
Comment on lines
+22
to
+25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove extra spaces from array brackets. Same formatting issue as in the Apply this diff: - deps: [ build ]
+ deps: [build]
cmds:
- - cmd: chmod +x {{.TASKFILE_DIR}}/bin/core
- platforms: [ linux, darwin ]
+ - cmd: chmod +x {{.TASKFILE_DIR}}/../../bin/core
+ platforms: [linux, darwin]
- "{{.TASKFILE_DIR}}/bin/core dev sync"
🧰 Tools🪛 YAMLlint (1.37.1)[error] 22-22: too many spaces inside brackets (brackets) [error] 22-22: too many spaces inside brackets (brackets) [error] 25-25: too many spaces inside brackets (brackets) [error] 25-25: too many spaces inside brackets (brackets) 🤖 Prompt for AI Agents |
||
| - "{{.TASKFILE_DIR}}/bin/core dev sync" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package cmd | ||
| package dev | ||
|
|
||
| import ( | ||
| "github.com/leaanthony/clir" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| package dev | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "go/ast" | ||
| "go/parser" | ||
| "go/token" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
|
|
||
| "github.com/leaanthony/clir" | ||
| ) | ||
|
|
||
| // AddDocCommand adds the 'doc' command to the provided clir instance. | ||
| func AddDocCommand(parent *clir.Command) { | ||
| docCmd := parent.NewSubCommand("doc", "Generate missing docstrings for the public API.") | ||
| docCmd.LongDescription(`This command scans the public API of your project and generates placeholder docstrings for any exported functions, types, or methods that are missing them. | ||
|
|
||
| It will also report any values that cannot be determined through reflection, providing a list of file names and line numbers that require manual attention. | ||
|
|
||
| If a comment already exists, it will be preserved.`) | ||
|
|
||
| var basePath string | ||
| docCmd.StringFlag("path", "Base path to scan for Go files (defaults to current directory)", &basePath) | ||
|
|
||
| docCmd.Action(func() error { | ||
| if basePath == "" { | ||
| basePath = "." | ||
| } | ||
|
|
||
| reports := []string{} | ||
| fset := token.NewFileSet() | ||
|
|
||
| err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error { | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if info.IsDir() && (info.Name() == "vendor" || strings.HasPrefix(info.Name(), ".")) { | ||
| return filepath.SkipDir | ||
| } | ||
| if !info.IsDir() && strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, "_test.go") { | ||
| fileContent, readErr := os.ReadFile(path) | ||
| if readErr != nil { | ||
| return fmt.Errorf("failed to read file %s: %w", path, readErr) | ||
| } | ||
|
|
||
| node, parseErr := parser.ParseFile(fset, path, fileContent, parser.ParseComments) | ||
| if parseErr != nil { | ||
| return fmt.Errorf("failed to parse file %s: %w", path, parseErr) | ||
| } | ||
|
|
||
| for _, decl := range node.Decls { | ||
| switch d := decl.(type) { | ||
| case *ast.GenDecl: | ||
| if d.Doc == nil || len(d.Doc.List) == 0 { | ||
| for _, spec := range d.Specs { | ||
| switch s := spec.(type) { | ||
| case *ast.TypeSpec: | ||
| if ast.IsExported(s.Name.Name) { | ||
| reports = append(reports, fmt.Sprintf("%s:%d: Missing docstring for type %s. Suggested: // %s ...", path, fset.Position(s.Pos()).Line, s.Name.Name, s.Name.Name)) | ||
| } | ||
| case *ast.ValueSpec: // For var and const | ||
| for _, name := range s.Names { | ||
| if ast.IsExported(name.Name) { | ||
| reports = append(reports, fmt.Sprintf("%s:%d: Missing docstring for %s %s. Suggested: // %s ...", path, fset.Position(name.Pos()).Line, d.Tok.String(), name.Name, name.Name)) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+53
to
+71
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Account for spec-level doc comments When a - case *ast.GenDecl:
- if d.Doc == nil || len(d.Doc.List) == 0 {
+ case *ast.GenDecl:
+ genDocMissing := d.Doc == nil || len(d.Doc.List) == 0
for _, spec := range d.Specs {
switch s := spec.(type) {
case *ast.TypeSpec:
- if ast.IsExported(s.Name.Name) {
+ if ast.IsExported(s.Name.Name) && (genDocMissing && (s.Doc == nil || len(s.Doc.List) == 0)) {
reports = append(reports, fmt.Sprintf("%s:%d: Missing docstring for type %s. Suggested: // %s ...", path, fset.Position(s.Pos()).Line, s.Name.Name, s.Name.Name))
}
case *ast.ValueSpec: // For var and const
for _, name := range s.Names {
- if ast.IsExported(name.Name) {
+ if ast.IsExported(name.Name) && (genDocMissing && (s.Doc == nil || len(s.Doc.List) == 0)) {
reports = append(reports, fmt.Sprintf("%s:%d: Missing docstring for %s %s. Suggested: // %s ...", path, fset.Position(name.Pos()).Line, d.Tok.String(), name.Name, name.Name))
}
}
}
- }
+ } |
||
| case *ast.FuncDecl: | ||
| if d.Doc == nil || len(d.Doc.List) == 0 { | ||
| if d.Name != nil && ast.IsExported(d.Name.Name) { | ||
| funcName := d.Name.Name | ||
| if d.Recv != nil && len(d.Recv.List) > 0 { | ||
| // It's a method | ||
| recvType := "" | ||
| switch rt := d.Recv.List[0].Type.(type) { | ||
| case *ast.Ident: | ||
| recvType = rt.Name | ||
| case *ast.StarExpr: | ||
| if id, ok := rt.X.(*ast.Ident); ok { | ||
| recvType = "*" + id.Name | ||
| } | ||
| } | ||
| reports = append(reports, fmt.Sprintf("%s:%d: Missing docstring for method (%s) %s. Suggested: // (%s) %s ...", path, fset.Position(d.Pos()).Line, recvType, funcName, recvType, funcName)) | ||
| } else { | ||
| // It's a function | ||
| reports = append(reports, fmt.Sprintf("%s:%d: Missing docstring for function %s. Suggested: // %s ...", path, fset.Position(d.Pos()).Line, funcName, funcName)) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return nil | ||
| }) | ||
|
|
||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, "Error walking through files: %v\n", err) | ||
| return err | ||
| } | ||
|
|
||
| if len(reports) == 0 { | ||
| fmt.Println("No missing docstrings found for exported declarations.") | ||
| } else { | ||
| fmt.Println("Missing docstrings found:") | ||
| for _, r := range reports { | ||
| fmt.Println(r) | ||
| } | ||
| } | ||
| return nil | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package cmd | ||
| package dev | ||
|
|
||
| import ( | ||
| "bytes" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package cmd | ||
| package dev | ||
|
|
||
| import ( | ||
| "bytes" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix path inconsistency.
The
docgentask uses relative paths (../../bin/core) whilst the other tasks use{{.TASKFILE_DIR}}/bin/core. This inconsistency could cause issues if the Taskfile location changes or when tasks are invoked from different contexts.Apply this diff to use consistent path references:
📝 Committable suggestion
🤖 Prompt for AI Agents