From aae932b1f360957b5d1bb9eb3ea587b2a6b75e18 Mon Sep 17 00:00:00 2001 From: Ian Lewis Date: Wed, 30 Oct 2024 04:01:20 +0000 Subject: [PATCH 1/4] Add --label GLOB filter A new `--label` flag is added that takes a GLOB and matches against TODO labels. The flag can be included multilpe times. A TODO that matches any label is included in the output. Fixes #1562 Signed-off-by: Ian Lewis --- internal/cmd/todos/app.go | 43 +++++++++++++++++++-------- internal/walker/walker.go | 25 ++++++++++++++-- internal/walker/walker_test.go | 54 ++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 15 deletions(-) diff --git a/internal/cmd/todos/app.go b/internal/cmd/todos/app.go index 619aa9fe..185d92f7 100644 --- a/internal/cmd/todos/app.go +++ b/internal/cmd/todos/app.go @@ -86,9 +86,11 @@ func newTODOsApp() *cli.App { Name: filepath.Base(os.Args[0]), Usage: "Search for TODOS in code.", Flags: []cli.Flag{ + // Flags for functionality are in alphabetical order. &cli.BoolFlag{ - Name: "exclude-hidden", - Usage: "exclude hidden files and directories", + Name: "blame", + Usage: "[BETA] attempt to find committer info", + Value: false, DisableDefaultText: true, }, &cli.StringFlag{ @@ -105,6 +107,11 @@ func newTODOsApp() *cli.App { Name: "exclude-dir", Usage: "exclude directories that match `GLOB`", }, + &cli.BoolFlag{ + Name: "exclude-hidden", + Usage: "exclude hidden files and directories", + DisableDefaultText: true, + }, &cli.BoolFlag{ Name: "include-vcs", Usage: "include version control directories (.git, .hg, .svn)", @@ -122,16 +129,10 @@ func newTODOsApp() *cli.App { Value: false, DisableDefaultText: true, }, - &cli.BoolFlag{ - Name: "blame", - Usage: "[BETA] attempt to find committer info", - Value: false, - DisableDefaultText: true, - }, - &cli.StringFlag{ - Name: "todo-types", - Usage: "comma separated list of TODO `TYPES`", - Value: strings.Join(todos.DefaultTypes, ","), + &cli.StringSliceFlag{ + Name: "label", + Usage: "only output TODOs that match `GLOB`", + Aliases: []string{"l"}, }, &cli.StringFlag{ Name: "output", @@ -139,6 +140,13 @@ func newTODOsApp() *cli.App { Value: defaultOutput, Aliases: []string{"o"}, }, + &cli.StringFlag{ + Name: "todo-types", + Usage: "comma separated list of TODO `TYPES`", + Value: strings.Join(todos.DefaultTypes, ","), + }, + + // Special flags are shown at the end. &cli.BoolFlag{ Name: "help", Usage: "print this help text and exit", @@ -358,11 +366,22 @@ func walkerOptionsFromContext(c *cli.Context) (*walker.Options, error) { } o.Blame = c.Bool("blame") + + // File Includes o.IncludeGenerated = c.Bool("include-generated") o.IncludeHidden = !c.Bool("exclude-hidden") o.IncludeVCS = c.Bool("include-vcs") o.IncludeVendored = c.Bool("include-vendored") + // Filters + for _, label := range c.StringSlice("label") { + g, err := glob.Compile(label) + if err != nil { + return nil, fmt.Errorf("%w: label: %w", ErrFlagParse, err) + } + o.LabelGlobs = append(o.LabelGlobs, g) + } + outType := c.String("output") outFunc, ok := outTypes[outType] if !ok { diff --git a/internal/walker/walker.go b/internal/walker/walker.go index 95a0b612..92cd76ec 100644 --- a/internal/walker/walker.go +++ b/internal/walker/walker.go @@ -65,6 +65,10 @@ type Options struct { // ErrorFunc handles when errors are found. ErrorFunc ErrorHandler + // Blame indicates that the walker should attempt to find the git committer + // that committed each TODO. + Blame bool + // Config is the config for scanning todos. Config *todos.Config @@ -93,9 +97,8 @@ type Options struct { // IncludeVCS indicates that VCS paths (.git, .hg, .svn, etc.) should be included. IncludeVCS bool - // Blame indicates that the walker should attempt to find the git committer - // that committed each TODO. - Blame bool + // LabelGlobs is a list of Glob to filter TODOs by label. + LabelGlobs []glob.Glob // Paths are the paths to walk to look for TODOs. Paths []string @@ -313,8 +316,24 @@ func (w *TODOWalker) scanFile(f *os.File, force bool) error { return nil } t := todos.NewTODOScanner(s, w.options.Config) +scanL: for t.Scan() { todo := t.Next() + + // Check the label globs to see if any match. + if len(w.options.LabelGlobs) > 0 { + labelMatch := false + for _, g := range w.options.LabelGlobs { + if g.Match(todo.Label) { + labelMatch = true + break + } + } + if !labelMatch { + continue scanL + } + } + if w.options.TODOFunc != nil { var gitUser *GitUser repo, br, gitUser, err = w.gitUser(f.Name(), repo, br, todo.Line) diff --git a/internal/walker/walker_test.go b/internal/walker/walker_test.go index caa38f2b..b2b42216 100644 --- a/internal/walker/walker_test.go +++ b/internal/walker/walker_test.go @@ -946,6 +946,60 @@ var testCases = []testCase{ }, }, }, + { + name: "label glob filter", + files: []*testutils.File{ + { + Path: "line_comments.go", + Contents: []byte(`package foo + // package comment + + // TODO: no label + // TODO(no-match): no match + + // TODO is a function. + // TODO(todo-label): some task. + func TODO() { + return // TODO(other-label): Return comment + }`), + Mode: 0o600, + }, + }, + opts: &Options{ + Config: &todos.Config{ + Types: []string{"TODO"}, + }, + LabelGlobs: []glob.Glob{ + glob.MustCompile("todo-*"), + glob.MustCompile("other-*"), + }, + Charset: "UTF-8", + }, + expected: []*TODORef{ + { + FileName: "line_comments.go", + TODO: &todos.TODO{ + Type: "TODO", + Text: "// TODO(todo-label): some task.", + Message: "some task.", + Label: "todo-label", + Line: 8, + CommentLine: 8, + }, + }, + { + FileName: "line_comments.go", + TODO: &todos.TODO{ + Type: "TODO", + Text: "// TODO(other-label): Return comment", + Message: "Return comment", + Label: "other-label", + Line: 10, + CommentLine: 10, + }, + }, + }, + }, } type blameTestCase struct { From 0a70fe9fca9954ea04a015c94f44c9920aad39a2 Mon Sep 17 00:00:00 2001 From: Ian Lewis Date: Wed, 30 Oct 2024 04:12:20 +0000 Subject: [PATCH 2/4] Add CHANGELOG entry for --label Signed-off-by: Ian Lewis --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a9cc61a..ef2ad9cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- A new `--label` flag was added to support filtering TODOs by label + ([#1562](https://github.com/ianlewis/todos/issues/1562)). - Support was added for [MATLAB](https://www.mathworks.com/products/matlab.html) - Support was added for @@ -33,7 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed a bug where TODOs were not being reported if they were located after a multi-line comment with no TODOs in the same file - ([#1520](https://github.com/ianlewis/todos/issues/1520)) + ([#1520](https://github.com/ianlewis/todos/issues/1520)). ## [0.8.0] - 2024-02-21 From 5806950765735a1b6a0a19b52fbe5c9dfb7758ff Mon Sep 17 00:00:00 2001 From: Ian Lewis Date: Wed, 30 Oct 2024 04:16:48 +0000 Subject: [PATCH 3/4] add unit test for --label flag Signed-off-by: Ian Lewis --- internal/cmd/todos/app_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/internal/cmd/todos/app_test.go b/internal/cmd/todos/app_test.go index 0c79e88c..a57caf2a 100644 --- a/internal/cmd/todos/app_test.go +++ b/internal/cmd/todos/app_test.go @@ -531,6 +531,18 @@ func Test_walkerOptionsFromContext(t *testing.T) { args: []string{"--charset=invalid"}, err: ErrFlagParse, }, + "label": { + args: []string{"--label=foo", "--label=bar-*"}, + expected: &walker.Options{ + Config: &todos.Config{ + Types: todos.DefaultTypes, + }, + LabelGlobs: []glob.Glob{glob.MustCompile("foo"), glob.MustCompile("bar-*")}, + Charset: defaultCharset, + IncludeHidden: true, + Paths: []string{"."}, + }, + }, } for name, tc := range testCases { From 098e6ef447a28a8d31657831120347f40d7e2d1f Mon Sep 17 00:00:00 2001 From: Ian Lewis Date: Wed, 30 Oct 2024 04:54:21 +0000 Subject: [PATCH 4/4] remove label Signed-off-by: Ian Lewis --- internal/walker/walker.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/walker/walker.go b/internal/walker/walker.go index 92cd76ec..bf635ce3 100644 --- a/internal/walker/walker.go +++ b/internal/walker/walker.go @@ -316,7 +316,6 @@ func (w *TODOWalker) scanFile(f *os.File, force bool) error { return nil } t := todos.NewTODOScanner(s, w.options.Config) -scanL: for t.Scan() { todo := t.Next() @@ -330,7 +329,7 @@ scanL: } } if !labelMatch { - continue scanL + continue } }