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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cca7f5724f71b44df5ad9ac04646a26b9258cb2fb491b0e93b15b6c1f29c68d2
4
- data.tar.gz: 249da515ff5d4b44928b8401f691e337440f0abd60d78b7155a1e5490b981519
3
+ metadata.gz: 584d55c84c26fddeb65bf537b4743b5cac4308f39d796d336d95c900fc966deb
4
+ data.tar.gz: 73ea4289a1e43ce0b67f66f803a0db07359fe3f077358328a58c172c06daa107
5
5
  SHA512:
6
- metadata.gz: eb3e8285512ba4091326d3c20c5d69e671d7a2f310b6d11f3f7b36ba6b3b19ab237059b12aa091158515eff675d8f6df0e10436119714a5de718e1f8f45a84bb
7
- data.tar.gz: 1244b815b5666fb758c0b6359a584a5a11e493ce4f30a8c7e325cd9438b1d8ebaee638115d3765954cf9335947648105447941603a168c53aeaeb1ed5a7e5031
6
+ metadata.gz: 89945d2a8df1c1ea11360da4258c09f9b5c8ad38f41ca5ef98a68b99c33b41fb0b718919cc680eb356903f284cef8dd4271acea94af7f8c4adca031d445a6a26
7
+ data.tar.gz: 7c3f8c30bf5a4c69270641aadfb036303c42ee8678a09c2e8b5faad7883aff0ac0139caf465f9d37b84cf170ed4ee072ad20d4872b8c760b97d0e2ca1f3b6e2e
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ ---
2
+ language: ruby
3
+ sudo: required
4
+ dist: trusty
5
+ cache: bundler
6
+ rvm:
7
+ - ruby-3.0.1
8
+ install:
9
+ - gem install bundler --version '2.2.29'
10
+ - bundle install
11
+ script: "bundle exec rake test"
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.1.20)
4
+ na (1.1.22)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  gli (~> 2.21.0)
7
7
  tty-reader (~> 0.9, >= 0.9.0)
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.20
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.19
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 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
+ 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 show` (list all next actions in the current directory)
212
- - `na show -d 3` (list all next actions in the current directory and look for additional files 3 levels deep from there)
213
- - `na show marked2` (show next actions from another directory you've previously used na on)
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] OPTIONAL_QUERY
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
- tagged - Find actions matching a tag
252
+ next - Show next actions
249
253
 
250
254
  SYNOPSIS
251
255
 
252
- na [global options] tagged [command options] TAG [VALUE]
256
+ na [global options] next [command options] [QUERY]
253
257
 
254
258
  DESCRIPTION
255
- 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.
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: 1)
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
- -v, --invert - Show actions not matching tags
264
+ -t, --tag=TAG - Alternate tag to search for (default: none)
263
265
 
264
266
  EXAMPLES
265
267
 
266
- # Show all actions tagged @maybe
267
- na tagged maybe
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
- # Show actions with @priority(4) or @priority(5)
276
- na tagged "priority>=4"
271
+ # display the next actions from the current directory, traversing 3 levels deep
272
+ na next -d 3
277
273
 
278
- # Show actions with a due date coming up in the next 2 days
279
- na tagged "due<in 2 days"
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
- arg_name 'OPTIONAL_QUERY'
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
- m = arg.match(/^(?<req>\+)?(?<tok>.*?)$/)
95
- tokens.push({
96
- token: m['tok'],
97
- required: !m['req'].nil?
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 reminders and (if you set up Prompt Hooks)
118
- they\'ll automatically display next time you enter the directory.
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 allow you to pick to which
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
- token: options[:in],
294
- required: true
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
- contains all of the tags listed. Add a + before a tag to make it required
318
- and others optional. You can specify values using TAG=VALUE pairs.
319
- Use <, >, and = for numeric comparisons, and *=, ^=, and $= for text comparisons.
320
- Date comparisons use natural language (`na tagged "due<=today"`) and
321
- are detected automatically.'
322
- arg_name 'TAG [VALUE]'
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
- token: options[:in],
375
- required: true
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 '[PROJECT]'
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 '[SHELL]'
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 '[SHELL]'
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?
@@ -5,13 +5,27 @@ module NA
5
5
  class << self
6
6
  attr_accessor :verbose, :extension, :na_tag
7
7
 
8
- def notify(msg, exit_code: false)
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 && @verbose
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
- dirs.delete_if { |d| !d.dir_matches(any: optional, all: required) }
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 [String] color (see #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 assumption
46
- # that the result of inserting them will generate the
47
- # same color as was set at the end of the string.
48
- # Because you can send modifiers like dark and bold
49
- # separate from color codes, only using the last code
50
- # may not render the same style.
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
- def dir_to_rx
59
- "#{split(%r{[/:]}).join('.*?/.*?')}[^/]+$"
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
@@ -1,3 +1,3 @@
1
1
  module Na
2
- VERSION = '1.1.20'
2
+ VERSION = '1.1.22'
3
3
  end
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.19<!--END VER-->.
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
- NAME
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
- NAME
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
- NAME
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
- NAME
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
- NAME
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 show` (list all next actions in the current directory)
211
- - `na show -d 3` (list all next actions in the current directory and look for additional files 3 levels deep from there)
212
- - `na show marked2` (show next actions from another directory you've previously used na on)
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
- NAME
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
- NAME
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.20
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