na 1.1.20 → 1.1.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +11 -0
- data/CHANGELOG.md +29 -0
- data/Gemfile.lock +1 -1
- data/README.md +24 -28
- data/bin/na +70 -29
- data/lib/na/next_action.rb +108 -18
- data/lib/na/string.rb +53 -9
- data/lib/na/version.rb +1 -1
- data/src/README.md +12 -180
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 584d55c84c26fddeb65bf537b4743b5cac4308f39d796d336d95c900fc966deb
|
4
|
+
data.tar.gz: 73ea4289a1e43ce0b67f66f803a0db07359fe3f077358328a58c172c06daa107
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89945d2a8df1c1ea11360da4258c09f9b5c8ad38f41ca5ef98a68b99c33b41fb0b718919cc680eb356903f284cef8dd4271acea94af7f8c4adca031d445a6a26
|
7
|
+
data.tar.gz: 7c3f8c30bf5a4c69270641aadfb036303c42ee8678a09c2e8b5faad7883aff0ac0139caf465f9d37b84cf170ed4ee072ad20d4872b8c760b97d0e2ca1f3b6e2e
|
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,32 @@
|
|
1
|
+
### 1.1.22
|
2
|
+
|
3
|
+
2022-10-07 05:58
|
4
|
+
|
5
|
+
#### IMPROVED
|
6
|
+
|
7
|
+
- Help output and code documentation
|
8
|
+
- Allow wildcards (* and ?) when matching todo history
|
9
|
+
- Allow multiple todo queries separated by comma
|
10
|
+
|
11
|
+
#### FIXED
|
12
|
+
|
13
|
+
- Remove file extension when matching todo history
|
14
|
+
- Todo history query failed on exact match
|
15
|
+
|
16
|
+
### 1.1.21
|
17
|
+
|
18
|
+
2022-10-07 04:26
|
19
|
+
|
20
|
+
#### NEW
|
21
|
+
|
22
|
+
- `na todos` will list (and optionally search) known todo files from history
|
23
|
+
|
24
|
+
#### IMPROVED
|
25
|
+
|
26
|
+
- Fuzzier matching of todo file history
|
27
|
+
- Help output fixes
|
28
|
+
- Code documentation
|
29
|
+
|
1
30
|
### 1.1.20
|
2
31
|
|
3
32
|
2022-10-07 03:16
|
data/Gemfile.lock
CHANGED
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.1.
|
12
|
+
The current version of `na` is 1.1.22
|
13
13
|
.
|
14
14
|
|
15
15
|
`na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
|
@@ -59,7 +59,7 @@ SYNOPSIS
|
|
59
59
|
na [global options] command [command options] [arguments...]
|
60
60
|
|
61
61
|
VERSION
|
62
|
-
1.1.
|
62
|
+
1.1.22
|
63
63
|
|
64
64
|
GLOBAL OPTIONS
|
65
65
|
-a, --[no-]add - Add a next action (deprecated, for backwards compatibility)
|
@@ -83,6 +83,7 @@ COMMANDS
|
|
83
83
|
next, show - Show next actions
|
84
84
|
prompt - Show or install prompt hooks for the current shell
|
85
85
|
tagged - Find actions matching a tag
|
86
|
+
todos - Show list of known todo files
|
86
87
|
```
|
87
88
|
|
88
89
|
#### Commands
|
@@ -102,7 +103,7 @@ SYNOPSIS
|
|
102
103
|
na [global options] add [command options] ACTION
|
103
104
|
|
104
105
|
DESCRIPTION
|
105
|
-
Provides an easy way to store todos while you work. Add quick
|
106
|
+
Provides an easy way to store todos while you work. Add quick reminders and (if you set up Prompt Hooks) they'll automatically display next time you enter the directory. If multiple todo files are found in the current directory, a menu will allow you to pick to which file the action gets added.
|
106
107
|
|
107
108
|
COMMAND OPTIONS
|
108
109
|
-d, --depth=DEPTH - Search for files X directories deep (default: 1)
|
@@ -170,7 +171,7 @@ DESCRIPTION
|
|
170
171
|
COMMAND OPTIONS
|
171
172
|
-d, --depth=DEPTH - Recurse to depth (default: none)
|
172
173
|
-e, --regex - Interpret search pattern as regular expression
|
173
|
-
--in=TODO_PATH - Show actions from a specific todo file in history (default: none)
|
174
|
+
--in=TODO_PATH - Show actions from a specific todo file in history. May use wildcards (* and ?) (default: none)
|
174
175
|
-o, --or - Combine search tokens with OR, displaying actions matching ANY of the terms
|
175
176
|
--proj, --project=PROJECT[/SUBPROJECT] - Show actions from a specific project (default: none)
|
176
177
|
-v, --invert - Show actions not matching search pattern
|
@@ -206,11 +207,11 @@ EXAMPLES
|
|
206
207
|
|
207
208
|
##### next, show
|
208
209
|
|
209
|
-
Examples:
|
210
|
+
Examples:
|
210
211
|
|
211
|
-
- `na
|
212
|
-
- `na
|
213
|
-
- `na
|
212
|
+
- `na next` (list all next actions in the current directory)
|
213
|
+
- `na next -d 3` (list all next actions in the current directory and look for additional files 3 levels deep from there)
|
214
|
+
- `na next marked2` (show next actions from another directory you've previously used na on)
|
214
215
|
|
215
216
|
```
|
216
217
|
NAME
|
@@ -218,7 +219,10 @@ NAME
|
|
218
219
|
|
219
220
|
SYNOPSIS
|
220
221
|
|
221
|
-
na [global options] next [command options]
|
222
|
+
na [global options] next [command options] [QUERY]
|
223
|
+
|
224
|
+
DESCRIPTION
|
225
|
+
Next actions are actions which contain the next action tag (default @na), do not contain @done, and are not in the Archive project. Arguments will target a todo file from history, whether it's in the current directory or not. Todo file queries can include path components separated by / or :, and may use wildcards (`*` to match any text, `?` to match a single character). Multiple queries allowed (separate arguments or separated by comma).
|
222
226
|
|
223
227
|
COMMAND OPTIONS
|
224
228
|
-d, --depth=DEPTH - Recurse to depth (default: 2)
|
@@ -245,38 +249,30 @@ Separate multiple tags with spaces or commas. By default tags are combined with
|
|
245
249
|
|
246
250
|
```
|
247
251
|
NAME
|
248
|
-
|
252
|
+
next - Show next actions
|
249
253
|
|
250
254
|
SYNOPSIS
|
251
255
|
|
252
|
-
na [global options]
|
256
|
+
na [global options] next [command options] [QUERY]
|
253
257
|
|
254
258
|
DESCRIPTION
|
255
|
-
|
259
|
+
Next actions are actions which contain the next action tag (default @na), do not contain @done, and are not in the Archive project. Arguments will target a todo file from history, whether it's in the current directory or not. Todo file queries can include path components separated by / or :, and may use wildcards (`*` to match any text, `?` to match a single character). Multiple queries allowed (separate arguments or separated by comma).
|
256
260
|
|
257
261
|
COMMAND OPTIONS
|
258
|
-
-d, --depth=DEPTH - Recurse to depth (default:
|
259
|
-
--in=TODO_PATH - Show actions from a specific todo file in history (default: none)
|
260
|
-
-o, --or - Combine tags with OR, displaying actions matching ANY of the tags
|
262
|
+
-d, --depth=DEPTH - Recurse to depth (default: 2)
|
261
263
|
--proj, --project=PROJECT[/SUBPROJECT] - Show actions from a specific project (default: none)
|
262
|
-
-
|
264
|
+
-t, --tag=TAG - Alternate tag to search for (default: none)
|
263
265
|
|
264
266
|
EXAMPLES
|
265
267
|
|
266
|
-
#
|
267
|
-
na
|
268
|
-
|
269
|
-
# Show all actions tagged @feature AND @idea, recurse 3 levels
|
270
|
-
na tagged -d 3 "feature, idea"
|
271
|
-
|
272
|
-
# Show all actions tagged @feature OR @idea
|
273
|
-
na tagged --or "feature, idea"
|
268
|
+
# display the next actions from any todo files in the current directory
|
269
|
+
na next
|
274
270
|
|
275
|
-
#
|
276
|
-
na
|
271
|
+
# display the next actions from the current directory, traversing 3 levels deep
|
272
|
+
na next -d 3
|
277
273
|
|
278
|
-
#
|
279
|
-
na
|
274
|
+
# display next actions for a project you visited in the past
|
275
|
+
na next marked
|
280
276
|
```
|
281
277
|
|
282
278
|
### Configuration
|
data/bin/na
CHANGED
@@ -54,7 +54,13 @@ class App
|
|
54
54
|
switch %i[debug]
|
55
55
|
|
56
56
|
desc 'Show next actions'
|
57
|
-
|
57
|
+
long_desc 'Next actions are actions which contain the next action tag (default @na),
|
58
|
+
do not contain @done, and are not in the Archive project.
|
59
|
+
|
60
|
+
Arguments will target a todo file from history, whether it\'s in the current
|
61
|
+
directory or not. Todo file queries can include path components separated by /
|
62
|
+
or :, and may use wildcards (`*` to match any text, `?` to match a single character). Multiple queries allowed (separate arguments or separated by comma).'
|
63
|
+
arg_name 'QUERY', optional: true
|
58
64
|
command %i[next show] do |c|
|
59
65
|
c.example 'na next', desc: 'display the next actions from any todo files in the current directory'
|
60
66
|
c.example 'na next -d 3', desc: 'display the next actions from the current directory, traversing 3 levels deep'
|
@@ -91,11 +97,13 @@ class App
|
|
91
97
|
if args.count.positive?
|
92
98
|
tokens = []
|
93
99
|
args.each do |arg|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
100
|
+
arg.split(/ *, */).each do |a|
|
101
|
+
m = a.match(/^(?<req>\+)?(?<tok>.*?)$/)
|
102
|
+
tokens.push({
|
103
|
+
token: m['tok'],
|
104
|
+
required: !m['req'].nil?
|
105
|
+
})
|
106
|
+
end
|
99
107
|
end
|
100
108
|
end
|
101
109
|
|
@@ -114,11 +122,12 @@ class App
|
|
114
122
|
end
|
115
123
|
|
116
124
|
desc 'Add a new next action'
|
117
|
-
long_desc 'Provides an easy way to store todos while you work. Add quick
|
118
|
-
they\'ll automatically display
|
125
|
+
long_desc 'Provides an easy way to store todos while you work. Add quick
|
126
|
+
reminders and (if you set up Prompt Hooks) they\'ll automatically display
|
127
|
+
next time you enter the directory.
|
119
128
|
|
120
|
-
If multiple todo files are found in the current directory, a menu will
|
121
|
-
file the action gets added.'
|
129
|
+
If multiple todo files are found in the current directory, a menu will
|
130
|
+
allow you to pick to which file the action gets added.'
|
122
131
|
arg_name 'ACTION'
|
123
132
|
command :add do |c|
|
124
133
|
c.example 'na add "A cool feature I thought of @idea"', desc: 'Add a new action to the Inbox, including a tag'
|
@@ -248,7 +257,7 @@ class App
|
|
248
257
|
c.arg_name 'DEPTH'
|
249
258
|
c.flag %i[d depth], type: :integer, must_match: /^\d+$/
|
250
259
|
|
251
|
-
c.desc 'Show actions from a specific todo file in history'
|
260
|
+
c.desc 'Show actions from a specific todo file in history. May use wildcards (* and ?)'
|
252
261
|
c.arg_name 'TODO_PATH'
|
253
262
|
c.flag %i[in]
|
254
263
|
|
@@ -289,10 +298,14 @@ class App
|
|
289
298
|
|
290
299
|
todo = nil
|
291
300
|
if options[:in]
|
292
|
-
todo = [
|
293
|
-
|
294
|
-
|
295
|
-
|
301
|
+
todo = []
|
302
|
+
options[:in].split(/ *, */).each do |a|
|
303
|
+
m = a.match(/^(?<req>\+)?(?<tok>.*?)$/)
|
304
|
+
todo.push({
|
305
|
+
token: m['tok'],
|
306
|
+
required: !m['req'].nil?
|
307
|
+
})
|
308
|
+
end
|
296
309
|
end
|
297
310
|
|
298
311
|
files, actions = NA.parse_actions(depth: depth,
|
@@ -314,12 +327,12 @@ class App
|
|
314
327
|
|
315
328
|
desc 'Find actions matching a tag'
|
316
329
|
long_desc 'Finds actions with tags matching the arguments. An action is shown if it
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
arg_name 'TAG
|
330
|
+
contains all of the tags listed. Add a + before a tag to make it required
|
331
|
+
and others optional. You can specify values using TAG=VALUE pairs.
|
332
|
+
Use <, >, and = for numeric comparisons, and *=, ^=, and $= for text comparisons.
|
333
|
+
Date comparisons use natural language (`na tagged "due<=today"`) and
|
334
|
+
are detected automatically.'
|
335
|
+
arg_name 'TAG[=VALUE]'
|
323
336
|
command %i[tagged] do |c|
|
324
337
|
c.example 'na tagged maybe', desc: 'Show all actions tagged @maybe'
|
325
338
|
c.example 'na tagged -d 3 "feature, idea"', desc: 'Show all actions tagged @feature AND @idea, recurse 3 levels'
|
@@ -332,7 +345,7 @@ class App
|
|
332
345
|
c.default_value 1
|
333
346
|
c.flag %i[d depth], type: :integer, must_match: /^\d+$/
|
334
347
|
|
335
|
-
c.desc 'Show actions from a specific todo file in history'
|
348
|
+
c.desc 'Show actions from a specific todo file in history. May use wildcards (* and ?)'
|
336
349
|
c.arg_name 'TODO_PATH'
|
337
350
|
c.flag %i[in]
|
338
351
|
|
@@ -370,10 +383,14 @@ class App
|
|
370
383
|
|
371
384
|
todo = nil
|
372
385
|
if options[:in]
|
373
|
-
todo = [
|
374
|
-
|
375
|
-
|
376
|
-
|
386
|
+
todo = []
|
387
|
+
options[:in].split(/ *, */).each do |a|
|
388
|
+
m = a.match(/^(?<req>\+)?(?<tok>.*?)$/)
|
389
|
+
todo.push({
|
390
|
+
token: m['tok'],
|
391
|
+
required: !m['req'].nil?
|
392
|
+
})
|
393
|
+
end
|
377
394
|
end
|
378
395
|
|
379
396
|
files, actions = NA.parse_actions(depth: depth,
|
@@ -388,7 +405,7 @@ class App
|
|
388
405
|
end
|
389
406
|
|
390
407
|
desc 'Create a new todo file in the current directory'
|
391
|
-
arg_name '
|
408
|
+
arg_name 'PROJECT', optional: true
|
392
409
|
command %i[init create] do |c|
|
393
410
|
c.example 'na init', desc: 'Generate a new todo file, prompting for project name'
|
394
411
|
c.example 'na init warpspeed', desc: 'Generate a new todo for a project called warpspeed'
|
@@ -456,13 +473,37 @@ class App
|
|
456
473
|
end
|
457
474
|
end
|
458
475
|
|
476
|
+
desc 'Show list of known todo files'
|
477
|
+
long_desc 'Arguments will be interpreted as a query against which the
|
478
|
+
list of todos will be fuzzy matched. Separate directories with
|
479
|
+
/, :, or a space, e.g. `na todos code/marked`'
|
480
|
+
arg_name 'QUERY', optional: true
|
481
|
+
command %i[todos] do |c|
|
482
|
+
c.action do |_global_options, _options, args|
|
483
|
+
if args.count.positive?
|
484
|
+
tokens = []
|
485
|
+
args.each do |arg|
|
486
|
+
arg.split(/ *, */).each do |a|
|
487
|
+
m = a.match(/^(?<req>\+)?(?<tok>.*?)$/)
|
488
|
+
tokens.push({
|
489
|
+
token: m['tok'],
|
490
|
+
required: !m['req'].nil?
|
491
|
+
})
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
NA.list_todos(query: tokens)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
459
500
|
desc 'Show or install prompt hooks for the current shell'
|
460
501
|
long_desc 'Installing the prompt hook allows you to automatically
|
461
502
|
list next actions when you cd into a directory'
|
462
503
|
command %i[prompt] do |c|
|
463
504
|
c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to
|
464
505
|
specify a shell (zsh, bash, fish)'
|
465
|
-
c.arg_name '
|
506
|
+
c.arg_name 'SHELL', optional: true
|
466
507
|
c.command %i[show] do |s|
|
467
508
|
s.action do |_global_options, _options, args|
|
468
509
|
shell = if args.count.positive?
|
@@ -483,7 +524,7 @@ class App
|
|
483
524
|
end
|
484
525
|
|
485
526
|
c.desc 'Install the hook for the current shell to the appropriate startup file.'
|
486
|
-
c.arg_name '
|
527
|
+
c.arg_name 'SHELL', optional: true
|
487
528
|
c.command %i[install] do |s|
|
488
529
|
s.action do |_global_options, _options, args|
|
489
530
|
shell = if args.count.positive?
|
data/lib/na/next_action.rb
CHANGED
@@ -5,13 +5,27 @@ module NA
|
|
5
5
|
class << self
|
6
6
|
attr_accessor :verbose, :extension, :na_tag
|
7
7
|
|
8
|
-
|
8
|
+
##
|
9
|
+
## Output to STDERR
|
10
|
+
##
|
11
|
+
## @param msg [String] The message
|
12
|
+
## @param exit_code [Number] The exit code, no exit if false
|
13
|
+
##
|
14
|
+
def notify(msg, exit_code: false, debug: false)
|
15
|
+
return if debug && !@verbose
|
16
|
+
|
9
17
|
$stderr.puts NA::Color.template("{x}#{msg}{x}")
|
10
18
|
if exit_code && exit_code.is_a?(Number)
|
11
19
|
Process.exit exit_code
|
12
20
|
end
|
13
21
|
end
|
14
22
|
|
23
|
+
##
|
24
|
+
## Create a new todo file
|
25
|
+
##
|
26
|
+
## @param target [String] The target path
|
27
|
+
## @param basename [String] The project base name
|
28
|
+
##
|
15
29
|
def create_todo(target, basename)
|
16
30
|
File.open(target, 'w') do |f|
|
17
31
|
content = <<~ENDCONTENT
|
@@ -32,12 +46,24 @@ module NA
|
|
32
46
|
notify("{y}Created {bw}#{target}")
|
33
47
|
end
|
34
48
|
|
49
|
+
##
|
50
|
+
## Use the *nix `find` command to locate files matching NA.extension
|
51
|
+
##
|
52
|
+
## @param depth [Number] The depth at which to search
|
53
|
+
##
|
35
54
|
def find_files(depth: 1)
|
36
55
|
files = `find . -name "*.#{NA.extension}" -maxdepth #{depth}`.strip.split("\n")
|
37
56
|
files.each { |f| save_working_dir(File.expand_path(f)) }
|
38
57
|
files
|
39
58
|
end
|
40
59
|
|
60
|
+
##
|
61
|
+
## Select from multiple files
|
62
|
+
##
|
63
|
+
## @note If `gum` or `fzf` are available, they'll be used (in that order)
|
64
|
+
##
|
65
|
+
## @param files [Array] The files
|
66
|
+
##
|
41
67
|
def select_file(files)
|
42
68
|
if TTY::Which.exist?('gum')
|
43
69
|
args = [
|
@@ -63,12 +89,22 @@ module NA
|
|
63
89
|
end
|
64
90
|
end
|
65
91
|
|
92
|
+
##
|
93
|
+
## Add an action to a todo file
|
94
|
+
##
|
95
|
+
## @param file [String] The target file
|
96
|
+
## @param project [String] The project name
|
97
|
+
## @param action [String] The action
|
98
|
+
## @param note [String] The note
|
99
|
+
##
|
66
100
|
def add_action(file, project, action, note = nil)
|
67
101
|
content = IO.read(file)
|
102
|
+
# Insert the target project at the top if it doesn't exist
|
68
103
|
unless content =~ /^[ \t]*#{project}:/i
|
69
104
|
content = "#{project.cap_first}:\n#{content}"
|
70
105
|
end
|
71
106
|
|
107
|
+
# Insert the action at the top of the target project
|
72
108
|
content.sub!(/^([ \t]*)#{project}:(.*?)$/i) do
|
73
109
|
m = Regexp.last_match
|
74
110
|
note = note.nil? ? '' : "\n#{m[1]}\t\t#{note.join('').strip}"
|
@@ -80,6 +116,14 @@ module NA
|
|
80
116
|
notify("{by}Task added to {bw}#{file}")
|
81
117
|
end
|
82
118
|
|
119
|
+
##
|
120
|
+
## Pretty print a list of actions
|
121
|
+
##
|
122
|
+
## @param actions [Array] The actions
|
123
|
+
## @param depth [Number] The depth
|
124
|
+
## @param files [Array] The files actions originally came from
|
125
|
+
## @param regexes [Array] The regexes used to gather actions
|
126
|
+
##
|
83
127
|
def output_actions(actions, depth, files: nil, regexes: [])
|
84
128
|
return if files.nil?
|
85
129
|
|
@@ -99,11 +143,23 @@ module NA
|
|
99
143
|
'%parent%action'
|
100
144
|
end
|
101
145
|
|
102
|
-
files.map { |f| notify("{dw}#{f}") } if files
|
146
|
+
files.map { |f| notify("{dw}#{f}", debug: true) } if files
|
103
147
|
|
104
148
|
puts(actions.map { |action| action.pretty(template: { output: template }, regexes: regexes) })
|
105
149
|
end
|
106
150
|
|
151
|
+
##
|
152
|
+
## Read a todo file and create a list of actions
|
153
|
+
##
|
154
|
+
## @param depth [Number] The directory depth to search for files
|
155
|
+
## @param query [Hash] The project query
|
156
|
+
## @param tag [Hash] Tags to search for
|
157
|
+
## @param search [String] A search string
|
158
|
+
## @param negate [Boolean] Invert results
|
159
|
+
## @param regex [Boolean] Interpret as regular expression
|
160
|
+
## @param project [String] The project
|
161
|
+
## @param require_na [Boolean] Require @na tag
|
162
|
+
##
|
107
163
|
def parse_actions(depth: 1, query: nil, tag: nil, search: nil, negate: false, regex: false, project: nil, require_na: true)
|
108
164
|
actions = []
|
109
165
|
required = []
|
@@ -220,6 +276,9 @@ module NA
|
|
220
276
|
end
|
221
277
|
end
|
222
278
|
|
279
|
+
##
|
280
|
+
## Remove entries from cache database that no longer exist
|
281
|
+
##
|
223
282
|
def weed_cache_file
|
224
283
|
db_dir = File.expand_path('~/.local/share/na')
|
225
284
|
db_file = 'tdlist.txt'
|
@@ -231,6 +290,24 @@ module NA
|
|
231
290
|
end
|
232
291
|
end
|
233
292
|
|
293
|
+
def list_todos(query: [])
|
294
|
+
if query
|
295
|
+
dirs = match_working_dir(query)
|
296
|
+
else
|
297
|
+
file = database_path
|
298
|
+
content = File.exist?(file) ? IO.read(file).strip : ''
|
299
|
+
notify('{br}Database empty', exit_code: 1) if content.empty?
|
300
|
+
|
301
|
+
dirs = content.split(/\n/)
|
302
|
+
end
|
303
|
+
|
304
|
+
dirs.map! do |dir|
|
305
|
+
"{xg}#{dir.sub(/^#{ENV['HOME']}/, '~').sub(%r{/([^/]+)\.#{NA.extension}$}, '/{xbw}\1{x}')}"
|
306
|
+
end
|
307
|
+
|
308
|
+
puts NA::Color.template(dirs.join("\n"))
|
309
|
+
end
|
310
|
+
|
234
311
|
private
|
235
312
|
|
236
313
|
##
|
@@ -302,29 +379,26 @@ module NA
|
|
302
379
|
## between characters
|
303
380
|
##
|
304
381
|
def match_working_dir(search, distance: 1)
|
305
|
-
optional = []
|
306
|
-
required = []
|
307
|
-
|
308
|
-
search&.each do |t|
|
309
|
-
# Make "search" into "s.{0,1}e.{0,1}a.{0,1}r.{0,1}c.{0,1}h"
|
310
|
-
new_rx = t[:token].to_s.split('').join(".{0,#{distance}}")
|
311
|
-
|
312
|
-
optional.push(new_rx)
|
313
|
-
required.push(new_rx) if t[:required]
|
314
|
-
end
|
315
|
-
|
316
|
-
match_dir(optional, required)
|
317
|
-
end
|
318
|
-
|
319
|
-
def match_dir(optional, required)
|
320
382
|
file = database_path
|
321
383
|
notify('{r}No na database found', exit_code: 1) unless File.exist?(file)
|
322
384
|
|
323
385
|
dirs = IO.read(file).split("\n")
|
324
|
-
|
386
|
+
|
387
|
+
optional = search.map { |t| t[:token] }
|
388
|
+
required = search.filter { |s| s[:required] }.map { |t| t[:token] }
|
389
|
+
|
390
|
+
NA.notify("{bw}Optional directory regex: {x}#{optional.map(&:dir_to_rx)}", debug: true)
|
391
|
+
NA.notify("{bw}Required directory regex: {x}#{required.map(&:dir_to_rx)}", debug: true)
|
392
|
+
|
393
|
+
dirs.delete_if { |d| !d.sub(/\.#{NA.extension}$/, '').dir_matches(any: optional, all: required) }
|
325
394
|
dirs.sort.uniq
|
326
395
|
end
|
327
396
|
|
397
|
+
##
|
398
|
+
## Save a todo file path to the database
|
399
|
+
##
|
400
|
+
## @param todo_file The todo file path
|
401
|
+
##
|
328
402
|
def save_working_dir(todo_file)
|
329
403
|
file = database_path
|
330
404
|
content = File.exist?(file) ? IO.read(file) : ''
|
@@ -334,6 +408,12 @@ module NA
|
|
334
408
|
File.open(file, 'w') { |f| f.puts dirs.join("\n") }
|
335
409
|
end
|
336
410
|
|
411
|
+
##
|
412
|
+
## macOS open command
|
413
|
+
##
|
414
|
+
## @param file The file
|
415
|
+
## @param app The application
|
416
|
+
##
|
337
417
|
def darwin_open(file, app: nil)
|
338
418
|
if app
|
339
419
|
`open -a "#{app}" #{Shellwords.escape(file)}`
|
@@ -342,10 +422,20 @@ module NA
|
|
342
422
|
end
|
343
423
|
end
|
344
424
|
|
425
|
+
##
|
426
|
+
## Windows open command
|
427
|
+
##
|
428
|
+
## @param file The file
|
429
|
+
##
|
345
430
|
def win_open(file)
|
346
431
|
`start #{Shellwords.escape(file)}`
|
347
432
|
end
|
348
433
|
|
434
|
+
##
|
435
|
+
## Linux open command
|
436
|
+
##
|
437
|
+
## @param file The file
|
438
|
+
##
|
349
439
|
def linux_open(file)
|
350
440
|
if TTY::Which.exist?('xdg-open')
|
351
441
|
`xdg-open #{Shellwords.escape(file)}`
|
data/lib/na/string.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# String helpers
|
3
4
|
class ::String
|
5
|
+
##
|
6
|
+
## Determine indentation level of line
|
7
|
+
##
|
8
|
+
## @return [Number] number of indents detected
|
9
|
+
##
|
4
10
|
def indent_level
|
5
11
|
prefix = match(/(^[ \t]+)/)
|
6
12
|
return 0 if prefix.nil?
|
@@ -11,7 +17,13 @@ class ::String
|
|
11
17
|
##
|
12
18
|
## Colorize @tags with ANSI escapes
|
13
19
|
##
|
14
|
-
## @param color
|
20
|
+
## @param color [String] color (see #Color)
|
21
|
+
## @param value [String] The value color
|
22
|
+
## template
|
23
|
+
## @param parens [String] The parens color
|
24
|
+
## template
|
25
|
+
## @param last_color [String] Color to restore after
|
26
|
+
## tag highlight
|
15
27
|
##
|
16
28
|
## @return [String] string with @tags highlighted
|
17
29
|
##
|
@@ -23,6 +35,16 @@ class ::String
|
|
23
35
|
"\\1#{tag_color}\\2#{paren_color}\\3#{value_color}\\4#{paren_color}\\5#{last_color}")
|
24
36
|
end
|
25
37
|
|
38
|
+
##
|
39
|
+
## Highlight search results
|
40
|
+
##
|
41
|
+
## @param regexes [Array] The regexes for the
|
42
|
+
## search
|
43
|
+
## @param color [String] The highlight color
|
44
|
+
## template
|
45
|
+
## @param last_color [String] Color to restore after
|
46
|
+
## highlight
|
47
|
+
##
|
26
48
|
def highlight_search(regexes, color: '{y}', last_color: '{xg}')
|
27
49
|
string = dup
|
28
50
|
color = NA::Color.template(color)
|
@@ -42,12 +64,13 @@ class ::String
|
|
42
64
|
|
43
65
|
# Returns the last escape sequence from a string.
|
44
66
|
#
|
45
|
-
# Actually returns all escape codes, with the
|
46
|
-
# that the result of inserting them
|
47
|
-
# same color as was set at
|
48
|
-
# Because you can send
|
49
|
-
#
|
50
|
-
#
|
67
|
+
# @note Actually returns all escape codes, with the
|
68
|
+
# assumption that the result of inserting them
|
69
|
+
# will generate the same color as was set at
|
70
|
+
# the end of the string. Because you can send
|
71
|
+
# modifiers like dark and bold separate from
|
72
|
+
# color codes, only using the last code may
|
73
|
+
# not render the same style.
|
51
74
|
#
|
52
75
|
# @return [String] All escape codes in string
|
53
76
|
#
|
@@ -55,8 +78,18 @@ class ::String
|
|
55
78
|
scan(/\e\[[\d;]+m/).join('').gsub(/\e\[0m/, '')
|
56
79
|
end
|
57
80
|
|
58
|
-
|
59
|
-
|
81
|
+
##
|
82
|
+
## Convert a directory path to a regular expression
|
83
|
+
##
|
84
|
+
## @note Splits at / or :, adds variable distance
|
85
|
+
## between characters, joins segments with
|
86
|
+
## slashes and requires that last segment
|
87
|
+
## match last segment of target path
|
88
|
+
##
|
89
|
+
## @param distance The distance
|
90
|
+
##
|
91
|
+
def dir_to_rx(distance: 2)
|
92
|
+
"#{split(%r{[/:]}).map { |comp| comp.split('').join(".{0,#{distance}}").gsub(/\*/, '[^ ]*?') }.join('.*?/.*?')}[^/]*?$"
|
60
93
|
end
|
61
94
|
|
62
95
|
def dir_matches(any: [], all: [])
|
@@ -67,6 +100,11 @@ class ::String
|
|
67
100
|
matches_any(any) && matches_all(all) && matches_none(none)
|
68
101
|
end
|
69
102
|
|
103
|
+
##
|
104
|
+
## Convert wildcard characters to regular expressions
|
105
|
+
##
|
106
|
+
## @return [String] Regex string
|
107
|
+
##
|
70
108
|
def wildcard_to_rx
|
71
109
|
gsub(/\./, '\\.').gsub(/\*/, '[^ ]*?').gsub(/\?/, '.')
|
72
110
|
end
|
@@ -75,6 +113,12 @@ class ::String
|
|
75
113
|
replace cap_first
|
76
114
|
end
|
77
115
|
|
116
|
+
##
|
117
|
+
## Capitalize first character, leaving other
|
118
|
+
## capitalization in place
|
119
|
+
##
|
120
|
+
## @return [String] capitalized string
|
121
|
+
##
|
78
122
|
def cap_first
|
79
123
|
sub(/^([a-z])(.*)$/) do
|
80
124
|
m = Regexp.last_match
|
data/lib/na/version.rb
CHANGED
data/src/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 <!--VER-->1.1.
|
12
|
+
The current version of `na` is <!--VER-->1.1.21<!--END VER-->.
|
13
13
|
|
14
14
|
`na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
|
15
15
|
|
@@ -51,37 +51,7 @@ If found, it will try to locate an `Inbox:` project, or create one if it doesn't
|
|
51
51
|
### Usage
|
52
52
|
|
53
53
|
```
|
54
|
-
|
55
|
-
na - Add and list next actions for the current project
|
56
|
-
|
57
|
-
SYNOPSIS
|
58
|
-
na [global options] command [command options] [arguments...]
|
59
|
-
|
60
|
-
VERSION
|
61
|
-
1.1.19
|
62
|
-
|
63
|
-
GLOBAL OPTIONS
|
64
|
-
-a, --[no-]add - Add a next action (deprecated, for backwards compatibility)
|
65
|
-
-d, --depth=DEPTH - Recurse to depth (default: 1)
|
66
|
-
--[no-]debug - Display verbose output
|
67
|
-
--ext=EXT - File extension to consider a todo file (default: taskpaper)
|
68
|
-
--help - Show this message
|
69
|
-
-n, --note - Prompt for additional notes (deprecated, for backwards compatibility)
|
70
|
-
-p, --priority=PRIORITY - Set a priority 0-5 (deprecated, for backwards compatibility) (default: none)
|
71
|
-
-r, --[no-]recurse - Recurse 3 directories deep (deprecated, for backwards compatability)
|
72
|
-
-t, --na_tag=TAG - Tag to consider a next action (default: na)
|
73
|
-
--version - Display the program version
|
74
|
-
|
75
|
-
COMMANDS
|
76
|
-
add - Add a new next action
|
77
|
-
edit - Open a todo file in the default editor
|
78
|
-
find, grep - Find actions matching a search pattern
|
79
|
-
help - Shows a list of commands or help for one command
|
80
|
-
init, create - Create a new todo file in the current directory
|
81
|
-
initconfig - Initialize the config file using current global options
|
82
|
-
next, show - Show next actions
|
83
|
-
prompt - Show or install prompt hooks for the current shell
|
84
|
-
tagged - Find actions matching a tag
|
54
|
+
@cli(bundle exec bin/na help)
|
85
55
|
```
|
86
56
|
|
87
57
|
#### Commands
|
@@ -93,60 +63,13 @@ Example: `na add This feature @idea I have`
|
|
93
63
|
If you run the `add` command with no arguments, you'll be asked for input on the command line.
|
94
64
|
|
95
65
|
```
|
96
|
-
|
97
|
-
add - Add a new next action
|
98
|
-
|
99
|
-
SYNOPSIS
|
100
|
-
|
101
|
-
na [global options] add [command options] ACTION
|
102
|
-
|
103
|
-
DESCRIPTION
|
104
|
-
Provides an easy way to store todos while you work. Add quick reminders and (if you set up Prompt Hooks) they'll automatically display next time you enter the directory. If multiple todo files are found in the current directory, a menu will allow you to pick to which file the action gets added.
|
105
|
-
|
106
|
-
COMMAND OPTIONS
|
107
|
-
-d, --depth=DEPTH - Search for files X directories deep (default: 1)
|
108
|
-
-f, --file=PATH - Specify the file to which the task should be added (default: none)
|
109
|
-
-n, --note - Prompt for additional notes
|
110
|
-
-p, --priority=PRIO - Add a priority level 1-5 (default: 0)
|
111
|
-
-t, --tag=TAG - Use a tag other than the default next action tag (default: none)
|
112
|
-
--to=PROJECT - Add action to specific project (default: Inbox)
|
113
|
-
-x - Don't add next action tag to new entry
|
114
|
-
|
115
|
-
EXAMPLES
|
116
|
-
|
117
|
-
# Add a new action to the Inbox, including a tag
|
118
|
-
na add "A cool feature I thought of @idea"
|
119
|
-
|
120
|
-
# Add a new action to the Inbox, set its @priority to 4, and prompt for an additional note
|
121
|
-
na add "A bug I need to fix" -p 4 -n
|
66
|
+
@cli(bundle exec bin/na help add)
|
122
67
|
```
|
123
68
|
|
124
69
|
##### edit
|
125
70
|
|
126
71
|
```
|
127
|
-
|
128
|
-
edit - Open a todo file in the default editor
|
129
|
-
|
130
|
-
SYNOPSIS
|
131
|
-
|
132
|
-
na [global options] edit [command options]
|
133
|
-
|
134
|
-
DESCRIPTION
|
135
|
-
Let the system choose the defualt, (e.g. TaskPaper), or specify a command line utility (e.g. vim). If more than one todo file is found, a menu is displayed.
|
136
|
-
|
137
|
-
COMMAND OPTIONS
|
138
|
-
-a, --app=EDITOR - Specify a Mac app (default: none)
|
139
|
-
-d, --depth=DEPTH - Recurse to depth (default: 1)
|
140
|
-
-e, --editor=EDITOR - Specify an editor CLI (default: none)
|
141
|
-
|
142
|
-
EXAMPLES
|
143
|
-
|
144
|
-
# Open the main todo file in the default editor
|
145
|
-
na edit
|
146
|
-
|
147
|
-
# Display a menu of all todo files three levels deep from the
|
148
|
-
current directory, open selection in vim.
|
149
|
-
na edit -d 3 -a vim
|
72
|
+
@cli(bundle exec bin/na help edit)
|
150
73
|
```
|
151
74
|
|
152
75
|
##### find
|
@@ -156,84 +79,25 @@ Example: `na find cool feature idea`
|
|
156
79
|
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).
|
157
80
|
|
158
81
|
```
|
159
|
-
|
160
|
-
find - Find actions matching a search pattern
|
161
|
-
|
162
|
-
SYNOPSIS
|
163
|
-
|
164
|
-
na [global options] find [command options] PATTERN
|
165
|
-
|
166
|
-
DESCRIPTION
|
167
|
-
Search tokens are separated by spaces. Actions matching all tokens in the pattern will be shown (partial matches allowed). Add a + before a token to make it required, e.g. `na find +feature +maybe`
|
168
|
-
|
169
|
-
COMMAND OPTIONS
|
170
|
-
-d, --depth=DEPTH - Recurse to depth (default: none)
|
171
|
-
-e, --regex - Interpret search pattern as regular expression
|
172
|
-
--in=TODO_PATH - Show actions from a specific todo file in history (default: none)
|
173
|
-
-o, --or - Combine search tokens with OR, displaying actions matching ANY of the terms
|
174
|
-
--proj, --project=PROJECT[/SUBPROJECT] - Show actions from a specific project (default: none)
|
175
|
-
-v, --invert - Show actions not matching search pattern
|
176
|
-
-x, --exact - Match pattern exactly
|
177
|
-
|
178
|
-
EXAMPLES
|
179
|
-
|
180
|
-
# Find all actions containing feature, idea, and swift
|
181
|
-
na find feature idea swift
|
182
|
-
|
183
|
-
# Find all actions containing the exact text "feature idea"
|
184
|
-
na find -x feature idea
|
82
|
+
@cli(bundle exec bin/na help find)
|
185
83
|
```
|
186
84
|
|
187
85
|
##### init, create
|
188
86
|
|
189
87
|
```
|
190
|
-
|
191
|
-
init - Create a new todo file in the current directory
|
192
|
-
|
193
|
-
SYNOPSIS
|
194
|
-
|
195
|
-
na [global options] init [PROJECT]
|
196
|
-
|
197
|
-
EXAMPLES
|
198
|
-
|
199
|
-
# Generate a new todo file, prompting for project name
|
200
|
-
na init
|
201
|
-
|
202
|
-
# Generate a new todo for a project called warpspeed
|
203
|
-
na init warpspeed
|
88
|
+
@cli(bundle exec bin/na help init)
|
204
89
|
```
|
205
90
|
|
206
91
|
##### next, show
|
207
92
|
|
208
|
-
Examples:
|
93
|
+
Examples:
|
209
94
|
|
210
|
-
- `na
|
211
|
-
- `na
|
212
|
-
- `na
|
95
|
+
- `na next` (list all next actions in the current directory)
|
96
|
+
- `na next -d 3` (list all next actions in the current directory and look for additional files 3 levels deep from there)
|
97
|
+
- `na next marked2` (show next actions from another directory you've previously used na on)
|
213
98
|
|
214
99
|
```
|
215
|
-
|
216
|
-
next - Show next actions
|
217
|
-
|
218
|
-
SYNOPSIS
|
219
|
-
|
220
|
-
na [global options] next [command options] OPTIONAL_QUERY
|
221
|
-
|
222
|
-
COMMAND OPTIONS
|
223
|
-
-d, --depth=DEPTH - Recurse to depth (default: 2)
|
224
|
-
--proj, --project=PROJECT[/SUBPROJECT] - Show actions from a specific project (default: none)
|
225
|
-
-t, --tag=TAG - Alternate tag to search for (default: none)
|
226
|
-
|
227
|
-
EXAMPLES
|
228
|
-
|
229
|
-
# display the next actions from any todo files in the current directory
|
230
|
-
na next
|
231
|
-
|
232
|
-
# display the next actions from the current directory, traversing 3 levels deep
|
233
|
-
na next -d 3
|
234
|
-
|
235
|
-
# display next actions for a project you visited in the past
|
236
|
-
na next marked
|
100
|
+
@cli(bundle exec bin/na help next)
|
237
101
|
```
|
238
102
|
|
239
103
|
##### tagged
|
@@ -243,39 +107,7 @@ Example: `na tagged feature +maybe`.
|
|
243
107
|
Separate multiple tags with spaces or commas. By default tags are combined with AND, so actions matching all of the tags listed will be displayed. Use `+` to make a tag required and `!` to negate a tag (only display if the action does _not_ contain the tag). When `+` and/or `!` are used, undecorated tokens become optional matches. Use `-v` to invert the search and display all actions that _don't_ match.
|
244
108
|
|
245
109
|
```
|
246
|
-
|
247
|
-
tagged - Find actions matching a tag
|
248
|
-
|
249
|
-
SYNOPSIS
|
250
|
-
|
251
|
-
na [global options] tagged [command options] TAG [VALUE]
|
252
|
-
|
253
|
-
DESCRIPTION
|
254
|
-
Finds actions with tags matching the arguments. An action is shown if it contains all of the tags listed. Add a + before a tag to make it required and others optional. You can specify values using TAG=VALUE pairs. Use <, >, and = for numeric comparisons, and *=, ^=, and $= for text comparisons. Date comparisons use natural language (`na tagged "due<=today"`) and are detected automatically.
|
255
|
-
|
256
|
-
COMMAND OPTIONS
|
257
|
-
-d, --depth=DEPTH - Recurse to depth (default: 1)
|
258
|
-
--in=TODO_PATH - Show actions from a specific todo file in history (default: none)
|
259
|
-
-o, --or - Combine tags with OR, displaying actions matching ANY of the tags
|
260
|
-
--proj, --project=PROJECT[/SUBPROJECT] - Show actions from a specific project (default: none)
|
261
|
-
-v, --invert - Show actions not matching tags
|
262
|
-
|
263
|
-
EXAMPLES
|
264
|
-
|
265
|
-
# Show all actions tagged @maybe
|
266
|
-
na tagged maybe
|
267
|
-
|
268
|
-
# Show all actions tagged @feature AND @idea, recurse 3 levels
|
269
|
-
na tagged -d 3 "feature, idea"
|
270
|
-
|
271
|
-
# Show all actions tagged @feature OR @idea
|
272
|
-
na tagged --or "feature, idea"
|
273
|
-
|
274
|
-
# Show actions with @priority(4) or @priority(5)
|
275
|
-
na tagged "priority>=4"
|
276
|
-
|
277
|
-
# Show actions with a due date coming up in the next 2 days
|
278
|
-
na tagged "due<in 2 days"
|
110
|
+
@cli(bundle exec bin/na help show)
|
279
111
|
```
|
280
112
|
|
281
113
|
### Configuration
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: na
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
@@ -178,6 +178,7 @@ extra_rdoc_files:
|
|
178
178
|
- README.md
|
179
179
|
- na.rdoc
|
180
180
|
files:
|
181
|
+
- ".travis.yml"
|
181
182
|
- CHANGELOG.md
|
182
183
|
- Gemfile
|
183
184
|
- Gemfile.lock
|