na 1.2.34 → 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: 4b14bfb4de93f7d04ffbb7685426941e279da422d00802c4320586c85f08e14b
4
- data.tar.gz: 7a3e6c43f2118bbb5260e2d43e1fe135bc07db8f44489a47068b7733e8b57ec2
3
+ metadata.gz: 35e6d784b55e397b8843663130837bcebb9041640a7833d650cedc9b783289e2
4
+ data.tar.gz: a1ba45d7ba0d77e122667bc5c93629832dc379ee5d2275076182849055c8d72e
5
5
  SHA512:
6
- metadata.gz: b9fbce0504e8a40bf1a9961fcde1c538135ed37d0ced7bc491db3158d8bdd2be01b118f3102a148e0ff9df465e9210fd45fbe82ae14e2400a1b01d0300fbac9d
7
- data.tar.gz: 1715ad409675baca29123792064f4729d2d65123a5b0ac11343f165cb427bdf704f2fc810f41e91bf1847852108d5eda3de89d990bcd43621f22377fef77a896
6
+ metadata.gz: 1ee63a7b2bfd58496bf2047cd80a16f30bf255ca1edc8b0aa5a84a9abc8e4d1ccdd8082cd8f01fb8db60d5b73c7e2014f4f847369ac0d39274b5929ef8173f07
7
+ data.tar.gz: d0321cddedf590c96a18cbd1096c7982240cb8c172c9f3491e0569ecfdf8882955898056ff3fd56d9e529d91be2a9ca56f3003bdbf6f1517a9658d08a302ca30
data/CHANGELOG.md CHANGED
@@ -1,3 +1,48 @@
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
+
29
+ ### 1.2.35
30
+
31
+ 2023-08-30 11:59
32
+
33
+ #### IMPROVED
34
+
35
+ - If a search string contains @tags and --exact or --regex isn't specified, the @tags will be extracted and passed as a --tagged search.
36
+ - Tag handling (including values and comparisons) in tags extracted from search strings
37
+ - `na complete --project PROJ` flag to move to a specific project
38
+ - `na restore --project PROJ` flag to move restored action to
39
+ - Exit gracefully if tagged command is run with invalid
40
+ - Display action affected when using update command
41
+
42
+ #### FIXED
43
+
44
+ - Escape search for tokens to allow parenthesis and other
45
+
1
46
  ### 1.2.34
2
47
 
3
48
  2023-08-30 09:22
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.2.34)
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.34
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.34
80
+ 1.2.37
81
81
 
82
82
  GLOBAL OPTIONS
83
83
  -a, --add - Add a next action (deprecated, for backwards compatibility)
@@ -104,7 +104,7 @@ COMMANDS
104
104
  complete, finish - Find and mark an action as @done
105
105
  completed, finished - Display completed actions
106
106
  edit - Edit an existing action
107
- find, grep - Find actions matching a search pattern
107
+ find, grep, search - Find actions matching a search pattern
108
108
  help - Shows a list of commands or help for one command
109
109
  init, create - Create a new todo file in the current directory
110
110
  initconfig - Initialize the config file using current global options
@@ -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.
@@ -20,10 +20,18 @@ class App
20
20
  c.desc 'Add a @done tag to action and move to Archive'
21
21
  c.switch %i[a archive], negatable: false
22
22
 
23
+ c.desc 'Move action to specific project'
24
+ c.arg_name 'PROJECT'
25
+ c.flag %i[to project proj]
26
+
23
27
  c.desc 'Specify the file to search for the task'
24
28
  c.arg_name 'PATH'
25
29
  c.flag %i[file]
26
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
+
27
35
  c.desc 'Search for files X directories deep'
28
36
  c.arg_name 'DEPTH'
29
37
  c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
@@ -44,7 +52,7 @@ class App
44
52
  c.action do |global, options, args|
45
53
  options[:finish] = true
46
54
  options[:f] = true
47
- options[:project] = 'Archive' if options[:archive]
55
+ options[:project] = 'Archive' if options[:archive] && !options[:project]
48
56
 
49
57
  cmd = commands[:update]
50
58
  action = cmd.send(:get_action, nil)
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
@@ -7,7 +7,7 @@ class App
7
7
  (partial matches allowed). Add a + before a token to make it required, e.g. `na find +feature +maybe`,
8
8
  add a - or ! to ignore matches containing that token.'
9
9
  arg_name 'PATTERN'
10
- command %i[find grep] do |c|
10
+ command %i[find grep search] do |c|
11
11
  c.example 'na find feature idea swift', desc: 'Find all actions containing feature, idea, and swift'
12
12
  c.example 'na find feature idea -swift', desc: 'Find all actions containing feature and idea but NOT swift'
13
13
  c.example 'na find -x feature idea', desc: 'Find all actions containing the exact text "feature idea"'
@@ -64,17 +64,33 @@ class App
64
64
  NA.save_search(title, "#{NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.join(' ')}")
65
65
  end
66
66
 
67
-
68
67
  depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
69
68
  3
70
69
  else
71
70
  options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
72
71
  end
73
72
 
73
+ if options[:exact] || options[:regex]
74
+ search = args.join(' ')
75
+ else
76
+ search = args.join(' ').gsub(/(?<=\A|[ ,])(?<req>[+\-!])?@(?<tag>[^ *=<>$\^,@(]+)(?:\((?<value>.*?)\)| *(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?(?=\Z|[,@])))?/) do |arg|
77
+ m = Regexp.last_match
78
+ string = if m['value']
79
+ "#{m['req']}#{m['tag']}=#{m['value']}"
80
+ else
81
+ m[0]
82
+ end
83
+ options[:tagged] << string.sub(/@/, '')
84
+ ''
85
+ end
86
+ end
87
+
88
+ search = search.gsub(/ +/, ' ').strip
89
+
74
90
  all_req = options[:tagged].join(' ') !~ /[+!\-]/ && !options[:or]
75
91
  tags = []
76
92
  options[:tagged].join(',').split(/ *, */).each do |arg|
77
- m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
93
+ m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?) *(?:(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
78
94
 
79
95
  tags.push({
80
96
  tag: m['tag'].wildcard_to_rx,
@@ -85,54 +101,66 @@ class App
85
101
  })
86
102
  end
87
103
 
104
+ search_for_done = false
105
+ tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
106
+ options[:done] = true if search_for_done
107
+
88
108
  tokens = nil
89
109
  if options[:exact]
90
- tokens = args.join(' ')
110
+ tokens = search
91
111
  elsif options[:regex]
92
- tokens = Regexp.new(args.join(' '), Regexp::IGNORECASE)
112
+ tokens = Regexp.new(search, Regexp::IGNORECASE)
93
113
  else
94
114
  tokens = []
95
- all_req = args.join(' ') !~ /[+!\-]/ && !options[:or]
115
+ all_req = search !~ /[+!-]/ && !options[:or]
96
116
 
97
- args.join(' ').split(/ /).each do |arg|
117
+ search.split(/ /).each do |arg|
98
118
  m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
99
119
  tokens.push({
100
- token: m['tok'],
120
+ token: Regexp.escape(m['tok']),
101
121
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
102
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
122
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
103
123
  })
104
124
  end
105
125
  end
106
126
 
107
- todo = nil
127
+ todos = nil
108
128
  if options[:in]
109
- todo = []
129
+ todos = []
110
130
  options[:in].split(/ *, */).each do |a|
111
131
  m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
112
- todo.push({
113
- token: m['tok'],
114
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
115
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
116
- })
132
+ todos.push({
133
+ token: m['tok'],
134
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
135
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
136
+ })
117
137
  end
118
138
  end
119
139
 
120
- files, actions, = NA.parse_actions(depth: depth,
121
- done: options[:done],
122
- query: todo,
123
- search: tokens,
124
- tag: tags,
125
- negate: options[:invert],
126
- regex: options[:regex],
127
- project: options[:project],
128
- 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
+
129
152
  regexes = if tokens.is_a?(Array)
130
153
  tokens.delete_if { |token| token[:negate] }.map { |token| token[:token] }
131
154
  else
132
155
  [tokens]
133
156
  end
134
157
 
135
- 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])
136
164
  end
137
165
  end
138
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?
@@ -17,6 +17,10 @@ class App
17
17
  c.desc 'Overwrite note instead of appending'
18
18
  c.switch %i[o overwrite], negatable: false
19
19
 
20
+ c.desc 'Move action to specific project'
21
+ c.arg_name 'PROJECT'
22
+ c.flag %i[to project proj]
23
+
20
24
  c.desc 'Specify the file to search for the task'
21
25
  c.arg_name 'PATH'
22
26
  c.flag %i[file]
@@ -43,7 +47,7 @@ class App
43
47
  options[:done] = true
44
48
  options[:finish] = false
45
49
  options[:f] = false
46
-
50
+
47
51
  cmd = commands[:update]
48
52
  action = cmd.send(:get_action, nil)
49
53
  action.call(global, options, args)
@@ -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,22 +79,24 @@ 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
- m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
84
+ m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?) *(?:(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
85
+ next if m.nil?
84
86
 
85
87
  tags.push({
86
- tag: m['tag'].wildcard_to_rx,
88
+ tag: m['tag'].sub(/^@/, '').wildcard_to_rx,
87
89
  comp: m['op'],
88
90
  value: m['val'],
89
91
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
90
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
92
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
91
93
  })
92
94
  end
93
95
 
94
96
  search_for_done = false
95
97
  tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
96
- tags.push({ tag: 'done', value: nil, negate: true}) unless search_for_done
98
+ tags.push({ tag: 'done', value: nil, negate: true }) unless search_for_done || options[:done]
99
+ options[:done] = true if search_for_done
97
100
 
98
101
  tokens = nil
99
102
  if options[:search]
@@ -103,47 +106,55 @@ class App
103
106
  tokens = Regexp.new(options[:search].join(' '), Regexp::IGNORECASE)
104
107
  else
105
108
  tokens = []
106
- all_req = options[:search].join(' ') !~ /[+!\-]/ && !options[:or]
109
+ all_req = options[:search].join(' ') !~ /[+!-]/ && !options[:or]
107
110
 
108
111
  options[:search].join(' ').split(/ /).each do |arg|
109
112
  m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
110
113
  tokens.push({
111
114
  token: m['tok'],
112
115
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
113
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
116
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
114
117
  })
115
118
  end
116
119
  end
117
120
  end
118
121
 
119
- todo = nil
122
+ todos = nil
120
123
  if options[:in]
121
- todo = []
124
+ todos = []
125
+ all_req = options[:in] !~ /[+!-]/ && !options[:or]
122
126
  options[:in].split(/ *, */).each do |a|
123
127
  m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
124
- todo.push({
125
- token: m['tok'],
126
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
127
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
128
- })
128
+ todos.push({
129
+ token: m['tok'],
130
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
131
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
132
+ })
129
133
  end
130
134
  end
131
135
 
132
- files, actions, = NA.parse_actions(depth: depth,
133
- done: options[:done],
134
- query: todo,
135
- search: tokens,
136
- tag: tags,
137
- negate: options[:invert],
138
- project: options[:project],
139
- require_na: false)
140
- # regexes = tags.delete_if { |token| token[:negate] }.map { |token| token[:token] }
136
+ NA.notify('{br}No actions matched search', exit_code: 1) if tags.empty? && tokens.empty?
137
+
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
+
141
147
  regexes = if tokens.is_a?(Array)
142
148
  tokens.delete_if { |token| token[:negate] }.map { |token| token[:token] }
143
149
  else
144
150
  [tokens]
145
151
  end
146
- 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])
147
158
  end
148
159
  end
149
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