na 1.2.35 → 1.2.37

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: 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 /^>$/