diff --git a/adapter/sqlite/note_dao.go b/adapter/sqlite/note_dao.go index 53fca9a..fa656e5 100644 --- a/adapter/sqlite/note_dao.go +++ b/adapter/sqlite/note_dao.go @@ -173,6 +173,17 @@ func (d *NoteDAO) Find(opts note.FinderOpts, callback func(note.Match) error) (i } whereExprs = append(whereExprs, strings.Join(globs, " OR ")) + case note.ExcludePathFilter: + if len(filter) == 0 { + break + } + globs := make([]string, 0) + for _, path := range filter { + globs = append(globs, "n.path NOT GLOB ?") + args = append(args, path+"*") + } + whereExprs = append(whereExprs, strings.Join(globs, " AND ")) + case note.DateFilter: value := "?" field := "n." + dateField(filter) diff --git a/adapter/sqlite/note_dao_test.go b/adapter/sqlite/note_dao_test.go index 56921df..3d9c7b1 100644 --- a/adapter/sqlite/note_dao_test.go +++ b/adapter/sqlite/note_dao_test.go @@ -226,7 +226,7 @@ func TestNoteDAOFindInPath(t *testing.T) { ) } -func TestNoteDAOFindInMultiplePath(t *testing.T) { +func TestNoteDAOFindInMultiplePaths(t *testing.T) { testNoteDAOFindPaths(t, note.FinderOpts{ Filters: []note.Filter{note.PathFilter([]string{"ref", "index.md"})}, @@ -235,6 +235,24 @@ func TestNoteDAOFindInMultiplePath(t *testing.T) { ) } +func TestNoteDAOFindExcludingPath(t *testing.T) { + testNoteDAOFindPaths(t, + note.FinderOpts{ + Filters: []note.Filter{note.ExcludePathFilter([]string{"log"})}, + }, + []string{"ref/test/b.md", "f39c8.md", "ref/test/a.md", "index.md"}, + ) +} + +func TestNoteDAOFindExcludingMultiplePaths(t *testing.T) { + testNoteDAOFindPaths(t, + note.FinderOpts{ + Filters: []note.Filter{note.ExcludePathFilter([]string{"ref", "log/2021-01-*"})}, + }, + []string{"f39c8.md", "log/2021-02-04.md", "index.md"}, + ) +} + func TestNoteDAOFindCreatedOn(t *testing.T) { testNoteDAOFindPaths(t, note.FinderOpts{ diff --git a/cmd/list.go b/cmd/list.go index 7adf85c..1cdc27e 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -14,7 +14,7 @@ import ( // List displays notes matching a set of criteria. type List struct { - Path []string `arg optional placeholder:"PATH"` + Path []string `arg optional placeholder:"GLOB"` Format string `help:"Pretty prints the list using the given format" short:"f" placeholder:"TEMPLATE"` Match string `help:"Terms to search for in the notes" short:"m" placeholder:"TERMS"` Limit int `help:"Limit the number of results" short:"l" placeholder:"MAX"` @@ -24,6 +24,7 @@ type List struct { Modified string `help:"Show only the notes modified on the given date" placeholder:"DATE"` ModifiedBefore string `help:"Show only the notes modified before the given date" placeholder:"DATE"` ModifiedAfter string `help:"Show only the notes modified after the given date" placeholder:"DATE"` + Exclude []string `help:"Excludes notes matching the given file path pattern from the list" placeholder:"GLOB"` } func (cmd *List) Run(container *Container) error { @@ -63,17 +64,16 @@ func (cmd *List) Run(container *Container) error { func (cmd *List) ListOpts(zk *zk.Zk) (*note.ListOpts, error) { filters := make([]note.Filter, 0) - paths := make([]string, 0) - for _, p := range cmd.Path { - path, err := zk.RelPath(p) - if err == nil { - paths = append(paths, path) - } - } - if len(paths) > 0 { + paths, ok := relPaths(zk, cmd.Path) + if ok { filters = append(filters, note.PathFilter(paths)) } + excludePaths, ok := relPaths(zk, cmd.Exclude) + if ok { + filters = append(filters, note.ExcludePathFilter(excludePaths)) + } + if cmd.Match != "" { filters = append(filters, note.MatchFilter(cmd.Match)) } @@ -159,6 +159,17 @@ func (cmd *List) ListOpts(zk *zk.Zk) (*note.ListOpts, error) { }, nil } +func relPaths(zk *zk.Zk, paths []string) ([]string, bool) { + relPaths := make([]string, 0) + for _, p := range paths { + path, err := zk.RelPath(p) + if err == nil { + relPaths = append(relPaths, path) + } + } + return relPaths, len(relPaths) > 0 +} + func printNote(note string) error { _, err := fmt.Println(note) return err diff --git a/core/note/list.go b/core/note/list.go index 90bd652..ef477bd 100644 --- a/core/note/list.go +++ b/core/note/list.go @@ -18,6 +18,9 @@ type MatchFilter string // PathFilter is a note filter using path globs to match notes. type PathFilter []string +// ExcludePathFilter is a note filter using path globs to exclude notes from the list. +type ExcludePathFilter []string + // DateFilter can be used to filter notes created or modified before, after or on a given date. type DateFilter struct { Date time.Time @@ -186,6 +189,7 @@ type matchRenderContext struct { // Filter is a sealed interface implemented by Finder filter criteria. type Filter interface{ sealed() } -func (f MatchFilter) sealed() {} -func (f PathFilter) sealed() {} -func (f DateFilter) sealed() {} +func (f MatchFilter) sealed() {} +func (f PathFilter) sealed() {} +func (f ExcludePathFilter) sealed() {} +func (f DateFilter) sealed() {}