na 1.2.29 → 1.2.30

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.
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
@@ -1,48 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- desc 'Show or install prompt hooks for the current shell'
4
- long_desc 'Installing the prompt hook allows you to automatically
5
- list next actions when you cd into a directory'
6
- command %i[prompt] do |c|
7
- c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to
8
- specify a shell (zsh, bash, fish)'
9
- c.arg_name 'SHELL', optional: true
10
- c.command %i[show] do |s|
11
- s.action do |_global_options, _options, args|
12
- shell = if args.count.positive?
13
- args[0]
14
- else
15
- File.basename(ENV['SHELL'])
16
- end
3
+ class App
4
+ extend GLI::App
5
+ desc 'Show or install prompt hooks for the current shell'
6
+ long_desc 'Installing the prompt hook allows you to automatically
7
+ list next actions when you cd into a directory'
8
+ command %i[prompt] do |c|
9
+ c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to
10
+ specify a shell (zsh, bash, fish)'
11
+ c.arg_name 'SHELL', optional: true
12
+ c.command %i[show] do |s|
13
+ s.action do |_global_options, _options, args|
14
+ shell = if args.count.positive?
15
+ args[0]
16
+ else
17
+ File.basename(ENV['SHELL'])
18
+ end
17
19
 
18
- case shell
19
- when /^f/i
20
- NA::Prompt.show_prompt_hook(:fish)
21
- when /^z/i
22
- NA::Prompt.show_prompt_hook(:zsh)
23
- when /^b/i
24
- NA::Prompt.show_prompt_hook(:bash)
20
+ case shell
21
+ when /^f/i
22
+ NA::Prompt.show_prompt_hook(:fish)
23
+ when /^z/i
24
+ NA::Prompt.show_prompt_hook(:zsh)
25
+ when /^b/i
26
+ NA::Prompt.show_prompt_hook(:bash)
27
+ end
25
28
  end
26
29
  end
27
- end
28
30
 
29
- c.desc 'Install the hook for the current shell to the appropriate startup file.'
30
- c.arg_name 'SHELL', optional: true
31
- c.command %i[install] do |s|
32
- s.action do |_global_options, _options, args|
33
- shell = if args.count.positive?
34
- args[0]
35
- else
36
- File.basename(ENV['SHELL'])
37
- end
31
+ c.desc 'Install the hook for the current shell to the appropriate startup file.'
32
+ c.arg_name 'SHELL', optional: true
33
+ c.command %i[install] do |s|
34
+ s.action do |_global_options, _options, args|
35
+ shell = if args.count.positive?
36
+ args[0]
37
+ else
38
+ File.basename(ENV['SHELL'])
39
+ end
38
40
 
39
- case shell
40
- when /^f/i
41
- NA::Prompt.install_prompt_hook(:fish)
42
- when /^z/i
43
- NA::Prompt.install_prompt_hook(:zsh)
44
- when /^b/i
45
- NA::Prompt.install_prompt_hook(:bash)
41
+ case shell
42
+ when /^f/i
43
+ NA::Prompt.install_prompt_hook(:fish)
44
+ when /^z/i
45
+ NA::Prompt.install_prompt_hook(:zsh)
46
+ when /^b/i
47
+ NA::Prompt.install_prompt_hook(:bash)
48
+ end
46
49
  end
47
50
  end
48
51
  end
@@ -1,39 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- desc 'Execute a saved search'
4
- long_desc 'Run without argument to list saved searches'
5
- arg_name 'SEARCH_TITLE', optional: true
6
- command %i[saved] do |c|
7
- c.example 'na tagged "+maybe,+priority<=3" --save maybelater', description: 'save a search called "maybelater"'
8
- c.example 'na saved maybelater', description: 'perform the search named "maybelater"'
9
- c.example 'na saved maybe',
10
- description: 'perform the search named "maybelater", assuming no other searches match "maybe"'
11
- c.example 'na maybe',
12
- description: 'na run with no command and a single argument automatically performs a matching saved search'
13
- c.example 'na saved', description: 'list available searches'
14
-
15
- c.desc 'Open the saved search file in $EDITOR'
16
- c.switch %i[e edit], negatable: false
17
-
18
- c.desc 'Delete the specified search definition'
19
- c.switch %i[d delete], negatable: false
20
-
21
- c.action do |_global_options, options, args|
22
- NA.edit_searches if options[:edit]
23
-
24
- searches = NA.load_searches
25
- if args.empty?
26
- NA.notify("{bg}Saved searches stored in {bw}#{NA.database_path(file: 'saved_searches.yml')}")
27
- NA.notify(searches.map { |k, v| "{y}#{k}: {w}#{v}" }.join("\n"), exit_code: 0)
28
- else
29
- NA.delete_search(args) if options[:delete]
30
-
31
- keys = searches.keys.delete_if { |k| k !~ /#{args[0]}/ }
32
- NA.notify("{r}Search #{args[0]} not found", exit_code: 1) if keys.empty?
33
-
34
- key = keys[0]
35
- cmd = Shellwords.shellsplit(searches[key])
36
- exit run(cmd)
3
+ class App
4
+ extend GLI::App
5
+ desc 'Execute a saved search'
6
+ long_desc 'Run without argument to list saved searches'
7
+ arg_name 'SEARCH_TITLE', optional: true, multiple: true
8
+ command %i[saved] do |c|
9
+ c.example 'na tagged "+maybe,+priority<=3" --save maybelater', desc: 'save a search called "maybelater"'
10
+ c.example 'na saved maybelater', desc: 'perform the search named "maybelater"'
11
+ c.example 'na saved maybe',
12
+ desc: 'perform the search named "maybelater", assuming no other searches match "maybe"'
13
+ c.example 'na maybe',
14
+ desc: 'na run with no command and a single argument automatically performs a matching saved search'
15
+ c.example 'na saved', desc: 'list available searches'
16
+
17
+ c.desc 'Open the saved search file in $EDITOR'
18
+ c.switch %i[e edit], negatable: false
19
+
20
+ c.desc 'Delete the specified search definition'
21
+ c.switch %i[d delete], negatable: false
22
+
23
+ c.action do |_global_options, options, args|
24
+ NA.edit_searches if options[:edit]
25
+
26
+ if args.empty?
27
+ searches = NA.load_searches
28
+ NA.notify("{bg}Saved searches stored in {bw}#{NA.database_path(file: 'saved_searches.yml')}")
29
+ NA.notify(searches.map { |k, v| "{y}#{k}: {w}#{v}" }.join("\n"), exit_code: 0)
30
+ else
31
+ args.each do |arg|
32
+ searches = NA.load_searches
33
+
34
+ NA.delete_search(arg) if options[:delete]
35
+
36
+ keys = searches.keys.delete_if { |k| k !~ /#{arg}/ }
37
+ NA.notify("{r}Search #{arg} not found", exit_code: 1) if keys.empty?
38
+
39
+ key = keys[0]
40
+ cmd = Shellwords.shellsplit(searches[key])
41
+ run(cmd)
42
+ end
43
+ exit
44
+ end
37
45
  end
38
46
  end
39
47
  end