na 1.2.28 → 1.2.30

Sign up to get free protection for your applications and to get access to all the features.
data/bin/commands/find.rb CHANGED
@@ -1,135 +1,138 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- desc 'Find actions matching a search pattern'
4
- long_desc 'Search tokens are separated by spaces. Actions matching all tokens in the pattern will be shown
5
- (partial matches allowed). Add a + before a token to make it required, e.g. `na find +feature +maybe`,
6
- add a - or ! to ignore matches containing that token.'
7
- arg_name 'PATTERN'
8
- command %i[find grep] do |c|
9
- c.example 'na find feature idea swift', desc: 'Find all actions containing feature, idea, and swift'
10
- c.example 'na find feature idea -swift', desc: 'Find all actions containing feature and idea but NOT swift'
11
- c.example 'na find -x feature idea', desc: 'Find all actions containing the exact text "feature idea"'
3
+ class App
4
+ extend GLI::App
5
+ desc 'Find actions matching a search pattern'
6
+ long_desc 'Search tokens are separated by spaces. Actions matching all tokens in the pattern will be shown
7
+ (partial matches allowed). Add a + before a token to make it required, e.g. `na find +feature +maybe`,
8
+ add a - or ! to ignore matches containing that token.'
9
+ arg_name 'PATTERN'
10
+ command %i[find grep] do |c|
11
+ c.example 'na find feature idea swift', desc: 'Find all actions containing feature, idea, and swift'
12
+ c.example 'na find feature idea -swift', desc: 'Find all actions containing feature and idea but NOT swift'
13
+ c.example 'na find -x feature idea', desc: 'Find all actions containing the exact text "feature idea"'
12
14
 
13
- c.desc 'Interpret search pattern as regular expression'
14
- c.switch %i[e regex], negatable: false
15
+ c.desc 'Interpret search pattern as regular expression'
16
+ c.switch %i[e regex], negatable: false
15
17
 
16
- c.desc 'Match pattern exactly'
17
- c.switch %i[x exact], negatable: false
18
+ c.desc 'Match pattern exactly'
19
+ c.switch %i[x exact], negatable: false
18
20
 
19
- c.desc 'Recurse to depth'
20
- c.arg_name 'DEPTH'
21
- c.flag %i[d depth], type: :integer, must_match: /^\d+$/
21
+ c.desc 'Recurse to depth'
22
+ c.arg_name 'DEPTH'
23
+ c.flag %i[d depth], type: :integer, must_match: /^\d+$/
22
24
 
23
- c.desc 'Show actions from a specific todo file in history. May use wildcards (* and ?)'
24
- c.arg_name 'TODO_PATH'
25
- c.flag %i[in]
25
+ c.desc 'Show actions from a specific todo file in history. May use wildcards (* and ?)'
26
+ c.arg_name 'TODO_PATH'
27
+ c.flag %i[in]
26
28
 
27
- c.desc 'Include notes in output'
28
- c.switch %i[notes], negatable: true, default_value: false
29
+ c.desc 'Include notes in output'
30
+ c.switch %i[notes], negatable: true, default_value: false
29
31
 
30
- c.desc 'Combine search tokens with OR, displaying actions matching ANY of the terms'
31
- c.switch %i[o or], negatable: false
32
+ c.desc 'Combine search tokens with OR, displaying actions matching ANY of the terms'
33
+ c.switch %i[o or], negatable: false
32
34
 
33
- c.desc 'Show actions from a specific project'
34
- c.arg_name 'PROJECT[/SUBPROJECT]'
35
- c.flag %i[proj project]
35
+ c.desc 'Show actions from a specific project'
36
+ c.arg_name 'PROJECT[/SUBPROJECT]'
37
+ c.flag %i[proj project]
36
38
 
37
- c.desc 'Match actions containing tag. Allows value comparisons'
38
- c.arg_name 'TAG'
39
- c.flag %i[tagged], multiple: true
39
+ c.desc 'Match actions containing tag. Allows value comparisons'
40
+ c.arg_name 'TAG'
41
+ c.flag %i[tagged], multiple: true
40
42
 
41
- c.desc 'Include @done actions'
42
- c.switch %i[done]
43
+ c.desc 'Include @done actions'
44
+ c.switch %i[done]
43
45
 
44
- c.desc 'Show actions not matching search pattern'
45
- c.switch %i[v invert], negatable: false
46
+ c.desc 'Show actions not matching search pattern'
47
+ c.switch %i[v invert], negatable: false
46
48
 
47
- c.desc 'Save this search for future use'
48
- c.arg_name 'TITLE'
49
- c.flag %i[save]
49
+ c.desc 'Save this search for future use'
50
+ c.arg_name 'TITLE'
51
+ c.flag %i[save]
50
52
 
51
- c.desc 'Output actions nested by file'
52
- c.switch %[nest], negatable: false
53
+ c.desc 'Output actions nested by file'
54
+ c.switch %[nest], negatable: false
53
55
 
54
- c.desc 'Output actions nested by file and project'
55
- c.switch %[omnifocus], negatable: false
56
+ c.desc 'Output actions nested by file and project'
57
+ c.switch %[omnifocus], negatable: false
56
58
 
57
- c.action do |global_options, options, args|
58
- options[:nest] = true if options[:omnifocus]
59
+ c.action do |global_options, options, args|
60
+ options[:nest] = true if options[:omnifocus]
59
61
 
60
- if options[:save]
61
- title = options[:save].gsub(/[^a-z0-9]/, '_').gsub(/_+/, '_')
62
- NA.save_search(title, "#{NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.join(' ')}")
63
- end
62
+ if options[:save]
63
+ title = options[:save].gsub(/[^a-z0-9]/, '_').gsub(/_+/, '_')
64
+ NA.save_search(title, "#{NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.join(' ')}")
65
+ end
64
66
 
65
67
 
66
- depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
67
- 3
68
- else
69
- options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
70
- end
71
-
72
- all_req = options[:tagged].join(' ') !~ /[+!\-]/ && !options[:or]
73
- tags = []
74
- options[:tagged].join(',').split(/ *, */).each do |arg|
75
- m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
76
-
77
- tags.push({
78
- tag: m['tag'].wildcard_to_rx,
79
- comp: m['op'],
80
- value: m['val'],
81
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
82
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
83
- })
84
- end
68
+ depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
69
+ 3
70
+ else
71
+ options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
72
+ end
85
73
 
86
- tokens = nil
87
- if options[:exact]
88
- tokens = args.join(' ')
89
- elsif options[:regex]
90
- tokens = Regexp.new(args.join(' '), Regexp::IGNORECASE)
91
- else
92
- tokens = []
93
- all_req = args.join(' ') !~ /[+!\-]/ && !options[:or]
94
-
95
- args.join(' ').split(/ /).each do |arg|
96
- m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
97
- tokens.push({
98
- token: m['tok'],
99
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
100
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
101
- })
102
- end
103
- end
74
+ all_req = options[:tagged].join(' ') !~ /[+!\-]/ && !options[:or]
75
+ tags = []
76
+ options[:tagged].join(',').split(/ *, */).each do |arg|
77
+ m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
104
78
 
105
- todo = nil
106
- if options[:in]
107
- todo = []
108
- options[:in].split(/ *, */).each do |a|
109
- m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
110
- todo.push({
111
- token: m['tok'],
79
+ tags.push({
80
+ tag: m['tag'].wildcard_to_rx,
81
+ comp: m['op'],
82
+ value: m['val'],
112
83
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
113
84
  negate: !m['req'].nil? && m['req'] =~ /[!\-]/
114
85
  })
115
86
  end
116
- end
117
87
 
118
- files, actions, = NA.parse_actions(depth: depth,
119
- done: options[:done],
120
- query: todo,
121
- search: tokens,
122
- tag: tags,
123
- negate: options[:invert],
124
- regex: options[:regex],
125
- project: options[:project],
126
- require_na: false)
127
- regexes = if tokens.is_a?(Array)
128
- tokens.delete_if { |token| token[:negate] }.map { |token| token[:token] }
129
- else
130
- [tokens]
131
- end
88
+ tokens = nil
89
+ if options[:exact]
90
+ tokens = args.join(' ')
91
+ elsif options[:regex]
92
+ tokens = Regexp.new(args.join(' '), Regexp::IGNORECASE)
93
+ else
94
+ tokens = []
95
+ all_req = args.join(' ') !~ /[+!\-]/ && !options[:or]
96
+
97
+ args.join(' ').split(/ /).each do |arg|
98
+ m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
99
+ tokens.push({
100
+ token: m['tok'],
101
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
102
+ negate: !m['req'].nil? && m['req'] =~ /[!\-]/
103
+ })
104
+ end
105
+ end
132
106
 
133
- NA.output_actions(actions, depth, files: files, regexes: regexes, notes: options[:notes], nest: options[:nest], nest_projects: options[:omnifocus])
107
+ todo = nil
108
+ if options[:in]
109
+ todo = []
110
+ options[:in].split(/ *, */).each do |a|
111
+ 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
+ })
117
+ end
118
+ end
119
+
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)
129
+ regexes = if tokens.is_a?(Array)
130
+ tokens.delete_if { |token| token[:negate] }.map { |token| token[:token] }
131
+ else
132
+ [tokens]
133
+ end
134
+
135
+ NA.output_actions(actions, depth, files: files, regexes: regexes, notes: options[:notes], nest: options[:nest], nest_projects: options[:omnifocus])
136
+ end
134
137
  end
135
138
  end
data/bin/commands/init.rb CHANGED
@@ -1,28 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- desc 'Create a new todo file in the current directory'
4
- arg_name 'PROJECT', optional: true
5
- command %i[init create] do |c|
6
- c.example 'na init', desc: 'Generate a new todo file, prompting for project name'
7
- c.example 'na init warpspeed', desc: 'Generate a new todo for a project called warpspeed'
3
+ class App
4
+ extend GLI::App
5
+ desc 'Create a new todo file in the current directory'
6
+ arg_name 'PROJECT', optional: true
7
+ command %i[init create] do |c|
8
+ c.example 'na init', desc: 'Generate a new todo file, prompting for project name'
9
+ c.example 'na init warpspeed', desc: 'Generate a new todo for a project called warpspeed'
8
10
 
9
- c.action do |global_options, _options, args|
10
- reader = TTY::Reader.new
11
- if args.count.positive?
12
- project = args.join(' ')
13
- elsif
14
- project = File.expand_path('.').split('/').last
15
- project = reader.read_line(NA::Color.template('{y}Project name {bw}> {x}'), value: project).strip if $stdin.isatty
16
- end
11
+ c.action do |global_options, _options, args|
12
+ reader = TTY::Reader.new
13
+ if args.count.positive?
14
+ project = args.join(' ')
15
+ elsif
16
+ project = File.expand_path('.').split('/').last
17
+ project = reader.read_line(NA::Color.template('{y}Project name {bw}> {x}'), value: project).strip if $stdin.isatty
18
+ end
17
19
 
18
- target = "#{project}.#{NA.extension}"
20
+ target = "#{project}.#{NA.extension}"
19
21
 
20
- if File.exist?(target)
21
- res = NA.yn(NA::Color.template("{r}File {bw}#{target}{r} already exists, overwrite it"), default: false)
22
- Process.exit 1 unless res
22
+ if File.exist?(target)
23
+ res = NA.yn(NA::Color.template("{r}File {bw}#{target}{r} already exists, overwrite it"), default: false)
24
+ Process.exit 1 unless res
23
25
 
24
- end
26
+ end
25
27
 
26
- NA.create_todo(target, project, template: global_options[:template])
28
+ NA.create_todo(target, project, template: global_options[:template])
29
+ end
27
30
  end
28
31
  end
data/bin/commands/next.rb CHANGED
@@ -1,143 +1,147 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- desc 'Show next actions'
4
- long_desc 'Next actions are actions which contain the next action tag (default @na),
5
- do not contain @done, and are not in the Archive project.
6
-
7
- Arguments will target a todo file from history, whether it\'s in the current
8
- directory or not. Todo file queries can include path components separated by /
9
- or :, and may use wildcards (`*` to match any text, `?` to match a single character). Multiple queries allowed (separate arguments or separated by comma).'
10
- arg_name 'QUERY', optional: true
11
- command %i[next show] do |c|
12
- c.example 'na next', desc: 'display the next actions from any todo files in the current directory'
13
- c.example 'na next -d 3', desc: 'display the next actions from the current directory, traversing 3 levels deep'
14
- c.example 'na next marked', desc: 'display next actions for a project you visited in the past'
15
-
16
- c.desc 'Recurse to depth'
17
- c.arg_name 'DEPTH'
18
- c.flag %i[d depth], type: :integer, must_match: /^[1-9]$/
19
-
20
- c.desc 'Display matches from a known todo file'
21
- c.arg_name 'TODO_FILE'
22
- c.flag %i[in todo], multiple: true
23
-
24
- c.desc 'Alternate tag to search for'
25
- c.arg_name 'TAG'
26
- c.flag %i[t tag]
27
-
28
- c.desc 'Show actions from a specific project'
29
- c.arg_name 'PROJECT[/SUBPROJECT]'
30
- c.flag %i[proj project]
31
-
32
- c.desc 'Match actions containing tag. Allows value comparisons'
33
- c.arg_name 'TAG'
34
- c.flag %i[tagged], multiple: true
35
-
36
- c.desc 'Filter results using search terms'
37
- c.arg_name 'QUERY'
38
- c.flag %i[search], multiple: true
39
-
40
- c.desc 'Search query is regular expression'
41
- c.switch %i[regex], negatable: false
42
-
43
- c.desc 'Search query is exact text match (not tokens)'
44
- c.switch %i[exact], negatable: false
45
-
46
- c.desc 'Include notes in output'
47
- c.switch %i[notes], negatable: true, default_value: false
48
-
49
- c.desc 'Include @done actions'
50
- c.switch %i[done]
51
-
52
- c.desc 'Output actions nested by file'
53
- c.switch %[nest], negatable: false
54
-
55
- c.desc 'Output actions nested by file and project'
56
- c.switch %[omnifocus], negatable: false
57
-
58
- c.action do |global_options, options, args|
59
- if global_options[:add]
60
- cmd = ['add']
61
- cmd.push('--note') if global_options[:note]
62
- cmd.concat(['--priority', global_options[:priority]]) if global_options[:priority]
63
- cmd.push(NA.command_line) if NA.command_line.count > 1
64
- cmd.unshift(*NA.globals)
65
- exit run(cmd)
66
- end
3
+ class App
4
+ extend GLI::App
5
+ desc 'Show next actions'
6
+ long_desc 'Next actions are actions which contain the next action tag (default @na),
7
+ do not contain @done, and are not in the Archive project.
8
+
9
+ Arguments will target a todo file from history, whether it\'s in the current
10
+ directory or not. Todo file queries can include path components separated by /
11
+ or :, and may use wildcards (`*` to match any text, `?` to match a single character). Multiple queries allowed (separate arguments or separated by comma).'
12
+ arg_name 'QUERY', optional: true
13
+ command %i[next show] do |c|
14
+ c.example 'na next', desc: 'display the next actions from any todo files in the current directory'
15
+ c.example 'na next -d 3', desc: 'display the next actions from the current directory, traversing 3 levels deep'
16
+ c.example 'na next marked', desc: 'display next actions for a project you visited in the past'
17
+
18
+ c.desc 'Recurse to depth'
19
+ c.arg_name 'DEPTH'
20
+ c.flag %i[d depth], type: :integer, must_match: /^[1-9]$/
21
+
22
+ c.desc 'Display matches from a known todo file'
23
+ c.arg_name 'TODO_FILE'
24
+ c.flag %i[in todo], multiple: true
25
+
26
+ c.desc 'Alternate tag to search for'
27
+ c.arg_name 'TAG'
28
+ c.flag %i[t tag]
29
+
30
+ c.desc 'Show actions from a specific project'
31
+ c.arg_name 'PROJECT[/SUBPROJECT]'
32
+ c.flag %i[proj project]
33
+
34
+ c.desc 'Match actions containing tag. Allows value comparisons'
35
+ c.arg_name 'TAG'
36
+ c.flag %i[tagged], multiple: true
37
+
38
+ c.desc 'Filter results using search terms'
39
+ c.arg_name 'QUERY'
40
+ c.flag %i[search], multiple: true
41
+
42
+ c.desc 'Search query is regular expression'
43
+ c.switch %i[regex], negatable: false
44
+
45
+ c.desc 'Search query is exact text match (not tokens)'
46
+ c.switch %i[exact], negatable: false
47
+
48
+ c.desc 'Include notes in output'
49
+ c.switch %i[notes], negatable: true, default_value: false
50
+
51
+ c.desc 'Include @done actions'
52
+ c.switch %i[done]
53
+
54
+ c.desc 'Output actions nested by file'
55
+ c.switch %[nest], negatable: false
56
+
57
+ c.desc 'Output actions nested by file and project'
58
+ c.switch %[omnifocus], negatable: false
59
+
60
+ c.action do |global_options, options, args|
61
+ if global_options[:add]
62
+ cmd = ['add']
63
+ cmd.push('--note') if global_options[:note]
64
+ cmd.concat(['--priority', global_options[:priority]]) if global_options[:priority]
65
+ cmd.push(NA.command_line) if NA.command_line.count > 1
66
+ cmd.unshift(*NA.globals)
67
+ exit run(cmd)
68
+ end
67
69
 
68
- options[:nest] = true if options[:omnifocus]
69
-
70
- depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
71
- 3
72
- else
73
- options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
74
- end
75
-
76
- all_req = options[:tagged].join(' ') !~ /[+!\-]/ && !options[:or]
77
- tags = []
78
- options[:tagged].join(',').split(/ *, */).each do |arg|
79
- m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
80
-
81
- tags.push({
82
- tag: m['tag'].wildcard_to_rx,
83
- comp: m['op'],
84
- value: m['val'],
85
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
86
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
87
- })
88
- end
70
+ options[:nest] = true if options[:omnifocus]
71
+
72
+ depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
73
+ 3
74
+ else
75
+ options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
76
+ end
77
+
78
+ all_req = options[:tagged].join(' ') !~ /[+!\-]/ && !options[:or]
79
+ tags = []
80
+ options[:tagged].join(',').split(/ *, */).each do |arg|
81
+ m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
82
+
83
+ tags.push({
84
+ tag: m['tag'].wildcard_to_rx,
85
+ comp: m['op'],
86
+ value: m['val'],
87
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
88
+ negate: !m['req'].nil? && m['req'] =~ /[!\-]/
89
+ })
90
+ end
89
91
 
90
- args.concat(options[:in])
91
- if args.count.positive?
92
- all_req = args.join(' ') !~ /[+!\-]/
93
-
94
- tokens = []
95
- args.each do |arg|
96
- arg.split(/ *, */).each do |a|
97
- m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
98
- tokens.push({
99
- token: m['tok'],
100
- required: !m['req'].nil? && m['req'] == '+',
101
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
102
- })
92
+ args.concat(options[:in])
93
+ if args.count.positive?
94
+ all_req = args.join(' ') !~ /[+!\-]/
95
+
96
+ tokens = []
97
+ args.each do |arg|
98
+ arg.split(/ *, */).each do |a|
99
+ m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
100
+ tokens.push({
101
+ token: m['tok'],
102
+ required: !m['req'].nil? && m['req'] == '+',
103
+ negate: !m['req'].nil? && m['req'] =~ /[!\-]/
104
+ })
105
+ end
103
106
  end
104
107
  end
105
- end
106
108
 
107
- search = nil
108
- if options[:search]
109
- if options[:exact]
110
- search = options[:search].join(' ')
111
- elsif options[:regex]
112
- search = Regexp.new(options[:search].join(' '), Regexp::IGNORECASE)
113
- else
114
- search = []
115
- all_req = options[:search].join(' ') !~ /[+!\-]/ && !options[:or]
116
-
117
- options[:search].join(' ').split(/ /).each do |arg|
118
- m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
119
- search.push({
120
- token: m['tok'],
121
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
122
- negate: !m['req'].nil? && m['req'] =~ /[!\-]/
123
- })
109
+ search = nil
110
+ if options[:search]
111
+ if options[:exact]
112
+ search = options[:search].join(' ')
113
+ elsif options[:regex]
114
+ search = Regexp.new(options[:search].join(' '), Regexp::IGNORECASE)
115
+ else
116
+ search = []
117
+ all_req = options[:search].join(' ') !~ /[+!\-]/ && !options[:or]
118
+
119
+ options[:search].join(' ').split(/ /).each do |arg|
120
+ m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
121
+ search.push({
122
+ token: m['tok'],
123
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
124
+ negate: !m['req'].nil? && m['req'] =~ /[!\-]/
125
+ })
126
+ end
124
127
  end
125
128
  end
126
- end
127
-
128
- NA.na_tag = options[:tag] unless options[:tag].nil?
129
- require_na = true
130
129
 
131
- tag = [{ tag: NA.na_tag, value: nil }, { tag: 'done', value: nil, negate: true }]
132
- tag.concat(tags)
133
- files, actions, = NA.parse_actions(depth: depth,
134
- done: options[:done],
135
- query: tokens,
136
- tag: tag,
137
- search: search,
138
- project: options[:project],
139
- require_na: require_na)
140
-
141
- NA.output_actions(actions, depth, files: files, notes: options[:notes], nest: options[:nest], nest_projects: options[:omnifocus])
130
+ NA.na_tag = options[:tag] unless options[:tag].nil?
131
+ require_na = true
132
+
133
+ tag = [{ tag: NA.na_tag, value: nil }]
134
+ tag << { tag: 'done', value: nil, negate: true } unless options[:done]
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])
145
+ end
142
146
  end
143
147
  end
@@ -1,34 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- desc 'Show list of projects for a file'
4
- long_desc 'Arguments will be interpreted as a query for a known todo file,
5
- fuzzy matched. Separate directories with /, :, or a space, e.g. `na projects code/marked`'
6
- arg_name 'QUERY', optional: true
7
- command %i[projects] do |c|
8
- c.desc 'Search for files X directories deep'
9
- c.arg_name 'DEPTH'
10
- c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
3
+ class App
4
+ extend GLI::App
5
+ desc 'Show list of projects for a file'
6
+ long_desc 'Arguments will be interpreted as a query for a known todo file,
7
+ fuzzy matched. Separate directories with /, :, or a space, e.g. `na projects code/marked`'
8
+ arg_name 'QUERY', optional: true
9
+ command %i[projects] do |c|
10
+ c.desc 'Search for files X directories deep'
11
+ c.arg_name 'DEPTH'
12
+ c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
11
13
 
12
- c.desc 'Output projects as paths instead of hierarchy'
13
- c.switch %i[p paths], negatable: false
14
+ c.desc 'Output projects as paths instead of hierarchy'
15
+ c.switch %i[p paths], negatable: false
14
16
 
15
- c.action do |_global_options, options, args|
16
- if args.count.positive?
17
- all_req = args.join(' ') !~ /[+!-]/
17
+ c.action do |_global_options, options, args|
18
+ if args.count.positive?
19
+ all_req = args.join(' ') !~ /[+!-]/
18
20
 
19
- tokens = [{ token: '*', required: all_req, negate: false }]
20
- args.each do |arg|
21
- arg.split(/ *, */).each do |a|
22
- m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
23
- tokens.push({
24
- token: m['tok'],
25
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
26
- negate: !m['req'].nil? && m['req'] =~ /[!-]/
27
- })
21
+ tokens = [{ token: '*', required: all_req, negate: false }]
22
+ args.each do |arg|
23
+ arg.split(/ *, */).each do |a|
24
+ m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
25
+ tokens.push({
26
+ token: m['tok'],
27
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
28
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/
29
+ })
30
+ end
28
31
  end
29
32
  end
30
- end
31
33
 
32
- NA.list_projects(query: tokens, depth: options[:depth], paths: options[:paths])
34
+ NA.list_projects(query: tokens, depth: options[:depth], paths: options[:paths])
35
+ end
33
36
  end
34
37
  end