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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +1 -1
- data/README.md +28 -20
- data/bin/commands/add.rb +167 -164
- data/bin/commands/archive.rb +54 -51
- data/bin/commands/changes.rb +18 -15
- data/bin/commands/complete.rb +50 -47
- data/bin/commands/completed.rb +99 -0
- data/bin/commands/edit.rb +37 -34
- data/bin/commands/find.rb +109 -106
- data/bin/commands/init.rb +22 -19
- data/bin/commands/next.rb +135 -131
- data/bin/commands/projects.rb +27 -24
- data/bin/commands/prompt.rb +41 -38
- data/bin/commands/saved.rb +42 -34
- data/bin/commands/tagged.rb +137 -134
- data/bin/commands/todos.rb +23 -20
- data/bin/commands/update.rb +215 -207
- data/bin/na +7 -1166
- data/lib/na/array.rb +13 -0
- data/lib/na/help_monkey_patch.rb +32 -0
- data/lib/na/next_action.rb +3 -2
- data/lib/na/pager.rb +94 -0
- data/lib/na/string.rb +10 -0
- data/lib/na/version.rb +1 -1
- data/lib/na.rb +2 -0
- metadata +7 -3
data/bin/commands/changes.rb
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
class App
|
|
4
|
+
extend GLI::App
|
|
5
|
+
desc 'Display the changelog'
|
|
6
|
+
command %i[changes changelog] do |c|
|
|
7
|
+
c.action do |_, _, _|
|
|
8
|
+
changelog = File.expand_path(File.join(File.dirname(__FILE__), '..', 'CHANGELOG.md'))
|
|
9
|
+
pagers = [
|
|
10
|
+
'mdless',
|
|
11
|
+
'mdcat',
|
|
12
|
+
'bat',
|
|
13
|
+
ENV['PAGER'],
|
|
14
|
+
'less -FXr',
|
|
15
|
+
ENV['GIT_PAGER'],
|
|
16
|
+
'more -r'
|
|
17
|
+
]
|
|
18
|
+
pager = pagers.find { |cmd| TTY::Which.exist?(cmd.split.first) }
|
|
19
|
+
system %(#{pager} "#{changelog}")
|
|
20
|
+
end
|
|
18
21
|
end
|
|
19
22
|
end
|
data/bin/commands/complete.rb
CHANGED
|
@@ -1,51 +1,54 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
options
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
3
|
+
class App
|
|
4
|
+
extend GLI::App
|
|
5
|
+
desc 'Find and mark an action as @done'
|
|
6
|
+
arg_name 'ACTION'
|
|
7
|
+
command %i[complete finish] do |c|
|
|
8
|
+
c.example 'na complete "An existing task"',
|
|
9
|
+
desc: 'Find "An existing task" and mark @done'
|
|
10
|
+
c.example 'na finish "An existing task"',
|
|
11
|
+
desc: 'Alias for complete'
|
|
12
|
+
|
|
13
|
+
c.desc 'Prompt for additional notes. Input will be appended to any existing note.
|
|
14
|
+
If STDIN input (piped) is detected, it will be used as a note.'
|
|
15
|
+
c.switch %i[n note], negatable: false
|
|
16
|
+
|
|
17
|
+
c.desc 'Overwrite note instead of appending'
|
|
18
|
+
c.switch %i[o overwrite], negatable: false
|
|
19
|
+
|
|
20
|
+
c.desc 'Add a @done tag to action and move to Archive'
|
|
21
|
+
c.switch %i[a archive], negatable: false
|
|
22
|
+
|
|
23
|
+
c.desc 'Specify the file to search for the task'
|
|
24
|
+
c.arg_name 'PATH'
|
|
25
|
+
c.flag %i[file]
|
|
26
|
+
|
|
27
|
+
c.desc 'Search for files X directories deep'
|
|
28
|
+
c.arg_name 'DEPTH'
|
|
29
|
+
c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
|
|
30
|
+
|
|
31
|
+
c.desc 'Match actions containing tag. Allows value comparisons'
|
|
32
|
+
c.arg_name 'TAG'
|
|
33
|
+
c.flag %i[tagged], multiple: true
|
|
34
|
+
|
|
35
|
+
c.desc 'Act on all matches immediately (no menu)'
|
|
36
|
+
c.switch %i[all], negatable: false
|
|
37
|
+
|
|
38
|
+
c.desc 'Interpret search pattern as regular expression'
|
|
39
|
+
c.switch %i[e regex], negatable: false
|
|
40
|
+
|
|
41
|
+
c.desc 'Match pattern exactly'
|
|
42
|
+
c.switch %i[x exact], negatable: false
|
|
43
|
+
|
|
44
|
+
c.action do |global, options, args|
|
|
45
|
+
options[:finish] = true
|
|
46
|
+
options[:f] = true
|
|
47
|
+
options[:project] = 'Archive' if options[:archive]
|
|
48
|
+
|
|
49
|
+
cmd = commands[:update]
|
|
50
|
+
action = cmd.send(:get_action, nil)
|
|
51
|
+
action.call(global, options, args)
|
|
52
|
+
end
|
|
50
53
|
end
|
|
51
54
|
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class App
|
|
4
|
+
extend GLI::App
|
|
5
|
+
desc 'Display completed actions'
|
|
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 completed +feature +maybe`,
|
|
8
|
+
add a - or ! to ignore matches containing that token.'
|
|
9
|
+
arg_name 'PATTERN', optional: true, multiple: true
|
|
10
|
+
command %i[completed finished] do |c|
|
|
11
|
+
c.example 'na completed', desc: 'display completed actions'
|
|
12
|
+
c.example 'na completed --before "2 days ago"',
|
|
13
|
+
desc: 'display actions completed more than two days ago'
|
|
14
|
+
c.example 'na completed --on yesterday',
|
|
15
|
+
desc: 'display actions completed yesterday'
|
|
16
|
+
c.example 'na completed --after "1 week ago"',
|
|
17
|
+
desc: 'display actions completed in the last week'
|
|
18
|
+
c.example 'na completed feature',
|
|
19
|
+
desc: 'display completed actions matcning "feature"'
|
|
20
|
+
|
|
21
|
+
c.desc 'Display actions completed before (natural language) date string'
|
|
22
|
+
c.arg_name 'DATE_STRING'
|
|
23
|
+
c.flag %i[b before]
|
|
24
|
+
|
|
25
|
+
c.desc 'Display actions completed on (natural language) date string'
|
|
26
|
+
c.arg_name 'DATE_STRING'
|
|
27
|
+
c.flag %i[on]
|
|
28
|
+
|
|
29
|
+
c.desc 'Display actions completed after (natural language) date string'
|
|
30
|
+
c.arg_name 'DATE_STRING'
|
|
31
|
+
c.flag %i[a after]
|
|
32
|
+
|
|
33
|
+
c.desc 'Combine before, on, and/or after with OR, displaying actions matching ANY of the ranges'
|
|
34
|
+
c.switch %i[o or], negatable: false
|
|
35
|
+
|
|
36
|
+
c.desc 'Recurse to depth'
|
|
37
|
+
c.arg_name 'DEPTH'
|
|
38
|
+
c.flag %i[d depth], type: :integer, must_match: /^\d+$/
|
|
39
|
+
|
|
40
|
+
c.desc 'Show actions from a specific todo file in history. May use wildcards (* and ?)'
|
|
41
|
+
c.arg_name 'TODO_PATH'
|
|
42
|
+
c.flag %i[in]
|
|
43
|
+
|
|
44
|
+
c.desc 'Include notes in output'
|
|
45
|
+
c.switch %i[notes], negatable: true, default_value: false
|
|
46
|
+
|
|
47
|
+
c.desc 'Show actions from a specific project'
|
|
48
|
+
c.arg_name 'PROJECT[/SUBPROJECT]'
|
|
49
|
+
c.flag %i[proj project]
|
|
50
|
+
|
|
51
|
+
c.desc 'Match actions containing tag. Allows value comparisons'
|
|
52
|
+
c.arg_name 'TAG'
|
|
53
|
+
c.flag %i[tagged], multiple: true
|
|
54
|
+
|
|
55
|
+
c.desc 'Output actions nested by file'
|
|
56
|
+
c.switch %[nest], negatable: false
|
|
57
|
+
|
|
58
|
+
c.desc 'Output actions nested by file and project'
|
|
59
|
+
c.switch %[omnifocus], negatable: false
|
|
60
|
+
|
|
61
|
+
c.desc 'Save this search for future use'
|
|
62
|
+
c.arg_name 'TITLE'
|
|
63
|
+
c.flag %i[save]
|
|
64
|
+
|
|
65
|
+
c.action do |_global_options, options, args|
|
|
66
|
+
tag_string = []
|
|
67
|
+
if options[:before] || options[:on] || options[:after]
|
|
68
|
+
tag_string << "done<#{options[:before]}" if options[:before]
|
|
69
|
+
tag_string << "done=#{options[:on]}" if options[:on]
|
|
70
|
+
tag_string << "done>#{options[:after]}" if options[:after]
|
|
71
|
+
else
|
|
72
|
+
tag_string << 'done'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
tag_string.concat(options[:tagged]) if options[:tagged]
|
|
76
|
+
|
|
77
|
+
if args.empty?
|
|
78
|
+
cmd_string = %(tagged --done "#{tag_string.join(',')}")
|
|
79
|
+
else
|
|
80
|
+
cmd_string = %(find --tagged "#{tag_string.join(',')}" --done #{args.join(' ')})
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
cmd_string += ' --or' if options[:or]
|
|
84
|
+
cmd_string += %( --in "#{options[:in]}") if options[:in]
|
|
85
|
+
cmd_string += %( --project "#{options[:project]}") if options[:project]
|
|
86
|
+
cmd_string += %( --depth #{options[:depth]}) if options[:depth]
|
|
87
|
+
cmd_string += ' --nest' if options[:nest]
|
|
88
|
+
cmd_string += ' --omnifocus' if options[:omnifocus]
|
|
89
|
+
cmd_string += " --#{options[:notes] ? 'notes' : 'no-notes' }"
|
|
90
|
+
|
|
91
|
+
if options[:save]
|
|
92
|
+
title = options[:save].gsub(/[^a-z0-9]/, '_').gsub(/_+/, '_')
|
|
93
|
+
NA.save_search(title, cmd_string)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
exit run(Shellwords.shellsplit(cmd_string))
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
data/bin/commands/edit.rb
CHANGED
|
@@ -1,45 +1,48 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
command
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
class App
|
|
4
|
+
extend GLI::App
|
|
5
|
+
desc 'Open a todo file in the default editor'
|
|
6
|
+
long_desc 'Let the system choose the defualt, (e.g. TaskPaper), or specify a command line utility (e.g. vim).
|
|
7
|
+
If more than one todo file is found, a menu is displayed.'
|
|
8
|
+
command %i[edit] do |c|
|
|
9
|
+
c.example 'na edit', desc: 'Open the main todo file in the default editor'
|
|
10
|
+
c.example 'na edit -d 3 -a vim', desc: 'Display a menu of all todo files three levels deep from the
|
|
11
|
+
current directory, open selection in vim.'
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
c.desc 'Recurse to depth'
|
|
14
|
+
c.arg_name 'DEPTH'
|
|
15
|
+
c.default_value 1
|
|
16
|
+
c.flag %i[d depth], type: :integer, must_match: /^\d+$/
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
c.desc 'Specify an editor CLI'
|
|
19
|
+
c.arg_name 'EDITOR'
|
|
20
|
+
c.flag %i[e editor]
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
c.desc 'Specify a Mac app'
|
|
23
|
+
c.arg_name 'EDITOR'
|
|
24
|
+
c.flag %i[a app]
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
c.action do |global_options, options, args|
|
|
27
|
+
depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
|
|
28
|
+
3
|
|
29
|
+
else
|
|
30
|
+
options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
|
|
31
|
+
end
|
|
32
|
+
files = NA.find_files(depth: depth)
|
|
33
|
+
files.delete_if { |f| f !~ /.*?(#{args.join('|')}).*?.#{NA.extension}/ } if args.count.positive?
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
file = if files.count > 1
|
|
36
|
+
NA.select_file(files)
|
|
37
|
+
else
|
|
38
|
+
files[0]
|
|
39
|
+
end
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
if options[:editor]
|
|
42
|
+
system options[:editor], file
|
|
43
|
+
else
|
|
44
|
+
NA.edit_file(file: file, app: options[:app])
|
|
45
|
+
end
|
|
43
46
|
end
|
|
44
47
|
end
|
|
45
48
|
end
|
data/bin/commands/find.rb
CHANGED
|
@@ -1,135 +1,138 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
+
c.desc 'Interpret search pattern as regular expression'
|
|
16
|
+
c.switch %i[e regex], negatable: false
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
c.desc 'Match pattern exactly'
|
|
19
|
+
c.switch %i[x exact], negatable: false
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
+
c.desc 'Include notes in output'
|
|
30
|
+
c.switch %i[notes], negatable: true, default_value: false
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
c.desc 'Show actions from a specific project'
|
|
36
|
+
c.arg_name 'PROJECT[/SUBPROJECT]'
|
|
37
|
+
c.flag %i[proj project]
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
+
c.desc 'Include @done actions'
|
|
44
|
+
c.switch %i[done]
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
c.desc 'Show actions not matching search pattern'
|
|
47
|
+
c.switch %i[v invert], negatable: false
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
c.desc 'Save this search for future use'
|
|
50
|
+
c.arg_name 'TITLE'
|
|
51
|
+
c.flag %i[save]
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
c.desc 'Output actions nested by file'
|
|
54
|
+
c.switch %[nest], negatable: false
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
c.desc 'Output actions nested by file and project'
|
|
57
|
+
c.switch %[omnifocus], negatable: false
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
c.action do |global_options, options, args|
|
|
60
|
+
options[:nest] = true if options[:omnifocus]
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
20
|
+
target = "#{project}.#{NA.extension}"
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
+
end
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
NA.create_todo(target, project, template: global_options[:template])
|
|
29
|
+
end
|
|
27
30
|
end
|
|
28
31
|
end
|