na 1.2.94 → 1.2.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.cursor/commands/changelog.md +5 -2
- data/.rubocop_todo.yml +34 -12
- data/CHANGELOG.md +23 -0
- data/Gemfile.lock +23 -23
- data/README.md +146 -5
- data/bin/commands/add.rb +19 -1
- data/bin/commands/find.rb +61 -10
- data/bin/commands/next.rb +51 -0
- data/bin/commands/saved.rb +43 -8
- data/lib/na/action.rb +3 -1
- data/lib/na/next_action.rb +804 -3
- data/lib/na/todo.rb +42 -3
- data/lib/na/version.rb +1 -1
- data/src/_README.md +145 -4
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a56c16c9c040f54facd2d6b9fffbf6740e168a63099a77ba15fb90c122ba8a2
|
|
4
|
+
data.tar.gz: 624665b0fc50ac0d251ca6d464899050351f52aadf37a05e154e3e190fc117b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab7436edfa9b1edb36a93b68a11966955168a14d0834a178bd90744d4f2333a95aea9d7a03c46fed8dd862d2378fcb6af5b89ff3be39502828d5287617dc9cff
|
|
7
|
+
data.tar.gz: 2611cb0295cbceba368e418f3502148c1d2748a2515eab3fbaad90fca3d9c05cb654865aa84a050b8d0449f6c41378779e27f268e118d927497c0a6db85d6cc4
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
Write a commmit message that uses @ labels to specify what type of change each line is. Apply @new, @fixed, @changed, @improved, and @breaking as appropriate to each line. Only add @ labels to changes that affect the user, not technical details. Technical details can be included in the commit, just don't add @ labels to those lines. Be sure to include a general description (< 60 characters) as the first line, followed by a line break.
|
|
1
|
+
Review all staged and unstaged files in the repo. Write a commmit message that uses @ labels to specify what type of change each line is. Apply @new, @fixed, @changed, @improved, and @breaking as appropriate to each line. Only add @ labels to changes that affect the user, not technical details. Technical details can be included in the commit, just don't add @ labels to those lines. Be sure to include a general description (< 60 characters) as the first line, followed by a line break.
|
|
2
2
|
|
|
3
3
|
Do not add @tags to notes about documentation updates. Always focus on actual code changes we've made since the last commit when generating the commit message.
|
|
4
4
|
|
|
5
|
+
Always use straight quotes and ascii punctuation, never curl quotes. Don't use emoji.
|
|
6
|
+
|
|
7
|
+
Always include a blank line after the first line (commit message) before the note.
|
|
5
8
|
|
|
6
9
|
Save this commit message to commit_message.txt. Overwrite existing contents.
|
|
7
10
|
|
|
8
|
-
Save this commit message to commit_message.txt{% if args.reset %}. Overwrite existing contents.{% else %}. Update the file, merging changes, if file exists, otherwise create new.{% endif %}
|
|
11
|
+
Save this commit message to commit_message.txt{% if args.reset or args.replace %}. Overwrite existing contents.{% else %}. Update the file, merging changes, if file exists, otherwise create new.{% endif %}
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2025-
|
|
3
|
+
# on 2025-12-03 13:48:16 UTC using RuboCop version 1.81.7.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
@@ -21,18 +21,18 @@ Lint/UnusedMethodArgument:
|
|
|
21
21
|
Exclude:
|
|
22
22
|
- 'lib/na/string.rb'
|
|
23
23
|
|
|
24
|
-
# Offense count:
|
|
24
|
+
# Offense count: 58
|
|
25
25
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
|
26
26
|
Metrics/AbcSize:
|
|
27
27
|
Max: 309
|
|
28
28
|
|
|
29
|
-
# Offense count:
|
|
29
|
+
# Offense count: 14
|
|
30
30
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
31
31
|
# AllowedMethods: refine
|
|
32
32
|
Metrics/BlockLength:
|
|
33
|
-
Max:
|
|
33
|
+
Max: 186
|
|
34
34
|
|
|
35
|
-
# Offense count:
|
|
35
|
+
# Offense count: 7
|
|
36
36
|
# Configuration parameters: CountBlocks, CountModifierForms.
|
|
37
37
|
Metrics/BlockNesting:
|
|
38
38
|
Max: 4
|
|
@@ -40,29 +40,29 @@ Metrics/BlockNesting:
|
|
|
40
40
|
# Offense count: 6
|
|
41
41
|
# Configuration parameters: CountComments, CountAsOne.
|
|
42
42
|
Metrics/ClassLength:
|
|
43
|
-
Max:
|
|
43
|
+
Max: 1492
|
|
44
44
|
|
|
45
|
-
# Offense count:
|
|
45
|
+
# Offense count: 41
|
|
46
46
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
47
47
|
Metrics/CyclomaticComplexity:
|
|
48
48
|
Max: 91
|
|
49
49
|
|
|
50
|
-
# Offense count:
|
|
50
|
+
# Offense count: 63
|
|
51
51
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
52
52
|
Metrics/MethodLength:
|
|
53
|
-
Max:
|
|
53
|
+
Max: 239
|
|
54
54
|
|
|
55
55
|
# Offense count: 5
|
|
56
56
|
# Configuration parameters: CountComments, CountAsOne.
|
|
57
57
|
Metrics/ModuleLength:
|
|
58
|
-
Max:
|
|
58
|
+
Max: 1494
|
|
59
59
|
|
|
60
60
|
# Offense count: 5
|
|
61
61
|
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
|
62
62
|
Metrics/ParameterLists:
|
|
63
63
|
Max: 23
|
|
64
64
|
|
|
65
|
-
# Offense count:
|
|
65
|
+
# Offense count: 39
|
|
66
66
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
67
67
|
Metrics/PerceivedComplexity:
|
|
68
68
|
Max: 104
|
|
@@ -81,6 +81,13 @@ Security/YAMLLoad:
|
|
|
81
81
|
- 'lib/na/next_action.rb'
|
|
82
82
|
- 'lib/na/theme.rb'
|
|
83
83
|
|
|
84
|
+
# Offense count: 1
|
|
85
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
86
|
+
# Configuration parameters: MinBranchesCount.
|
|
87
|
+
Style/CaseLikeIf:
|
|
88
|
+
Exclude:
|
|
89
|
+
- 'lib/na/next_action.rb'
|
|
90
|
+
|
|
84
91
|
# Offense count: 2
|
|
85
92
|
# This cop supports safe autocorrection (--autocorrect).
|
|
86
93
|
Style/IfUnlessModifier:
|
|
@@ -88,6 +95,14 @@ Style/IfUnlessModifier:
|
|
|
88
95
|
- 'lib/na/benchmark.rb'
|
|
89
96
|
- 'lib/na/colors.rb'
|
|
90
97
|
|
|
98
|
+
# Offense count: 2
|
|
99
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
100
|
+
# Configuration parameters: EnforcedStyle.
|
|
101
|
+
# SupportedStyles: line_count_dependent, lambda, literal
|
|
102
|
+
Style/Lambda:
|
|
103
|
+
Exclude:
|
|
104
|
+
- 'lib/na/next_action.rb'
|
|
105
|
+
|
|
91
106
|
# Offense count: 1
|
|
92
107
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
93
108
|
# Configuration parameters: EnforcedStyle, Autocorrect.
|
|
@@ -96,6 +111,13 @@ Style/ModuleFunction:
|
|
|
96
111
|
Exclude:
|
|
97
112
|
- 'lib/na/colors.rb'
|
|
98
113
|
|
|
114
|
+
# Offense count: 4
|
|
115
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
116
|
+
# Configuration parameters: AllowMethodComparison, ComparisonsThreshold.
|
|
117
|
+
Style/MultipleComparison:
|
|
118
|
+
Exclude:
|
|
119
|
+
- 'lib/na/next_action.rb'
|
|
120
|
+
|
|
99
121
|
# Offense count: 1
|
|
100
122
|
# This cop supports safe autocorrection (--autocorrect).
|
|
101
123
|
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
|
|
@@ -118,7 +140,7 @@ Style/YAMLFileRead:
|
|
|
118
140
|
Exclude:
|
|
119
141
|
- 'lib/na/theme.rb'
|
|
120
142
|
|
|
121
|
-
# Offense count:
|
|
143
|
+
# Offense count: 38
|
|
122
144
|
# This cop supports safe autocorrection (--autocorrect).
|
|
123
145
|
# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
|
|
124
146
|
# URISchemes: http, https
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
### 1.2.95
|
|
2
|
+
|
|
3
|
+
2025-12-03 08:23
|
|
4
|
+
|
|
5
|
+
#### NEW
|
|
6
|
+
|
|
7
|
+
- Support TaskPaper @search() syntax in `na find`, `na saved`, and `na next` queries.
|
|
8
|
+
- Allow TaskPaper-style item paths (`/Inbox//Subproject`, `/*`) for targeting projects in `add --to`, `next --project`, and `find --project`.
|
|
9
|
+
- Discover TaskPaper saved searches from any `TITLE @search(...)` line in todo files, regardless of project name.
|
|
10
|
+
- Support TaskPaper type shortcuts (project, task, note) in @search() queries.
|
|
11
|
+
- Allow slicing syntax ([index], [start:end], etc.) in TaskPaper @search() to limit results, including parenthesized expressions.
|
|
12
|
+
|
|
13
|
+
#### IMPROVED
|
|
14
|
+
|
|
15
|
+
- Saved searches from `saved_searches.yml` now recognize TaskPaper `@search(...)` values and execute them via the TaskPaper search engine instead of shelling commands.
|
|
16
|
+
- TaskPaper `@search(...)` supports nested boolean expressions with `and`/`or`, tag predicates, `@text`, project filters, and `@done` handling.
|
|
17
|
+
- Item-path filters in `@search(...)` restrict results to matching project subtrees (e.g. `/Inbox//Bugs and not @done`).
|
|
18
|
+
- `na saved` and `na find` now combine TaskPaper and YAML saved searches, matching names across both sources.
|
|
19
|
+
- TaskPaper typei shortcuts `project`, `task`, and `note` are interpreted in @search() expressions (e.g. `project Inbox` => `project = "Inbox"`).
|
|
20
|
+
- TaskPaper @search() item paths integrate with next/find/saved and respect project hierarchies.
|
|
21
|
+
- Tag-only predicates like @priority now match tags with any value (for example @priority(3)).
|
|
22
|
+
- Na next/find/saved TaskPaper queries share the same evaluation engine, keeping behavior consistent across commands.
|
|
23
|
+
|
|
1
24
|
### 1.2.94
|
|
2
25
|
|
|
3
26
|
2025-12-01 09:22
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
na (1.2.
|
|
4
|
+
na (1.2.95)
|
|
5
5
|
chronic (~> 0.10, >= 0.10.2)
|
|
6
6
|
csv (>= 3.2)
|
|
7
7
|
git (~> 3.0.0)
|
|
@@ -15,7 +15,7 @@ PATH
|
|
|
15
15
|
GEM
|
|
16
16
|
remote: https://rubygems.org/
|
|
17
17
|
specs:
|
|
18
|
-
activesupport (8.1.
|
|
18
|
+
activesupport (8.1.1)
|
|
19
19
|
base64
|
|
20
20
|
bigdecimal
|
|
21
21
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
@@ -28,15 +28,15 @@ GEM
|
|
|
28
28
|
securerandom (>= 0.3)
|
|
29
29
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
30
30
|
uri (>= 0.13.1)
|
|
31
|
-
addressable (2.8.
|
|
32
|
-
public_suffix (>= 2.0.2, <
|
|
31
|
+
addressable (2.8.8)
|
|
32
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
33
33
|
ast (2.4.3)
|
|
34
34
|
base64 (0.3.0)
|
|
35
35
|
bigdecimal (3.3.1)
|
|
36
36
|
bump (0.6.1)
|
|
37
37
|
chronic (0.10.2)
|
|
38
38
|
concurrent-ruby (1.3.5)
|
|
39
|
-
connection_pool (2.5.
|
|
39
|
+
connection_pool (2.5.5)
|
|
40
40
|
csv (3.3.5)
|
|
41
41
|
diff-lcs (1.6.2)
|
|
42
42
|
docile (1.4.1)
|
|
@@ -49,40 +49,40 @@ GEM
|
|
|
49
49
|
gli (2.21.5)
|
|
50
50
|
i18n (1.14.7)
|
|
51
51
|
concurrent-ruby (~> 1.0)
|
|
52
|
-
json (2.
|
|
52
|
+
json (2.16.0)
|
|
53
53
|
language_server-protocol (3.17.0.5)
|
|
54
54
|
lint_roller (1.1.0)
|
|
55
55
|
logger (1.7.0)
|
|
56
56
|
mdless (1.0.37)
|
|
57
|
-
minitest (5.26.
|
|
57
|
+
minitest (5.26.2)
|
|
58
58
|
ostruct (0.6.3)
|
|
59
59
|
parallel (1.27.0)
|
|
60
|
-
parser (3.3.
|
|
60
|
+
parser (3.3.10.0)
|
|
61
61
|
ast (~> 2.4.1)
|
|
62
62
|
racc
|
|
63
63
|
prism (1.6.0)
|
|
64
64
|
process_executer (1.3.0)
|
|
65
|
-
public_suffix (
|
|
65
|
+
public_suffix (7.0.0)
|
|
66
66
|
racc (1.8.1)
|
|
67
67
|
rainbow (3.1.1)
|
|
68
|
-
rake (13.3.
|
|
68
|
+
rake (13.3.1)
|
|
69
69
|
rchardet (1.10.0)
|
|
70
70
|
rdoc (4.3.0)
|
|
71
|
-
regexp_parser (2.
|
|
72
|
-
rspec (3.13.
|
|
71
|
+
regexp_parser (2.11.3)
|
|
72
|
+
rspec (3.13.2)
|
|
73
73
|
rspec-core (~> 3.13.0)
|
|
74
74
|
rspec-expectations (~> 3.13.0)
|
|
75
75
|
rspec-mocks (~> 3.13.0)
|
|
76
|
-
rspec-core (3.13.
|
|
76
|
+
rspec-core (3.13.6)
|
|
77
77
|
rspec-support (~> 3.13.0)
|
|
78
78
|
rspec-expectations (3.13.5)
|
|
79
79
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
80
80
|
rspec-support (~> 3.13.0)
|
|
81
|
-
rspec-mocks (3.13.
|
|
81
|
+
rspec-mocks (3.13.7)
|
|
82
82
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
83
83
|
rspec-support (~> 3.13.0)
|
|
84
84
|
rspec-support (3.13.6)
|
|
85
|
-
rubocop (1.
|
|
85
|
+
rubocop (1.81.7)
|
|
86
86
|
json (~> 2.3)
|
|
87
87
|
language_server-protocol (~> 3.17.0.2)
|
|
88
88
|
lint_roller (~> 1.1.0)
|
|
@@ -90,16 +90,16 @@ GEM
|
|
|
90
90
|
parser (>= 3.3.0.2)
|
|
91
91
|
rainbow (>= 2.2.2, < 4.0)
|
|
92
92
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
93
|
-
rubocop-ast (>= 1.
|
|
93
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
94
94
|
ruby-progressbar (~> 1.7)
|
|
95
95
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
96
|
-
rubocop-ast (1.
|
|
96
|
+
rubocop-ast (1.48.0)
|
|
97
97
|
parser (>= 3.3.7.2)
|
|
98
98
|
prism (~> 1.4)
|
|
99
|
-
rubocop-performance (1.
|
|
99
|
+
rubocop-performance (1.26.1)
|
|
100
100
|
lint_roller (~> 1.1)
|
|
101
101
|
rubocop (>= 1.75.0, < 2.0)
|
|
102
|
-
rubocop-ast (>= 1.
|
|
102
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
103
103
|
ruby-progressbar (1.13.0)
|
|
104
104
|
securerandom (0.4.1)
|
|
105
105
|
simplecov (0.22.0)
|
|
@@ -119,10 +119,10 @@ GEM
|
|
|
119
119
|
tty-which (0.5.0)
|
|
120
120
|
tzinfo (2.0.6)
|
|
121
121
|
concurrent-ruby (~> 1.0)
|
|
122
|
-
unicode-display_width (3.
|
|
123
|
-
unicode-emoji (~> 4.
|
|
124
|
-
unicode-emoji (4.0
|
|
125
|
-
uri (1.
|
|
122
|
+
unicode-display_width (3.2.0)
|
|
123
|
+
unicode-emoji (~> 4.1)
|
|
124
|
+
unicode-emoji (4.1.0)
|
|
125
|
+
uri (1.1.1)
|
|
126
126
|
wisper (2.0.1)
|
|
127
127
|
|
|
128
128
|
PLATFORMS
|
data/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
_If you're one of the rare people like me who find this useful, feel free to
|
|
10
10
|
[buy me some coffee][donate]._
|
|
11
11
|
|
|
12
|
-
The current version of `na` is 1.2.
|
|
12
|
+
The current version of `na` is 1.2.95.
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
### Table of contents
|
|
@@ -22,6 +22,7 @@ The current version of `na` is 1.2.94.
|
|
|
22
22
|
- [Adding todos](#adding-todos)
|
|
23
23
|
- [Updating todos](#updating-todos)
|
|
24
24
|
- [Terminology](#terminology)
|
|
25
|
+
- [TaskPaper Syntax](#taskpaper-syntax)
|
|
25
26
|
- [Usage](#usage)
|
|
26
27
|
- [Commands](#commands)
|
|
27
28
|
- [add](#add)
|
|
@@ -106,6 +107,146 @@ You can mark todos as complete, delete them, add and remove tags, change priorit
|
|
|
106
107
|
|
|
107
108
|
**Note**: Refers to lines appearing between action lines that start without hyphens. The note is attached to the preceding action regardless of indentation.
|
|
108
109
|
|
|
110
|
+
### TaskPaper Syntax
|
|
111
|
+
|
|
112
|
+
NA has its own syntax for searching tags, titles, and notes, but it also understands most of the [TaskPaper query syntax](https://guide.taskpaper.com/reference/searches/). TaskPaper-style searches are accepted anywhere you can pass a `@search(...)` expression, including:
|
|
113
|
+
|
|
114
|
+
- `na next "@search(...)"` (or via saved searches)
|
|
115
|
+
- `na find "@search(...)"` and `na saved NAME` when the saved value is `@search(...)`
|
|
116
|
+
- TaskPaper files themselves, on any line of the form `TITLE @search(PARAMS)` (these become runnable saved searches)
|
|
117
|
+
|
|
118
|
+
What follows documents the subset and extensions of TaskPaper search that `na` supports.
|
|
119
|
+
|
|
120
|
+
#### Predicates and tag searches
|
|
121
|
+
|
|
122
|
+
- **Simple text**: Bare words are treated as plain-text search tokens on the action line, combined with `and` by default.
|
|
123
|
+
- **Tag predicates**:
|
|
124
|
+
- `@tag` (no value) means "this tag exists with any value". For example, `@priority` matches `@priority(1)`, `@priority(3)`, etc.
|
|
125
|
+
- `@tag op VALUE` uses the following operators (TaskPaper relations are mapped to NA's comparison codes):
|
|
126
|
+
- `=` / `==` / no operator: equality
|
|
127
|
+
- `!=`: inequality (implemented as a negated equality)
|
|
128
|
+
- `<`, `>`, `<=`, `>=`: numeric or date comparison when the value parses as a time or number
|
|
129
|
+
- `contains` ??? `*=`: value substring match
|
|
130
|
+
- `beginswith` ??? `^=`: prefix match
|
|
131
|
+
- `endswith` ??? `$=`: suffix match
|
|
132
|
+
- `matches` ??? `=~`: regular expression match (case-insensitive)
|
|
133
|
+
- Relation modifiers like `[i]`, `[s]`, `[n]`, `[d]`, `[l]` are parsed and discarded; matching is always case-insensitive for string comparisons.
|
|
134
|
+
- **`@text`**:
|
|
135
|
+
- `@text "foo"` is treated as a plain-text predicate on the action line (equivalent to searching for `"foo"`).
|
|
136
|
+
- `@text` without a value is ignored.
|
|
137
|
+
- **`@done`**:
|
|
138
|
+
- `@done` sets an internal "include done" flag and is not treated as a normal tag filter.
|
|
139
|
+
- `not @done` (or `@done` combined with other predicates) correctly toggles whether completed actions are included.
|
|
140
|
+
|
|
141
|
+
#### Project predicates
|
|
142
|
+
|
|
143
|
+
- **Project equality**:
|
|
144
|
+
- `project = "Inbox"` or `project == "Inbox"` limits results to actions under the `Inbox` project (matching NA's project path).
|
|
145
|
+
- `project != "Archive"` excludes actions whose project chain ends in `Archive`.
|
|
146
|
+
- **Shortcuts**:
|
|
147
|
+
- `project NAME` at the start of an expression is treated as a shortcut:
|
|
148
|
+
- If it is the entire predicate, it becomes `project = "NAME"`.
|
|
149
|
+
- If it is followed by additional logic, e.g. `project Inbox and @na`, the leading `NAME and` is dropped and the rest (`@na ...`) is parsed as normal. This matches TaskPaper's "type shortcut" usage rather than combining project name and text search.
|
|
150
|
+
|
|
151
|
+
#### Type shortcuts
|
|
152
|
+
|
|
153
|
+
TaskPaper defines type shortcuts that expand to `@type = ... and`. NA supports them in a way that is practical for task searches:
|
|
154
|
+
|
|
155
|
+
- `project QUERY`:
|
|
156
|
+
- If used alone, becomes `project = "QUERY"`.
|
|
157
|
+
- If followed by more predicates joined by `and`/`or`, only the tail expression is used (e.g. `project Inbox and @na` becomes `@na`).
|
|
158
|
+
- `task QUERY` and `note QUERY`:
|
|
159
|
+
- The leading keyword is removed and the rest of the expression is interpreted as a normal text/tag predicate.
|
|
160
|
+
- NA does not currently distinguish task vs note types for filtering; these shortcuts are primarily syntactic sugar.
|
|
161
|
+
|
|
162
|
+
#### Boolean logic
|
|
163
|
+
|
|
164
|
+
- `and` and `or` are supported with parentheses for grouping:
|
|
165
|
+
- `@na and not @done`
|
|
166
|
+
- `(@priority >= 3 and @na) or @today`
|
|
167
|
+
- Expressions are parsed into an internal boolean AST and converted to disjunctive normal form (OR of AND-clauses) before evaluation, so:
|
|
168
|
+
- Complex nested groupings with multiple `and`/`or` operators behave as expected.
|
|
169
|
+
- The unary `not` operator is handled at the predicate level (e.g. `not @done`, `not @priority >= 3`).
|
|
170
|
+
|
|
171
|
+
#### Item paths
|
|
172
|
+
|
|
173
|
+
NA understands a subset of TaskPaper item-path syntax and maps it to project scopes in your todo files. Item paths can be used:
|
|
174
|
+
|
|
175
|
+
- As the leading part of a `@search(...)` expression:
|
|
176
|
+
- `@search(/Inbox//Bugs and not @done)`
|
|
177
|
+
- In TaskPaper saved searches (`TITLE @search(/Inbox//Project A and @na)`).
|
|
178
|
+
|
|
179
|
+
Supported item-path features:
|
|
180
|
+
|
|
181
|
+
- **Axes**:
|
|
182
|
+
- `/Name` selects top-level items whose title contains `Name`.
|
|
183
|
+
- `//Name` selects any descendants (at any depth) whose title contains `Name`.
|
|
184
|
+
- **Wildcards**:
|
|
185
|
+
- `*` matches "all items" on that step.
|
|
186
|
+
- Example: `/*` selects all top-level projects; `/Inbox//*` selects everything under `Inbox`.
|
|
187
|
+
- **Semantics in NA**:
|
|
188
|
+
- Each matching project path is turned into an NA project chain like `"Inbox:New Videos"`.
|
|
189
|
+
- Actions are filtered post-parse so that only actions whose parent chain starts with one of the resolved project paths are returned.
|
|
190
|
+
|
|
191
|
+
Current limitations:
|
|
192
|
+
|
|
193
|
+
- Set operations such as `union`, `intersect`, and `except` are not yet implemented.
|
|
194
|
+
- Slicing on item-path steps (e.g. `project *//not @done[0]` where `[0]` is attached to a path step) is not yet interpreted; see "Slicing results" below for what is supported.
|
|
195
|
+
|
|
196
|
+
#### Slicing results
|
|
197
|
+
|
|
198
|
+
NA supports TaskPaper-style slicing on the **result set of a `@search(...)` expression**, not (yet) on individual item-path steps:
|
|
199
|
+
|
|
200
|
+
- Supported forms:
|
|
201
|
+
- `[index]`
|
|
202
|
+
- `[start:end]`
|
|
203
|
+
- `[start:]`
|
|
204
|
+
- `[:end]`
|
|
205
|
+
- `[:]`
|
|
206
|
+
- Examples:
|
|
207
|
+
- `@search((project Inbox and @na and not @done)[0])`:
|
|
208
|
+
- Evaluates the predicates, then returns only the first matching action.
|
|
209
|
+
- `@search(/Inbox//Bugs and @na and not @done[0])`:
|
|
210
|
+
- Restricts to the `Inbox/Bugs` subtree, then returns only the first incomplete `@na` action under that subtree.
|
|
211
|
+
|
|
212
|
+
Slice semantics:
|
|
213
|
+
|
|
214
|
+
- Slices are applied per clause after all tag/project/item-path filters:
|
|
215
|
+
- `[index]` ??? the single action at `index` (0-based) if it exists.
|
|
216
|
+
- `[start:end]` ??? actions in the half-open range `start...end`.
|
|
217
|
+
- `[start:]` ??? actions from `start` to the end.
|
|
218
|
+
- `[:end]` / `[:]` ??? from the beginning to `end` (or all actions).
|
|
219
|
+
|
|
220
|
+
#### Saved searches and TaskPaper files
|
|
221
|
+
|
|
222
|
+
- **YAML saved searches**:
|
|
223
|
+
- `~/.local/share/na/saved_searches.yml` values that are plain strings continue to use NA's original search syntax.
|
|
224
|
+
- Values of the form `@search(...)` are parsed using the TaskPaper engine described above and support the full feature set (predicates, boolean logic, item paths, slicing).
|
|
225
|
+
- **TaskPaper-embedded saved searches**:
|
|
226
|
+
- Any line in a `.taskpaper` file that matches:
|
|
227
|
+
- `TITLE @search(PARAMS)`
|
|
228
|
+
- is treated as a saved search. `TITLE` becomes the search name, and `PARAMS` is parsed with the same TaskPaper engine. These searches are available via `na saved TITLE` and can coexist with YAML definitions.
|
|
229
|
+
|
|
230
|
+
#### Supported vs. unsupported TaskPaper features
|
|
231
|
+
|
|
232
|
+
In summary, NA's TaskPaper support includes:
|
|
233
|
+
|
|
234
|
+
- Tag predicates with most TaskPaper relations and modifiers.
|
|
235
|
+
- `@text` predicates for plain-text search.
|
|
236
|
+
- `@done` handling wired into NA's "done" flag.
|
|
237
|
+
- `project` equality and exclusions, plus a practical "project" type shortcut.
|
|
238
|
+
- Boolean logic with `and`, `or`, `not`, and parentheses.
|
|
239
|
+
- Item paths with `/`, `//`, and `*` to scope searches by project hierarchy.
|
|
240
|
+
- Result slicing on entire `@search(...)` expressions.
|
|
241
|
+
- Integration with `next`, `find`, and `saved` via `@search(...)`, including YAML and TaskPaper-defined saved searches.
|
|
242
|
+
|
|
243
|
+
The following TaskPaper features are **not** yet implemented:
|
|
244
|
+
|
|
245
|
+
- Item-path set operations (`union`, `intersect`, `except`).
|
|
246
|
+
- Slicing applied directly to individual item-path steps (only whole-expression slicing is currently supported).
|
|
247
|
+
|
|
248
|
+
Where NA already provided its own search syntax (e.g. `na find`, `na tagged`), TaskPaper searches are additive: you can choose whichever is more convenient for a given query, and `@search(...)` expressions are routed through a common TaskPaper engine so behavior is consistent across commands.
|
|
249
|
+
|
|
109
250
|
### Usage
|
|
110
251
|
|
|
111
252
|
```
|
|
@@ -116,7 +257,7 @@ SYNOPSIS
|
|
|
116
257
|
na [global options] command [command options] [arguments...]
|
|
117
258
|
|
|
118
259
|
VERSION
|
|
119
|
-
1.2.
|
|
260
|
+
1.2.95
|
|
120
261
|
|
|
121
262
|
GLOBAL OPTIONS
|
|
122
263
|
-a, --add - Add a next action (deprecated, for backwards compatibility)
|
|
@@ -250,7 +391,7 @@ EXAMPLE
|
|
|
250
391
|
|
|
251
392
|
Example: `na find cool feature idea`
|
|
252
393
|
|
|
253
|
-
Unless `--exact` is specified, search is tokenized and combined with AND, so `na find cool feature idea` translates to `cool AND feature AND idea`, matching any string that contains all of the words. To make a token required and others optional, add a `+` before it (e.g. `cool +feature idea` is `(cool OR idea) AND feature`). Wildcards allowed (`*` and `?`), use `--regex` to interpret the search as a regular expression. Use `-v` to invert the results (display non-matching actions only).
|
|
394
|
+
Unless `--exact` is specified, search is tokenized and combined with AND, so `na find cool feature idea` translates to `cool AND feature AND idea`, matching any string that contains all of the words. To make a token required and others optional, add a `+` before it (e.g. `cool +feature idea` is `(cool OR idea) AND feature`). Wildcards allowed (`*` and `?`), use `--regex` to interpret the search as a regular expression. Use `-v` to invert the results (display non-matching actions only). Searches accept both NA search syntax and TaskPaper search syntax (see [TaskPaper search section](#taskpaper-syntax) above).
|
|
254
395
|
|
|
255
396
|
```
|
|
256
397
|
NAME
|
|
@@ -367,7 +508,7 @@ Examples:
|
|
|
367
508
|
- `na next -d 3` (list all next actions in the current directory and look for additional files 3 levels deep from there)
|
|
368
509
|
- `na next marked2` (show next actions from another directory you've previously used na on)
|
|
369
510
|
|
|
370
|
-
To see all next actions across all known todos, use `na next "*"`. You can combine multiple arguments to see actions across multiple todos, e.g. `na next marked nvultra`.
|
|
511
|
+
To see all next actions across all known todos, use `na next "*"`. You can combine multiple arguments to see actions across multiple todos, e.g. `na next marked nvultra`. Filters and search terms accept both NA search syntax and TaskPaper search syntax (see [TaskPaper search section](#taskpaper-syntax) above).
|
|
371
512
|
|
|
372
513
|
```
|
|
373
514
|
NAME
|
|
@@ -563,7 +704,7 @@ The saved command runs saved searches. To save a search, add `--save SEARCH_NAME
|
|
|
563
704
|
|
|
564
705
|
Search names can be partially matched when calling them, so if you have a search named "overdue," you can match it with `na saved over` (shortest match will be used).
|
|
565
706
|
|
|
566
|
-
Run `na saved` without an argument to list your saved searches.
|
|
707
|
+
Run `na saved` without an argument to list your saved searches. Saved searches preserve whether NA search syntax or TaskPaper search syntax was used, and both syntaxes are supported when defining or running them (see [TaskPaper search section](#taskpaper-syntax) above).
|
|
567
708
|
|
|
568
709
|
> As a shortcut, if `na` is run with one argument that matches the name of a saved search, it will execute that search, so running `na maybe` is the same as running `na saved maybe`.
|
|
569
710
|
|
data/bin/commands/add.rb
CHANGED
|
@@ -209,7 +209,25 @@ class App
|
|
|
209
209
|
action = "#{action} @started(#{started_str})"
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
# Resolve TaskPaper-style item path in --to/--project if provided
|
|
213
|
+
project = options[:project]
|
|
214
|
+
if project && project.start_with?('/')
|
|
215
|
+
paths = NA.resolve_item_path(path: project, file: target)
|
|
216
|
+
if paths.count > 1
|
|
217
|
+
choices = paths.map { |p| "#{File.basename(target)}: #{p}" }
|
|
218
|
+
res = NA.choose_from(choices, prompt: 'Select target project', multiple: false)
|
|
219
|
+
if res
|
|
220
|
+
m = res.match(/: (.+)\z/)
|
|
221
|
+
project = m ? m[1] : project
|
|
222
|
+
else
|
|
223
|
+
NA.notify("#{NA.theme[:error]}No project selected, cancelled", exit_code: 1)
|
|
224
|
+
end
|
|
225
|
+
elsif paths.count == 1
|
|
226
|
+
project = paths.first
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
NA.add_action(target, project, action, note,
|
|
213
231
|
finish: options[:finish], append: append,
|
|
214
232
|
started_at: started_at, done_at: done_at, duration_seconds: duration_seconds)
|
|
215
233
|
end
|
data/bin/commands/find.rb
CHANGED
|
@@ -99,6 +99,38 @@ class App
|
|
|
99
99
|
options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
+
# Detect TaskPaper-style @search() syntax in arguments and delegate
|
|
103
|
+
# directly to the TaskPaper search runner (supports OR and extended
|
|
104
|
+
# TaskPaper syntax).
|
|
105
|
+
joined_args = args.join(' ')
|
|
106
|
+
if joined_args =~ /@search\((.+)\)/
|
|
107
|
+
inner = Regexp.last_match(1)
|
|
108
|
+
expr = "@search(#{inner})"
|
|
109
|
+
NA.run_taskpaper_search(
|
|
110
|
+
expr,
|
|
111
|
+
file: nil,
|
|
112
|
+
options: {
|
|
113
|
+
depth: depth,
|
|
114
|
+
notes: options[:notes],
|
|
115
|
+
nest: options[:nest],
|
|
116
|
+
omnifocus: options[:omnifocus],
|
|
117
|
+
no_file: options[:no_file],
|
|
118
|
+
times: options[:times],
|
|
119
|
+
human: options[:human],
|
|
120
|
+
search_notes: options[:search_notes],
|
|
121
|
+
invert: options[:invert],
|
|
122
|
+
regex: options[:regex],
|
|
123
|
+
project: options[:project],
|
|
124
|
+
done: options[:done],
|
|
125
|
+
require_na: false
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
next
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
tokens = nil
|
|
132
|
+
tags = []
|
|
133
|
+
|
|
102
134
|
if options[:exact] || options[:regex]
|
|
103
135
|
search = args.join(" ")
|
|
104
136
|
else
|
|
@@ -122,24 +154,25 @@ class App
|
|
|
122
154
|
search = search.gsub(/ +/, " ").strip
|
|
123
155
|
|
|
124
156
|
all_req = options[:tagged].join(" ") !~ /(?<=[, ])[+!-]/ && !options[:or]
|
|
125
|
-
|
|
157
|
+
options_tags = []
|
|
126
158
|
options[:tagged].join(",").split(/ *, */).each do |arg|
|
|
127
159
|
m = arg.match(/^(?<req>[+!-])?(?<tag>[^ =<>$~\^]+?) *(?:(?<op>[=<>~]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
|
|
128
160
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
161
|
+
options_tags.push({
|
|
162
|
+
tag: m["tag"].wildcard_to_rx,
|
|
163
|
+
comp: m["op"],
|
|
164
|
+
value: m["val"],
|
|
165
|
+
required: all_req || (!m["req"].nil? && m["req"] == "+"),
|
|
166
|
+
negate: !m["req"].nil? && m["req"] =~ /[!-]/ ? true : false,
|
|
167
|
+
})
|
|
136
168
|
end
|
|
137
169
|
|
|
138
170
|
search_for_done = false
|
|
139
|
-
|
|
171
|
+
options_tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
|
|
140
172
|
options[:done] = true if search_for_done
|
|
141
173
|
|
|
142
|
-
|
|
174
|
+
tags = options_tags
|
|
175
|
+
|
|
143
176
|
if options[:exact]
|
|
144
177
|
tokens = search
|
|
145
178
|
elsif options[:regex]
|
|
@@ -172,6 +205,13 @@ class App
|
|
|
172
205
|
end
|
|
173
206
|
end
|
|
174
207
|
|
|
208
|
+
# Support TaskPaper-style item paths in --project when value starts with '/'
|
|
209
|
+
project_filter_paths = nil
|
|
210
|
+
if options[:project]&.start_with?('/')
|
|
211
|
+
project_filter_paths = NA.resolve_item_path(path: options[:project], depth: depth)
|
|
212
|
+
options[:project] = nil
|
|
213
|
+
end
|
|
214
|
+
|
|
175
215
|
todo = NA::Todo.new({
|
|
176
216
|
depth: depth,
|
|
177
217
|
done: options[:done],
|
|
@@ -185,6 +225,17 @@ class App
|
|
|
185
225
|
require_na: false,
|
|
186
226
|
})
|
|
187
227
|
|
|
228
|
+
# Apply item-path project filters, if any
|
|
229
|
+
if project_filter_paths && project_filter_paths.any?
|
|
230
|
+
todo.actions.delete_if do |a|
|
|
231
|
+
parents = Array(a.parent)
|
|
232
|
+
path = parents.join(':')
|
|
233
|
+
project_filter_paths.none? do |p|
|
|
234
|
+
path =~ /\A#{Regexp.escape(p)}(?::|\z)/i
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
188
239
|
regexes = if tokens.is_a?(Array)
|
|
189
240
|
tokens.delete_if { |token| token[:negate] }.map { |token| token[:token].wildcard_to_rx }
|
|
190
241
|
else
|