na 1.2.35 → 1.2.37

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f8982f5720115576a78ab96a430d1d83f501bb1b477b2cff1a694648a186afc
4
- data.tar.gz: 86dd520a3265473471b6d09fc05136dd07b53ded24ab2c82936074fa50595b67
3
+ metadata.gz: 35e6d784b55e397b8843663130837bcebb9041640a7833d650cedc9b783289e2
4
+ data.tar.gz: a1ba45d7ba0d77e122667bc5c93629832dc379ee5d2275076182849055c8d72e
5
5
  SHA512:
6
- metadata.gz: 7ea9bb861c82251c1ec52925d6527f0e4efa1eef0175d268b8a8bb307fafcc407dc888e5e5cd6d588e5e4a500a96ae64765106f23d9c4acda6e0667c71247d62
7
- data.tar.gz: e5f603160851411f66dd50ae47a65369aaa1e51352cf2eb6313674d95d64db8914d3a870f816dcc4303fbd5b4e675f213020fa6a7fd93805af4692f04eb99aed
6
+ metadata.gz: 1ee63a7b2bfd58496bf2047cd80a16f30bf255ca1edc8b0aa5a84a9abc8e4d1ccdd8082cd8f01fb8db60d5b73c7e2014f4f847369ac0d39274b5929ef8173f07
7
+ data.tar.gz: d0321cddedf590c96a18cbd1096c7982240cb8c172c9f3491e0569ecfdf8882955898056ff3fd56d9e529d91be2a9ca56f3003bdbf6f1517a9658d08a302ca30
data/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ### 1.2.37
2
+
3
+ 2023-09-01 12:42
4
+
5
+ #### NEW
6
+
7
+ - `na undo` command to undo last change or last change to file specified in arguments
8
+
9
+ #### IMPROVED
10
+
11
+ - Disable pagination when using --omnifocus
12
+ - Refactoring codebase
13
+ - `--in TODO` option for `na complete`
14
+
15
+ ### 1.2.36
16
+
17
+ 2023-09-01 12:41
18
+
19
+ #### NEW
20
+
21
+ - `na undo` command to undo last change or last change to file specified in arguments
22
+
23
+ #### IMPROVED
24
+
25
+ - Disable pagination when using --omnifocus
26
+ - Refactoring codebase
27
+ - `--in TODO` option for `na complete`
28
+
1
29
  ### 1.2.35
2
30
 
3
31
  2023-08-30 11:59
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.2.35)
4
+ na (1.2.36)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  gli (~> 2.21.0)
7
7
  mdless (~> 1.0, >= 1.0.32)
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.35
12
+ The current version of `na` is 1.2.37
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.
@@ -77,7 +77,7 @@ SYNOPSIS
77
77
  na [global options] command [command options] [arguments...]
78
78
 
79
79
  VERSION
80
- 1.2.35
80
+ 1.2.37
81
81
 
82
82
  GLOBAL OPTIONS
83
83
  -a, --add - Add a next action (deprecated, for backwards compatibility)
@@ -116,6 +116,7 @@ COMMANDS
116
116
  saved - Execute a saved search
117
117
  tagged - Find actions matching a tag
118
118
  todos - Show list of known todo files
119
+ undo - Undo the last change
119
120
  update - Update an existing action
120
121
  ```
121
122
 
@@ -512,6 +513,82 @@ EXAMPLES
512
513
  na update --archive My cool action
513
514
  ```
514
515
 
516
+ ##### changelog
517
+
518
+ View recent changes with `na changelog` or `na changes`.
519
+
520
+ ```
521
+ NAME
522
+ changes - Display the changelog
523
+
524
+ SYNOPSIS
525
+
526
+ na [global options] changes
527
+ ```
528
+
529
+ ##### complete
530
+
531
+ Mark an action as complete, shortcut for `na update --finish`.
532
+
533
+ ```
534
+ NAME
535
+ complete - Find and mark an action as @done
536
+
537
+ SYNOPSIS
538
+
539
+ na [global options] complete [command options] ACTION
540
+
541
+ COMMAND OPTIONS
542
+ -a, --archive - Add a @done tag to action and move to Archive
543
+ --all - Act on all matches immediately (no menu)
544
+ -d, --depth=DEPTH - Search for files X directories deep (default: 1)
545
+ -e, --regex - Interpret search pattern as regular expression
546
+ --file=PATH - Specify the file to search for the task (default: none)
547
+ --in, --todo=TODO_FILE - Use a known todo file, partial matches allowed (default: none)
548
+ -n, --note - Prompt for additional notes. Input will be appended to any existing note. If STDIN input (piped) is detected, it will be used as a note.
549
+ -o, --overwrite - Overwrite note instead of appending
550
+ --tagged=TAG - Match actions containing tag. Allows value comparisons (may be used more than once, default: none)
551
+ --to, --project, --proj=PROJECT - Move action to specific project (default: none)
552
+ -x, --exact - Match pattern exactly
553
+
554
+ EXAMPLES
555
+
556
+ # Find "An existing task" and mark @done
557
+ na complete "An existing task"
558
+
559
+ # Alias for complete
560
+ na finish "An existing task"
561
+ ```
562
+
563
+ ##### archive
564
+
565
+ Mark an action as complete and move to archive, shortcut for `na update --archive`.
566
+
567
+ ```
568
+ NAME
569
+ archive - Mark an action as @done and archive
570
+
571
+ SYNOPSIS
572
+
573
+ na [global options] archive [command options] ACTION
574
+
575
+ COMMAND OPTIONS
576
+ --all - Act on all matches immediately (no menu)
577
+ -d, --depth=DEPTH - Search for files X directories deep (default: 1)
578
+ --done - Archive all done tasks
579
+ -e, --regex - Interpret search pattern as regular expression
580
+ --file=PATH - Specify the file to search for the task (default: none)
581
+ -n, --note - Prompt for additional notes. Input will be appended to any existing note. If STDIN input (piped) is detected, it will be used as a note.
582
+ -o, --overwrite - Overwrite note instead of appending
583
+ --tagged=TAG - Match actions containing tag. Allows value comparisons (may be used more than once, default: none)
584
+ -x, --exact - Match pattern exactly
585
+
586
+ EXAMPLE
587
+
588
+ # Find "An existing task", mark @done if needed, and move to archive
589
+ na archive "An existing task"
590
+ ```
591
+
515
592
  ### Configuration
516
593
 
517
594
  Global options such as todo extension and default next action tag can be stored permanently by using the `na initconfig` command. Run na with the global options you'd like to set, and add `initconfig` at the end of the command. A file will be written to `~/.na.rc`. You can edit this manually, or just update it using the `initconfig --force` command to overwrite it with new settings.
@@ -28,6 +28,10 @@ class App
28
28
  c.arg_name 'PATH'
29
29
  c.flag %i[file]
30
30
 
31
+ c.desc 'Use a known todo file, partial matches allowed'
32
+ c.arg_name 'TODO_FILE'
33
+ c.flag %i[in todo]
34
+
31
35
  c.desc 'Search for files X directories deep'
32
36
  c.arg_name 'DEPTH'
33
37
  c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
data/bin/commands/edit.rb CHANGED
@@ -140,11 +140,12 @@ class App
140
140
  NA.notify('{r}No search terms provided', exit_code: 1) if tokens.nil? && options[:tagged].empty?
141
141
 
142
142
  targets.each do |target|
143
- NA.update_action(target, tokens,
144
- done: options[:done],
145
- edit: options[:edit],
146
- project: target_proj,
147
- tagged: tags)
143
+ NA.update_action(target,
144
+ tokens,
145
+ done: options[:done],
146
+ edit: options[:edit],
147
+ project: target_proj,
148
+ tagged: tags)
148
149
  end
149
150
  end
150
151
  end
data/bin/commands/find.rb CHANGED
@@ -112,47 +112,55 @@ class App
112
112
  tokens = Regexp.new(search, Regexp::IGNORECASE)
113
113
  else
114
114
  tokens = []
115
- all_req = search !~ /[+!\-]/ && !options[:or]
115
+ all_req = search !~ /[+!-]/ && !options[:or]
116
116
 
117
117
  search.split(/ /).each do |arg|
118
118
  m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
119
119
  tokens.push({
120
120
  token: Regexp.escape(m['tok']),
121
121
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
122
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
122
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
123
123
  })
124
124
  end
125
125
  end
126
126
 
127
- todo = nil
127
+ todos = nil
128
128
  if options[:in]
129
- todo = []
129
+ todos = []
130
130
  options[:in].split(/ *, */).each do |a|
131
131
  m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
132
- todo.push({
133
- token: m['tok'],
134
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
135
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
136
- })
132
+ todos.push({
133
+ token: m['tok'],
134
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
135
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
136
+ })
137
137
  end
138
138
  end
139
139
 
140
- files, actions, = NA.parse_actions(depth: depth,
141
- done: options[:done],
142
- query: todo,
143
- search: tokens,
144
- tag: tags,
145
- negate: options[:invert],
146
- regex: options[:regex],
147
- project: options[:project],
148
- require_na: false)
140
+ todo = NA::Todo.new({
141
+ depth: depth,
142
+ done: options[:done],
143
+ query: todos,
144
+ search: tokens,
145
+ tag: tags,
146
+ negate: options[:invert],
147
+ regex: options[:regex],
148
+ project: options[:project],
149
+ require_na: false
150
+ })
151
+
149
152
  regexes = if tokens.is_a?(Array)
150
153
  tokens.delete_if { |token| token[:negate] }.map { |token| token[:token] }
151
154
  else
152
155
  [tokens]
153
156
  end
154
157
 
155
- NA.output_actions(actions, depth, files: files, regexes: regexes, notes: options[:notes], nest: options[:nest], nest_projects: options[:omnifocus])
158
+ todo.actions.output(depth,
159
+ files: todo.files,
160
+ regexes: regexes,
161
+ notes: options[:notes],
162
+ nest: options[:nest],
163
+ nest_projects: options[:omnifocus])
156
164
  end
157
165
  end
158
166
  end
data/bin/commands/next.rb CHANGED
@@ -52,10 +52,10 @@ class App
52
52
  c.switch %i[done]
53
53
 
54
54
  c.desc 'Output actions nested by file'
55
- c.switch %[nest], negatable: false
55
+ c.switch %i[nest], negatable: false
56
56
 
57
57
  c.desc 'Output actions nested by file and project'
58
- c.switch %[omnifocus], negatable: false
58
+ c.switch %i[omnifocus], negatable: false
59
59
 
60
60
  c.action do |global_options, options, args|
61
61
  if global_options[:add]
@@ -75,23 +75,23 @@ class App
75
75
  options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
76
76
  end
77
77
 
78
- all_req = options[:tagged].join(' ') !~ /[+!\-]/ && !options[:or]
78
+ all_req = options[:tagged].join(' ') !~ /[+!-]/ && !options[:or]
79
79
  tags = []
80
80
  options[:tagged].join(',').split(/ *, */).each do |arg|
81
- m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
81
+ m = arg.match(/^(?<req>[+!-])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
82
82
 
83
83
  tags.push({
84
84
  tag: m['tag'].wildcard_to_rx,
85
85
  comp: m['op'],
86
86
  value: m['val'],
87
87
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
88
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
88
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
89
89
  })
90
90
  end
91
91
 
92
92
  args.concat(options[:in])
93
93
  if args.count.positive?
94
- all_req = args.join(' ') !~ /[+!\-]/
94
+ all_req = args.join(' ') !~ /[+!-]/
95
95
 
96
96
  tokens = []
97
97
  args.each do |arg|
@@ -100,7 +100,7 @@ class App
100
100
  tokens.push({
101
101
  token: m['tok'],
102
102
  required: !m['req'].nil? && m['req'] == '+',
103
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
103
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
104
104
  })
105
105
  end
106
106
  end
@@ -114,14 +114,14 @@ class App
114
114
  search = Regexp.new(options[:search].join(' '), Regexp::IGNORECASE)
115
115
  else
116
116
  search = []
117
- all_req = options[:search].join(' ') !~ /[+!\-]/ && !options[:or]
117
+ all_req = options[:search].join(' ') !~ /[+!-]/ && !options[:or]
118
118
 
119
119
  options[:search].join(' ').split(/ /).each do |arg|
120
120
  m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
121
121
  search.push({
122
122
  token: m['tok'],
123
123
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
124
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
124
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
125
125
  })
126
126
  end
127
127
  end
@@ -133,15 +133,19 @@ class App
133
133
  tag = [{ tag: NA.na_tag, value: nil }]
134
134
  tag << { tag: 'done', value: nil, negate: true } unless options[:done]
135
135
  tag.concat(tags)
136
- files, actions, = NA.parse_actions(depth: depth,
137
- done: options[:done],
138
- query: tokens,
139
- tag: tag,
140
- search: search,
141
- project: options[:project],
142
- require_na: require_na)
143
-
144
- NA.output_actions(actions, depth, files: files, notes: options[:notes], nest: options[:nest], nest_projects: options[:omnifocus])
136
+ todo = NA::Todo.new({ depth: depth,
137
+ done: options[:done],
138
+ query: tokens,
139
+ tag: tag,
140
+ search: search,
141
+ project: options[:project],
142
+ require_na: require_na })
143
+ NA::Pager.paginate = false if options[:omnifocus]
144
+ todo.actions.output(depth,
145
+ files: todo.files,
146
+ notes: options[:notes],
147
+ nest: options[:nest],
148
+ nest_projects: options[:omnifocus])
145
149
  end
146
150
  end
147
151
  end
@@ -9,6 +9,8 @@ class App
9
9
  c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to
10
10
  specify a shell (zsh, bash, fish)'
11
11
  c.arg_name 'SHELL', optional: true
12
+ c.default_command :show
13
+
12
14
  c.command %i[show] do |s|
13
15
  s.action do |_global_options, _options, args|
14
16
  shell = if args.count.positive?
@@ -57,17 +57,18 @@ class App
57
57
  c.flag %i[save]
58
58
 
59
59
  c.desc 'Output actions nested by file'
60
- c.switch %[nest], negatable: false
60
+ c.switch %i[nest], negatable: false
61
61
 
62
62
  c.desc 'Output actions nested by file and project'
63
- c.switch %[omnifocus], negatable: false
63
+ c.switch %i[omnifocus], negatable: false
64
64
 
65
65
  c.action do |global_options, options, args|
66
66
  options[:nest] = true if options[:omnifocus]
67
67
 
68
68
  if options[:save]
69
69
  title = options[:save].gsub(/[^a-z0-9]/, '_').gsub(/_+/, '_')
70
- NA.save_search(title, "#{NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.join(' ')}")
70
+ cmd = NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.join(' ')
71
+ NA.save_search(title, cmd)
71
72
  end
72
73
 
73
74
  depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
@@ -78,7 +79,7 @@ class App
78
79
 
79
80
  tags = []
80
81
 
81
- all_req = args.join(' ') !~ /[+!\-]/ && !options[:or]
82
+ all_req = args.join(' ') !~ /[+!-]/ && !options[:or]
82
83
  args.join(',').split(/ *, */).each do |arg|
83
84
  m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?) *(?:(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
84
85
  next if m.nil?
@@ -88,13 +89,13 @@ class App
88
89
  comp: m['op'],
89
90
  value: m['val'],
90
91
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
91
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
92
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
92
93
  })
93
94
  end
94
95
 
95
96
  search_for_done = false
96
97
  tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
97
- tags.push({ tag: 'done', value: nil, negate: true}) unless search_for_done || options[:done]
98
+ tags.push({ tag: 'done', value: nil, negate: true }) unless search_for_done || options[:done]
98
99
  options[:done] = true if search_for_done
99
100
 
100
101
  tokens = nil
@@ -105,49 +106,55 @@ class App
105
106
  tokens = Regexp.new(options[:search].join(' '), Regexp::IGNORECASE)
106
107
  else
107
108
  tokens = []
108
- all_req = options[:search].join(' ') !~ /[+!\-]/ && !options[:or]
109
+ all_req = options[:search].join(' ') !~ /[+!-]/ && !options[:or]
109
110
 
110
111
  options[:search].join(' ').split(/ /).each do |arg|
111
112
  m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
112
113
  tokens.push({
113
114
  token: m['tok'],
114
115
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
115
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
116
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
116
117
  })
117
118
  end
118
119
  end
119
120
  end
120
121
 
121
- todo = nil
122
+ todos = nil
122
123
  if options[:in]
123
- todo = []
124
+ todos = []
125
+ all_req = options[:in] !~ /[+!-]/ && !options[:or]
124
126
  options[:in].split(/ *, */).each do |a|
125
127
  m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
126
- todo.push({
127
- token: m['tok'],
128
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
129
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
130
- })
128
+ todos.push({
129
+ token: m['tok'],
130
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
131
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
132
+ })
131
133
  end
132
134
  end
133
135
 
134
136
  NA.notify('{br}No actions matched search', exit_code: 1) if tags.empty? && tokens.empty?
135
137
 
136
- files, actions, = NA.parse_actions(depth: depth,
137
- done: options[:done],
138
- query: todo,
139
- search: tokens,
140
- tag: tags,
141
- negate: options[:invert],
142
- project: options[:project],
143
- require_na: false)
144
- # regexes = tags.delete_if { |token| token[:negate] }.map { |token| token[:token] }
138
+ todo = NA::Todo.new({ depth: depth,
139
+ done: options[:done],
140
+ query: todos,
141
+ search: tokens,
142
+ tag: tags,
143
+ negate: options[:invert],
144
+ project: options[:project],
145
+ require_na: false })
146
+
145
147
  regexes = if tokens.is_a?(Array)
146
148
  tokens.delete_if { |token| token[:negate] }.map { |token| token[:token] }
147
149
  else
148
150
  [tokens]
149
151
  end
150
- NA.output_actions(actions, depth, files: files, regexes: regexes, notes: options[:notes], nest: options[:nest], nest_projects: options[:omnifocus])
152
+ todo.actions.output(depth,
153
+ files: todo.files,
154
+ regexes: regexes,
155
+ notes: options[:notes],
156
+ nest: options[:nest],
157
+ nest_projects: options[:omnifocus])
151
158
  end
152
159
  end
153
160
  end
@@ -10,16 +10,16 @@ class App
10
10
  command %i[todos] do |c|
11
11
  c.action do |_global_options, _options, args|
12
12
  if args.count.positive?
13
- all_req = args.join(' ') !~ /[+!\-]/
13
+ all_req = args.join(' ') !~ /[+!-]/
14
14
 
15
15
  tokens = [{ token: '*', required: all_req, negate: false }]
16
16
  args.each do |arg|
17
17
  arg.split(/ *, */).each do |a|
18
- m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
18
+ m = a.match(/^(?<req>[+!-])?(?<tok>.*?)$/)
19
19
  tokens.push({
20
20
  token: m['tok'],
21
21
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
22
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
22
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
23
23
  })
24
24
  end
25
25
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class App
4
+ extend GLI::App
5
+ desc 'Undo the last change'
6
+ long_desc 'Run without argument to undo most recent change'
7
+ arg_name 'FILE', optional: true, multiple: true
8
+ command %i[undo] do |c|
9
+ c.example 'na undo', desc: 'Undo the last change'
10
+ c.example 'na undo myproject', desc: 'Undo the last change to a file matching "myproject"'
11
+
12
+ c.action do |_global_options, options, args|
13
+ if args.empty?
14
+ NA.restore_last_modified_file
15
+ else
16
+ args.each do |arg|
17
+ NA.restore_last_modified_file(search: arg)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -63,7 +63,7 @@ class App
63
63
  c.desc 'Delete an action'
64
64
  c.switch %i[delete], negatable: false
65
65
 
66
- c.desc "Open action in editor (#{NA.default_editor}).
66
+ c.desc "Open action in editor (#{NA::Editor.default_editor}).
67
67
  Natural language dates will be parsed and converted in date-based tags."
68
68
  c.switch %i[edit], negatable: false
69
69
 
@@ -97,6 +97,8 @@ class App
97
97
  options[:tagged] << '+done'
98
98
  elsif !options[:remove].nil? && !options[:remove].empty?
99
99
  options[:tagged].concat(options[:remove])
100
+ elsif options[:finish]
101
+ options[:tagged] << '-done'
100
102
  end
101
103
 
102
104
  action = if args.count.positive?
@@ -211,7 +213,15 @@ class App
211
213
 
212
214
  end
213
215
  else
214
- files = NA.find_files(depth: options[:depth])
216
+ files = NA.find_files_matching({
217
+ depth: options[:depth],
218
+ done: options[:done],
219
+ project: target_proj,
220
+ regex: options[:regex],
221
+ require_na: false,
222
+ search: tokens,
223
+ tag: tags
224
+ })
215
225
  NA.notify('{r}No todo file found', exit_code: 1) if files.count.zero?
216
226
 
217
227
  targets = files.count > 1 ? NA.select_file(files, multiple: true) : [files[0]]
data/lib/na/action.rb CHANGED
@@ -18,6 +18,32 @@ module NA
18
18
  @note = note
19
19
  end
20
20
 
21
+ def process(priority: 0, finish: false, add_tag: [], remove_tag: [], note: [])
22
+ string = @action.dup
23
+
24
+ if priority&.positive?
25
+ string.gsub!(/(?<=\A| )@priority\(\d+\)/, '').strip!
26
+ string += " @priority(#{priority})"
27
+ end
28
+
29
+ add_tag.each do |tag|
30
+ string.gsub!(/(?<=\A| )@#{tag.gsub(/([()*?])/, '\\\\1')}(\(.*?\))?/, '')
31
+ string.strip!
32
+ string += " @#{tag}"
33
+ end
34
+
35
+ remove_tag.each do |tag|
36
+ string.gsub!(/(?<=\A| )@#{tag.gsub(/([()*?])/, '\\\\1')}(\(.*?\))?/, '')
37
+ string.strip!
38
+ end
39
+
40
+ string = "#{string.strip} @done(#{Time.now.strftime('%Y-%m-%d %H:%M')})" if finish && string !~ /(?<=\A| )@done/
41
+
42
+ @action = string
43
+ @action.expand_date_tags
44
+ @note = note unless note.empty?
45
+ end
46
+
21
47
  def to_s
22
48
  note = if @note.count.positive?
23
49
  "\n#{@note.join("\n")}"
@@ -179,7 +205,7 @@ module NA
179
205
  date = Time.parse(date.strftime('%Y-%m-%d 12:00'))
180
206
  end
181
207
 
182
- puts "Comparing #{tag_date} #{tag[:comp]} #{date}" if NA.verbose
208
+ # NA.notify("{dw}Comparing #{tag_date} #{tag[:comp]} #{date}{x}", debug: true)
183
209
 
184
210
  case tag[:comp]
185
211
  when /^>$/