diff --git a/docs/editors-integration.md b/docs/editors-integration.md index 72622cea..4f65702e 100644 --- a/docs/editors-integration.md +++ b/docs/editors-integration.md @@ -202,7 +202,7 @@ This LSP command calls `zk list` to search a notebook. It takes two arguments: 2.
A dictionary of additional options (click to expand) | Key | Type | Required? | Description | - | ------------------ | -------------- | ----------- | ------------------------------------------------------------------------- | + | ------------------ | -------------- | ----------- | --------------------------------------------------------------------------------------------------------- | | `select` | string array | Yes | List of note fields to return1 | | `hrefs` | string array | No | Find notes matching the given path, including its descendants | | `limit` | integer | No | Limit the number of notes found | @@ -216,6 +216,7 @@ This LSP command calls `zk list` to search a notebook. It takes two arguments: | `linkTo` | string array | No | Find notes which are linking to the given ones | | `linkedBy` | string array | No | Find notes which are linked by the given ones | | `orphan` | boolean | No | Find notes which are not linked by any other note | + | `tagless` | boolean | No | Find notes which have no tags | | `related` | string array | No | Find notes which might be related to the given ones | | `maxDistance` | integer | No | Maximum distance between two linked notes | | `recursive` | boolean | No | Follow links recursively | @@ -242,7 +243,7 @@ This LSP command calls `zk tag list` to return the list of tags in a notebook. I | Key | Type | Required? | Description | |--------|--------------|-----------|--------------------------------------------------| - | `sort` | string array | No | Order the tags by the given criteria1 | + | `sort` | string array | No | Order the tags by the given criteria1 | 1. The available sort criteria are `name` and `note-count`. You can change the order by appending `-` or `+` to the criterion. diff --git a/docs/note-filtering.md b/docs/note-filtering.md index 8dc01d09..9718cc21 100644 --- a/docs/note-filtering.md +++ b/docs/note-filtering.md @@ -190,12 +190,18 @@ $ zk list --tag "NOT done" Your shell might give you some trouble using the `-` prefix. You can quote it and add an extra space as a workaround, e.g. `--tag " -done"`. -Finally, you can use glob patterns to match multiple tags. This is particularly useful if you use a separator (e.g. `/`) to group multiple tags under a parent tag. +You can use glob patterns to match multiple tags. This is particularly useful if you use a separator (e.g. `/`) to group multiple tags under a parent tag. ```sh $ zk list --tag "year/201*" ``` +A useful [notebook housekeeping](notebook-housekeeping.md) feature is to find tags which _do not_ have tags. + +```sh +$ zk list --tagless +``` + ## Filter by creation or modification date To find notes created or modified on a specific day, use `--created ` and `--modified `. They accept a human-friendly date for argument. diff --git a/docs/notebook-housekeeping.md b/docs/notebook-housekeeping.md index 584e9ece..5a7e5867 100644 --- a/docs/notebook-housekeeping.md +++ b/docs/notebook-housekeeping.md @@ -24,3 +24,9 @@ $ zk list --format '{{word-count}}\t{{title}}' --sort word-count --limit 20 86 Anatomy of a notebook ... ``` + +## Find notes without tags + +```sh +$ zk list --tagless +``` diff --git a/internal/adapter/sqlite/note_dao.go b/internal/adapter/sqlite/note_dao.go index 9d54fb28..edb27d1e 100644 --- a/internal/adapter/sqlite/note_dao.go +++ b/internal/adapter/sqlite/note_dao.go @@ -653,6 +653,10 @@ WHERE collection_id IN (SELECT id FROM collections t WHERE kind = '%s' AND (%s)) )`) } + if opts.Tagless { + whereExprs = append(whereExprs, `tags IS NULL`) + } + if opts.CreatedStart != nil { whereExprs = append(whereExprs, "created >= ?") args = append(args, opts.CreatedStart) diff --git a/internal/cli/filtering.go b/internal/cli/filtering.go index 51ed9006..216c89f8 100644 --- a/internal/cli/filtering.go +++ b/internal/cli/filtering.go @@ -29,6 +29,7 @@ type Filtering struct { LinkedBy []string `kong:"group='filter',short='L',placeholder='PATH',help='Find notes which are linked by the given ones.'" json:"linkedBy"` NoLinkedBy []string `kong:"group='filter',placeholder='PATH',help='Find notes which are not linked by the given ones.'" json:"-"` Orphan bool `kong:"group='filter',help='Find notes which are not linked by any other note.'" json:"orphan"` + Tagless bool `kong:"group='filter',help='Find notes which have no tags.'" json:"tagless"` Related []string `kong:"group='filter',placeholder='PATH',help='Find notes which might be related to the given ones.'" json:"related"` MaxDistance int `kong:"group='filter',placeholder='COUNT',help='Maximum distance between two linked notes.'" json:"maxDistance"` Recursive bool `kong:"group='filter',short='r',help='Follow links recursively.'" json:"recursive"` @@ -89,6 +90,7 @@ func (f Filtering) ExpandNamedFilters(filters map[string]string, expandedFilters f.ExactMatch = f.ExactMatch || parsedFilter.ExactMatch f.Interactive = f.Interactive || parsedFilter.Interactive f.Orphan = f.Orphan || parsedFilter.Orphan + f.Tagless = f.Tagless || parsedFilter.Tagless f.Recursive = f.Recursive || parsedFilter.Recursive if f.Limit == 0 { @@ -203,6 +205,7 @@ func (f Filtering) NewNoteFindOpts(notebook *core.Notebook) (core.NoteFindOpts, } opts.Orphan = f.Orphan + opts.Tagless = f.Tagless if f.Created != "" { start, end, err := parseDayRange(f.Created) diff --git a/internal/core/note_find.go b/internal/core/note_find.go index 2365a4cc..783e98b4 100644 --- a/internal/core/note_find.go +++ b/internal/core/note_find.go @@ -38,6 +38,8 @@ type NoteFindOpts struct { Related []string // Filter to select notes having no other notes linking to them. Orphan bool + // Filter to select notes having no tags. + Tagless bool // Filter notes created after the given date. CreatedStart *time.Time // Filter notes created before the given date. diff --git a/tests/cmd-graph.tesh b/tests/cmd-graph.tesh index de09fd0c..1356bdc9 100644 --- a/tests/cmd-graph.tesh +++ b/tests/cmd-graph.tesh @@ -43,6 +43,7 @@ $ zk graph --help > ones. > --orphan Find notes which are not linked by any other > note. +> --tagless Find notes which have no tags. > --related=PATH,... Find notes which might be related to the > given ones. > --max-distance=COUNT Maximum distance between two linked notes. diff --git a/tests/cmd-list.tesh b/tests/cmd-list.tesh index 76a352d4..bbc8066f 100644 --- a/tests/cmd-list.tesh +++ b/tests/cmd-list.tesh @@ -51,6 +51,7 @@ $ zk list --help > ones. > --orphan Find notes which are not linked by any other > note. +> --tagless Find notes which have no tags. > --related=PATH,... Find notes which might be related to the > given ones. > --max-distance=COUNT Maximum distance between two linked notes. diff --git a/tests/fixtures/tagless/.zk/config.toml b/tests/fixtures/tagless/.zk/config.toml new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/tagless/inline.md b/tests/fixtures/tagless/inline.md new file mode 100644 index 00000000..16e1e299 --- /dev/null +++ b/tests/fixtures/tagless/inline.md @@ -0,0 +1,6 @@ +--- +title: This note has inline tags +date: 2024-08-22 +--- + +This #note has #inline tags. diff --git a/tests/fixtures/tagless/tag-array.md b/tests/fixtures/tagless/tag-array.md new file mode 100644 index 00000000..594d1fec --- /dev/null +++ b/tests/fixtures/tagless/tag-array.md @@ -0,0 +1,9 @@ +--- +title: This note has a tag array +date: 2024-08-22 +tags: + - tag + - array +--- + +This note has a tag array. diff --git a/tests/fixtures/tagless/tagged.md b/tests/fixtures/tagless/tagged.md new file mode 100644 index 00000000..8d853efd --- /dev/null +++ b/tests/fixtures/tagless/tagged.md @@ -0,0 +1,7 @@ +--- +title: This note has tags +date: 2024-08-22 +tags: inline tags +--- + +This note has tags. diff --git a/tests/fixtures/tagless/tagless.md b/tests/fixtures/tagless/tagless.md new file mode 100644 index 00000000..4ea6a509 --- /dev/null +++ b/tests/fixtures/tagless/tagless.md @@ -0,0 +1,6 @@ +--- +title: This note does not have tags +date: 2024-08-22 +--- + +This note does not have tags. diff --git a/tests/tagless.tesh b/tests/tagless.tesh new file mode 100644 index 00000000..0c093ee6 --- /dev/null +++ b/tests/tagless.tesh @@ -0,0 +1,5 @@ +$ cd tagless + +# should output only one note +$ zk list -f '\{{title}}' -q --tagless +>This note does not have tags