na 1.2.74 → 1.2.76
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/.rubocop.yml +4 -0
- data/.rubocop_todo.yml +623 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +25 -8
- data/Gemfile +1 -0
- data/Gemfile.lock +55 -39
- data/README.md +5 -3
- data/Rakefile +147 -32
- data/bin/commands/find.rb +71 -67
- data/bin/commands/next.rb +85 -85
- data/bin/commands/tagged.rb +64 -60
- data/bin/na +58 -1
- data/docker/Dockerfile +10 -0
- data/docker/Dockerfile-2.6 +11 -0
- data/docker/Dockerfile-2.7 +11 -0
- data/docker/Dockerfile-3.0 +11 -0
- data/docker/Dockerfile-3.3 +12 -0
- data/docker/bash_profile +17 -0
- data/docker/inputrc +57 -0
- data/docker/sources.list +11 -0
- data/lib/na/actions.rb +48 -46
- data/lib/na/next_action.rb +1 -1
- data/lib/na/version.rb +1 -1
- data/na.gemspec +2 -3
- data/scripts/generate-fish-completions.rb +170 -0
- data/scripts/runtests.sh +5 -0
- data/src/_README.md +1 -1
- metadata +49 -51
data/bin/commands/next.rb
CHANGED
@@ -2,108 +2,108 @@
|
|
2
2
|
|
3
3
|
class App
|
4
4
|
extend GLI::App
|
5
|
-
desc
|
5
|
+
desc "Show next actions"
|
6
6
|
long_desc 'Next actions are actions which contain the next action tag (default @na),
|
7
7
|
do not contain @done, and are not in the Archive project.
|
8
8
|
|
9
9
|
Arguments will target a todo file from history, whether it\'s in the current
|
10
10
|
directory or not. Todo file queries can include path components separated by /
|
11
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
|
12
|
+
arg_name "QUERY", optional: true
|
13
13
|
command %i[next show] do |c|
|
14
|
-
c.example
|
15
|
-
c.example
|
16
|
-
c.example
|
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
17
|
|
18
|
-
c.desc
|
19
|
-
c.arg_name
|
18
|
+
c.desc "Recurse to depth"
|
19
|
+
c.arg_name "DEPTH"
|
20
20
|
c.flag %i[d depth], type: :integer, must_match: /^[1-9]$/
|
21
21
|
|
22
|
-
c.desc
|
22
|
+
c.desc "Show next actions from all known todo files (in any directory)"
|
23
23
|
c.switch %i[all], negatable: false, default_value: false
|
24
24
|
|
25
|
-
c.desc
|
26
|
-
c.arg_name
|
25
|
+
c.desc "Display matches from a known todo file anywhere in history (short name)"
|
26
|
+
c.arg_name "TODO"
|
27
27
|
c.flag %i[in todo], multiple: true
|
28
28
|
|
29
|
-
c.desc
|
30
|
-
c.arg_name
|
29
|
+
c.desc "Display matches from specific todo file ([relative] path)"
|
30
|
+
c.arg_name "TODO_FILE"
|
31
31
|
c.flag %i[file]
|
32
32
|
|
33
|
-
c.desc
|
34
|
-
c.arg_name
|
33
|
+
c.desc "Alternate tag to search for"
|
34
|
+
c.arg_name "TAG"
|
35
35
|
c.flag %i[t tag]
|
36
36
|
|
37
|
-
c.desc
|
38
|
-
c.arg_name
|
37
|
+
c.desc "Show actions from a specific project"
|
38
|
+
c.arg_name "PROJECT[/SUBPROJECT]"
|
39
39
|
c.flag %i[proj project]
|
40
40
|
|
41
|
-
c.desc
|
42
|
-
c.arg_name
|
41
|
+
c.desc "Match actions containing tag. Allows value comparisons"
|
42
|
+
c.arg_name "TAG"
|
43
43
|
c.flag %i[tagged], multiple: true
|
44
44
|
|
45
|
-
c.desc
|
46
|
-
c.arg_name
|
45
|
+
c.desc "Match actions with priority, allows <>= comparison"
|
46
|
+
c.arg_name "PRIORITY"
|
47
47
|
c.flag %i[p prio priority], multiple: true
|
48
48
|
|
49
|
-
c.desc
|
50
|
-
c.arg_name
|
49
|
+
c.desc "Filter results using search terms"
|
50
|
+
c.arg_name "QUERY"
|
51
51
|
c.flag %i[search find grep], multiple: true
|
52
52
|
|
53
|
-
c.desc
|
53
|
+
c.desc "Include notes in search"
|
54
54
|
c.switch %i[search_notes], negatable: true, default_value: true
|
55
55
|
|
56
|
-
c.desc
|
56
|
+
c.desc "Search query is regular expression"
|
57
57
|
c.switch %i[regex], negatable: false
|
58
58
|
|
59
|
-
c.desc
|
59
|
+
c.desc "Search query is exact text match (not tokens)"
|
60
60
|
c.switch %i[exact], negatable: false
|
61
61
|
|
62
|
-
c.desc
|
62
|
+
c.desc "Include notes in output"
|
63
63
|
c.switch %i[notes], negatable: true, default_value: false
|
64
64
|
|
65
|
-
c.desc
|
65
|
+
c.desc "Include @done actions"
|
66
66
|
c.switch %i[done]
|
67
67
|
|
68
|
-
c.desc
|
68
|
+
c.desc "Output actions nested by file"
|
69
69
|
c.switch %i[nest], negatable: false
|
70
70
|
|
71
|
-
c.desc
|
71
|
+
c.desc "No filename in output"
|
72
72
|
c.switch %i[no_file], negatable: false
|
73
73
|
|
74
|
-
c.desc
|
74
|
+
c.desc "Output actions nested by file and project"
|
75
75
|
c.switch %i[omnifocus], negatable: false
|
76
76
|
|
77
|
-
c.desc
|
78
|
-
c.arg_name
|
77
|
+
c.desc "Save this search for future use"
|
78
|
+
c.arg_name "TITLE"
|
79
79
|
c.flag %i[save]
|
80
80
|
|
81
81
|
c.action do |global_options, options, args|
|
82
82
|
# For backward compatibility with na -a
|
83
83
|
if global_options[:add]
|
84
|
-
cmd = [
|
85
|
-
cmd.push(
|
86
|
-
cmd.concat([
|
84
|
+
cmd = ["add"]
|
85
|
+
cmd.push("--note") if global_options[:note]
|
86
|
+
cmd.concat(["--priority", global_options[:priority]]) if global_options[:priority]
|
87
87
|
cmd.push(NA.command_line) if NA.command_line.count > 1
|
88
88
|
cmd.unshift(*NA.globals)
|
89
89
|
exit run(cmd)
|
90
90
|
end
|
91
91
|
|
92
92
|
if options[:save]
|
93
|
-
title = options[:save].gsub(/[^a-z0-9]/,
|
94
|
-
NA.save_search(title, "#{NA.command_line.join(
|
93
|
+
title = options[:save].gsub(/[^a-z0-9]/, "_").gsub(/_+/, "_")
|
94
|
+
NA.save_search(title, "#{NA.command_line.join(" ").sub(/ --save[= ]*\S+/, "").split(" ").map { |t| %("#{t}") }.join(" ")}")
|
95
95
|
end
|
96
96
|
|
97
97
|
options[:nest] = true if options[:omnifocus]
|
98
98
|
|
99
99
|
depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
3
|
101
|
+
else
|
102
|
+
options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
|
103
|
+
end
|
104
104
|
|
105
105
|
if options[:exact] || options[:regex]
|
106
|
-
search = options[:search].join(
|
106
|
+
search = options[:search].join(" ")
|
107
107
|
else
|
108
108
|
# This regex matches the following:
|
109
109
|
# @tag(value)
|
@@ -113,74 +113,74 @@ class App
|
|
113
113
|
rx = [
|
114
114
|
'(?<=\A|[ ,])(?<req>[+!-])?@(?<tag>[^ *=<>$~\^,@(]+)',
|
115
115
|
'(?:\((?<value>.*?)\)| *(?<op>=~|[=<>~]{1,2}|[*$\^]=) *',
|
116
|
-
'(?<val>.*?(?=\Z|[,@])))?'
|
117
|
-
].join(
|
116
|
+
'(?<val>.*?(?=\Z|[,@])))?',
|
117
|
+
].join("")
|
118
118
|
# convert tag(value) to tag=value
|
119
|
-
search = options[:search].join(
|
119
|
+
search = options[:search].join(" ").gsub(Regexp.new(rx)) do
|
120
120
|
m = Regexp.last_match
|
121
|
-
string = if m[
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
options[:tagged] << string.sub(/@/,
|
127
|
-
|
121
|
+
string = if m["value"]
|
122
|
+
"#{m["req"]}#{m["tag"]}=#{m["value"]}"
|
123
|
+
else
|
124
|
+
m[0]
|
125
|
+
end
|
126
|
+
options[:tagged] << string.sub(/@/, "")
|
127
|
+
""
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
-
search = search.gsub(/,/,
|
131
|
+
search = search.gsub(/,/, "").gsub(/ +/, " ") unless search.nil?
|
132
132
|
|
133
133
|
if options[:priority].count.positive?
|
134
|
-
prios = options[:priority].join(
|
134
|
+
prios = options[:priority].join(",").split(/,/)
|
135
135
|
options[:or] = true if prios.count > 1
|
136
136
|
prios.map! do |p|
|
137
137
|
p.sub(/([hml])$/) do
|
138
138
|
case Regexp.last_match[1]
|
139
|
-
when
|
140
|
-
NA.priority_map[
|
141
|
-
when
|
142
|
-
NA.priority_map[
|
143
|
-
when
|
144
|
-
NA.priority_map[
|
139
|
+
when "h"
|
140
|
+
NA.priority_map["h"]
|
141
|
+
when "m"
|
142
|
+
NA.priority_map["m"]
|
143
|
+
when "l"
|
144
|
+
NA.priority_map["l"]
|
145
145
|
end
|
146
146
|
end
|
147
147
|
end
|
148
148
|
prios.each do |p|
|
149
149
|
options[:tagged] << if p =~ /^[<>=]{1,2}/
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
150
|
+
"priority#{p}"
|
151
|
+
else
|
152
|
+
"priority=#{p}"
|
153
|
+
end
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
|
-
all_req = options[:tagged].join(
|
157
|
+
all_req = options[:tagged].join(" ") !~ /(?<=[, ])[+!-]/ && !options[:or]
|
158
158
|
tags = []
|
159
|
-
options[:tagged].join(
|
159
|
+
options[:tagged].join(",").split(/ *, */).each do |arg|
|
160
160
|
m = arg.match(/^(?<req>[+!-])?(?<tag>[^ =<>$~\^]+?) *(?:(?<op>[=<>~]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
|
161
161
|
|
162
162
|
tags.push({
|
163
|
-
tag: m[
|
164
|
-
comp: m[
|
165
|
-
value: m[
|
166
|
-
required: all_req || (!m[
|
167
|
-
negate: !m[
|
163
|
+
tag: m["tag"].wildcard_to_rx,
|
164
|
+
comp: m["op"],
|
165
|
+
value: m["val"],
|
166
|
+
required: all_req || (!m["req"].nil? && m["req"] == "+"),
|
167
|
+
negate: !m["req"].nil? && m["req"] =~ /[!-]/ ? true : false,
|
168
168
|
})
|
169
169
|
end
|
170
170
|
|
171
171
|
args.concat(options[:in])
|
172
|
-
args <<
|
172
|
+
args << "*" if options[:all]
|
173
173
|
if args.count.positive?
|
174
|
-
all_req = args.join(
|
174
|
+
all_req = args.join(" ") !~ /(?<=[, ])[+!-]/
|
175
175
|
|
176
176
|
tokens = []
|
177
177
|
args.each do |arg|
|
178
178
|
arg.split(/ *, */).each do |a|
|
179
179
|
m = a.match(/^(?<req>[+!-])?(?<tok>.*?)$/)
|
180
180
|
tokens.push({
|
181
|
-
token: m[
|
182
|
-
required: !m[
|
183
|
-
negate: !m[
|
181
|
+
token: m["tok"],
|
182
|
+
required: !m["req"].nil? && m["req"] == "+",
|
183
|
+
negate: !m["req"].nil? && m["req"] =~ /[!-]/ ? true : false,
|
184
184
|
})
|
185
185
|
end
|
186
186
|
end
|
@@ -200,9 +200,9 @@ class App
|
|
200
200
|
search.split(/ /).each do |arg|
|
201
201
|
m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
|
202
202
|
search_tokens.push({
|
203
|
-
token: m[
|
204
|
-
required: all_req || (!m[
|
205
|
-
negate: !m[
|
203
|
+
token: m["tok"],
|
204
|
+
required: all_req || (!m["req"].nil? && m["req"] == "+"),
|
205
|
+
negate: !m["req"].nil? && m["req"] =~ /[!-]/ ? true : false,
|
206
206
|
})
|
207
207
|
end
|
208
208
|
end
|
@@ -211,7 +211,7 @@ class App
|
|
211
211
|
require_na = true
|
212
212
|
|
213
213
|
tag = [{ tag: NA.na_tag, value: nil, required: true, negate: false }]
|
214
|
-
tag << { tag:
|
214
|
+
tag << { tag: "done", value: nil, negate: true } unless options[:done]
|
215
215
|
tag.concat(tags)
|
216
216
|
|
217
217
|
file_path = options[:file] ? File.expand_path(options[:file]) : nil
|
@@ -231,11 +231,11 @@ class App
|
|
231
231
|
end
|
232
232
|
NA::Pager.paginate = false if options[:omnifocus]
|
233
233
|
todo.actions.output(depth,
|
234
|
-
files: todo.files,
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
234
|
+
{ files: todo.files,
|
235
|
+
nest: options[:nest],
|
236
|
+
nest_projects: options[:omnifocus],
|
237
|
+
notes: options[:notes],
|
238
|
+
no_files: options[:no_file] })
|
239
239
|
end
|
240
240
|
end
|
241
241
|
end
|
data/bin/commands/tagged.rb
CHANGED
@@ -2,121 +2,124 @@
|
|
2
2
|
|
3
3
|
class App
|
4
4
|
extend GLI::App
|
5
|
-
desc
|
5
|
+
desc "Find actions matching a tag"
|
6
6
|
long_desc 'Finds actions with tags matching the arguments. An action is shown if it
|
7
7
|
contains all of the tags listed. Add a + before a tag to make it required
|
8
8
|
and others optional. You can specify values using TAG=VALUE pairs.
|
9
9
|
Use <, >, and = for numeric comparisons, and *=, ^=, $=, or =~ (regex) for text comparisons.
|
10
10
|
Date comparisons use natural language (`na tagged "due<=today"`) and
|
11
11
|
are detected automatically.'
|
12
|
-
arg_name
|
12
|
+
arg_name "TAG[=VALUE]"
|
13
13
|
command %i[tagged] do |c|
|
14
|
-
c.example
|
15
|
-
c.example 'na tagged -d 3 "feature, idea"', desc:
|
16
|
-
c.example 'na tagged --or "feature, idea"', desc:
|
17
|
-
c.example 'na tagged "priority>=4"', desc:
|
18
|
-
c.example 'na tagged "due<in 2 days"', desc:
|
19
|
-
|
20
|
-
c.desc
|
21
|
-
c.arg_name
|
14
|
+
c.example "na tagged maybe", desc: "Show all actions tagged @maybe"
|
15
|
+
c.example 'na tagged -d 3 "feature, idea"', desc: "Show all actions tagged @feature AND @idea, recurse 3 levels"
|
16
|
+
c.example 'na tagged --or "feature, idea"', desc: "Show all actions tagged @feature OR @idea"
|
17
|
+
c.example 'na tagged "priority>=4"', desc: "Show actions with @priority(4) or @priority(5)"
|
18
|
+
c.example 'na tagged "due<in 2 days"', desc: "Show actions with a due date coming up in the next 2 days"
|
19
|
+
|
20
|
+
c.desc "Recurse to depth"
|
21
|
+
c.arg_name "DEPTH"
|
22
22
|
c.default_value 1
|
23
23
|
c.flag %i[d depth], type: :integer, must_match: /^\d+$/
|
24
24
|
|
25
|
-
c.desc
|
26
|
-
c.arg_name
|
25
|
+
c.desc "Show actions from a specific todo file in history. May use wildcards (* and ?)"
|
26
|
+
c.arg_name "TODO_PATH"
|
27
27
|
c.flag %i[in]
|
28
28
|
|
29
|
-
c.desc
|
29
|
+
c.desc "Include notes in output"
|
30
30
|
c.switch %i[notes], negatable: true, default_value: false
|
31
31
|
|
32
|
-
c.desc
|
32
|
+
c.desc "Combine tags with OR, displaying actions matching ANY of the tags"
|
33
33
|
c.switch %i[o or], negatable: false
|
34
34
|
|
35
|
-
c.desc
|
36
|
-
c.arg_name
|
35
|
+
c.desc "Show actions from a specific project"
|
36
|
+
c.arg_name "PROJECT[/SUBPROJECT]"
|
37
37
|
c.flag %i[proj project]
|
38
38
|
|
39
|
-
c.desc
|
40
|
-
c.arg_name
|
39
|
+
c.desc "Filter results using search terms"
|
40
|
+
c.arg_name "QUERY"
|
41
41
|
c.flag %i[search find grep], multiple: true
|
42
42
|
|
43
|
-
c.desc
|
43
|
+
c.desc "Include notes in search"
|
44
44
|
c.switch %i[search_notes], negatable: true, default_value: true
|
45
45
|
|
46
|
-
c.desc
|
46
|
+
c.desc "Search query is regular expression"
|
47
47
|
c.switch %i[regex], negatable: false
|
48
48
|
|
49
|
-
c.desc
|
49
|
+
c.desc "Search query is exact text match (not tokens)"
|
50
50
|
c.switch %i[exact], negatable: false
|
51
51
|
|
52
|
-
c.desc
|
52
|
+
c.desc "Include @done actions"
|
53
53
|
c.switch %i[done]
|
54
54
|
|
55
|
-
c.desc
|
55
|
+
c.desc "Show actions not matching tags"
|
56
56
|
c.switch %i[v invert], negatable: false
|
57
57
|
|
58
|
-
c.desc
|
59
|
-
c.arg_name
|
58
|
+
c.desc "Save this search for future use"
|
59
|
+
c.arg_name "TITLE"
|
60
60
|
c.flag %i[save]
|
61
61
|
|
62
|
-
c.desc
|
62
|
+
c.desc "Output actions nested by file"
|
63
63
|
c.switch %i[nest], negatable: false
|
64
64
|
|
65
|
-
c.desc
|
65
|
+
c.desc "No filename in output"
|
66
|
+
c.switch %i[no_file], negatable: false
|
67
|
+
|
68
|
+
c.desc "Output actions nested by file and project"
|
66
69
|
c.switch %i[omnifocus], negatable: false
|
67
70
|
|
68
71
|
c.action do |global_options, options, args|
|
69
72
|
options[:nest] = true if options[:omnifocus]
|
70
73
|
|
71
74
|
if options[:save]
|
72
|
-
title = options[:save].gsub(/[^a-z0-9]/,
|
73
|
-
cmd = NA.command_line.join(
|
75
|
+
title = options[:save].gsub(/[^a-z0-9]/, "_").gsub(/_+/, "_")
|
76
|
+
cmd = NA.command_line.join(" ").sub(/ --save[= ]*\S+/, "").split(" ").map { |t| %("#{t}") }.join(" ")
|
74
77
|
NA.save_search(title, cmd)
|
75
78
|
end
|
76
79
|
|
77
80
|
depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
81
|
+
3
|
82
|
+
else
|
83
|
+
options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
|
84
|
+
end
|
82
85
|
|
83
86
|
tags = []
|
84
87
|
|
85
|
-
all_req = args.join(
|
86
|
-
args.join(
|
88
|
+
all_req = args.join(" ") !~ /(?<=[, ])[+!-]/ && !options[:or]
|
89
|
+
args.join(",").split(/ *, */).each do |arg|
|
87
90
|
m = arg.match(/^(?<req>[+!-])?(?<tag>[^ =<>$~\^]+?) *(?:(?<op>[=<>~]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
|
88
91
|
next if m.nil?
|
89
92
|
|
90
93
|
tags.push({
|
91
|
-
tag: m[
|
92
|
-
comp: m[
|
93
|
-
value: m[
|
94
|
-
required: all_req || (!m[
|
95
|
-
negate: !m[
|
94
|
+
tag: m["tag"].sub(/^@/, "").wildcard_to_rx,
|
95
|
+
comp: m["op"],
|
96
|
+
value: m["val"],
|
97
|
+
required: all_req || (!m["req"].nil? && m["req"] == "+"),
|
98
|
+
negate: !m["req"].nil? && m["req"] =~ /[!-]/,
|
96
99
|
})
|
97
100
|
end
|
98
101
|
|
99
102
|
search_for_done = false
|
100
103
|
tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
|
101
|
-
tags.push({ tag:
|
104
|
+
tags.push({ tag: "done", value: nil, negate: true }) unless search_for_done || options[:done]
|
102
105
|
options[:done] = true if search_for_done
|
103
106
|
|
104
107
|
tokens = nil
|
105
108
|
if options[:search]
|
106
109
|
if options[:exact]
|
107
|
-
tokens = options[:search].join(
|
110
|
+
tokens = options[:search].join(" ")
|
108
111
|
elsif options[:regex]
|
109
|
-
tokens = Regexp.new(options[:search].join(
|
112
|
+
tokens = Regexp.new(options[:search].join(" "), Regexp::IGNORECASE)
|
110
113
|
else
|
111
114
|
tokens = []
|
112
|
-
all_req = options[:search].join(
|
115
|
+
all_req = options[:search].join(" ") !~ /(?<=[, ])[+!-]/ && !options[:or]
|
113
116
|
|
114
|
-
options[:search].join(
|
117
|
+
options[:search].join(" ").split(/ /).each do |arg|
|
115
118
|
m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
|
116
119
|
tokens.push({
|
117
|
-
token: m[
|
118
|
-
required: all_req || (!m[
|
119
|
-
negate: !m[
|
120
|
+
token: m["tok"],
|
121
|
+
required: all_req || (!m["req"].nil? && m["req"] == "+"),
|
122
|
+
negate: !m["req"].nil? && m["req"] =~ /[!-]/,
|
120
123
|
})
|
121
124
|
end
|
122
125
|
end
|
@@ -129,9 +132,9 @@ class App
|
|
129
132
|
options[:in].split(/ *, */).each do |a|
|
130
133
|
m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
|
131
134
|
todos.push({
|
132
|
-
token: m[
|
133
|
-
required: all_req || (!m[
|
134
|
-
negate: !m[
|
135
|
+
token: m["tok"],
|
136
|
+
required: all_req || (!m["req"].nil? && m["req"] == "+"),
|
137
|
+
negate: !m["req"].nil? && m["req"] =~ /[!-]/,
|
135
138
|
})
|
136
139
|
end
|
137
140
|
end
|
@@ -149,16 +152,17 @@ class App
|
|
149
152
|
require_na: false })
|
150
153
|
|
151
154
|
regexes = if tokens.is_a?(Array)
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
155
|
+
tokens.delete_if { |token| token[:negate] }.map { |token| token[:token] }
|
156
|
+
else
|
157
|
+
[tokens]
|
158
|
+
end
|
156
159
|
todo.actions.output(depth,
|
157
|
-
files: todo.files,
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
160
|
+
{ files: todo.files,
|
161
|
+
regexes: regexes,
|
162
|
+
notes: options[:notes],
|
163
|
+
nest: options[:nest],
|
164
|
+
nest_projects: options[:omnifocus],
|
165
|
+
no_files: options[:no_files] })
|
162
166
|
end
|
163
167
|
end
|
164
168
|
end
|
data/bin/na
CHANGED
@@ -8,6 +8,25 @@ require 'na'
|
|
8
8
|
require 'fcntl'
|
9
9
|
require 'tempfile'
|
10
10
|
|
11
|
+
# Search for XDG compliant config first. Default to ~/.na.rc for compatibility
|
12
|
+
def self.find_config_file
|
13
|
+
home = ENV['HOME']
|
14
|
+
xdg_config_home = ENV['XDG_CONFIG_HOME'] || File.join(home, '.config')
|
15
|
+
|
16
|
+
rc_paths = [
|
17
|
+
File.join(xdg_config_home, 'na', 'na.rc'), # Check $XDG_CONFIG_HOME/na/na.rc first
|
18
|
+
File.join(xdg_config_home, 'na.rc'), # Then check $XDG_CONFIG_HOME/na.rc
|
19
|
+
File.join(home, '.na.rc') # Finally check ~/.na.rc for compatibility
|
20
|
+
]
|
21
|
+
|
22
|
+
# Return the first path that exists
|
23
|
+
existing_path = rc_paths.find { |path| File.exist?(path) }
|
24
|
+
|
25
|
+
# If none exist, return XDG-compliant path for creation
|
26
|
+
existing_path || File.join(xdg_config_home, 'na', 'na.rc')
|
27
|
+
end
|
28
|
+
|
29
|
+
|
11
30
|
# Main application
|
12
31
|
class App
|
13
32
|
extend GLI::App
|
@@ -56,8 +75,11 @@ class App
|
|
56
75
|
arg_name 'PATH'
|
57
76
|
flag %i[f file]
|
58
77
|
|
78
|
+
desc 'Use a taskpaper file named after the git repository'
|
79
|
+
arg_name 'REPO'
|
80
|
+
switch %i[repo], negatable: true, default_value: true
|
81
|
+
|
59
82
|
desc 'Provide a template for new/blank todo files, use initconfig to make permanent'
|
60
|
-
arg_name 'PATH'
|
61
83
|
flag %[template]
|
62
84
|
|
63
85
|
desc 'Use current working directory as [p]roject, [t]ag, or [n]one'
|
@@ -101,6 +123,41 @@ class App
|
|
101
123
|
else
|
102
124
|
global[:cwd_as] =~ /^p/ ? :project : :tag
|
103
125
|
end
|
126
|
+
|
127
|
+
# start of git repo addition ==================================
|
128
|
+
# defaut to git repo if in a git managed directory
|
129
|
+
if global[:repo]
|
130
|
+
begin
|
131
|
+
require 'git'
|
132
|
+
|
133
|
+
# Check if we're in a git repo first
|
134
|
+
in_git_repo = system('git rev-parse --is-inside-work-tree >/dev/null 2>&1')
|
135
|
+
|
136
|
+
if in_git_repo
|
137
|
+
g = Git.open('.', log: Logger.new(File::NULL)) # Silence Git logs
|
138
|
+
repo_root = g.dir.path
|
139
|
+
repo_name = File.basename(repo_root)
|
140
|
+
taskpaper_file = File.join(repo_root, "#{repo_name}.#{NA.extension}")
|
141
|
+
NA.notify("Using repository taskpaper file: #{taskpaper_file}", debug: true)
|
142
|
+
NA.global_file = taskpaper_file
|
143
|
+
# Add this block to create the file if it doesn't exist
|
144
|
+
unless File.exist?(taskpaper_file)
|
145
|
+
res = NA.yn(NA::Color.template("#{NA.theme[:warning]}Repository file not found, create #{taskpaper_file}"), default: true)
|
146
|
+
if res
|
147
|
+
NA.create_todo(taskpaper_file, repo_name, template: global[:template])
|
148
|
+
else
|
149
|
+
NA.notify("#{NA.theme[:error]}Cancelled", exit_code: 1)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
else
|
153
|
+
NA.notify("#{NA.theme[:warning]}Not in a git repository, using default file location logic.", debug: true)
|
154
|
+
end
|
155
|
+
rescue LoadError
|
156
|
+
NA.notify("#{NA.theme[:error]}Git gem not installed. Run 'gem install git' to use --repo option.", exit_code: 1)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
# end of git repo addition ====================================
|
160
|
+
|
104
161
|
NA.weed_cache_file
|
105
162
|
NA.notify("{dw}{ globals: #{NA.globals}, command_line: #{NA.command_line}, command: #{NA.command}}", debug: true)
|
106
163
|
true
|
data/docker/Dockerfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
FROM ruby:3.1.3-slim
|
2
|
+
RUN mkdir /na
|
3
|
+
WORKDIR /na
|
4
|
+
RUN gem install bundler:2.2
|
5
|
+
COPY ./docker/sources.list /etc/apt/sources.list
|
6
|
+
RUN apt-get update -y --allow-insecure-repositories || true
|
7
|
+
RUN apt-get install -y less vim
|
8
|
+
COPY ./docker/inputrc /root/.inputrc
|
9
|
+
COPY ./docker/bash_profile /root/.bash_profile
|
10
|
+
CMD ["/na/scripts/runtests.sh"]
|
@@ -0,0 +1,11 @@
|
|
1
|
+
FROM ruby:2.6
|
2
|
+
RUN mkdir /na
|
3
|
+
WORKDIR /na
|
4
|
+
RUN gem install bundler:2.2
|
5
|
+
COPY ./docker/sources.list /etc/apt/sources.list
|
6
|
+
RUN apt-get update -y --allow-insecure-repositories || true
|
7
|
+
RUN apt-get install -y sudo || true
|
8
|
+
RUN sudo apt-get install -y less vim || true
|
9
|
+
COPY ./docker/inputrc /root/.inputrc
|
10
|
+
COPY ./docker/bash_profile /root/.bash_profile
|
11
|
+
CMD ["/na/scripts/runtests.sh"]
|
@@ -0,0 +1,11 @@
|
|
1
|
+
FROM ruby:2.7
|
2
|
+
RUN mkdir /na
|
3
|
+
WORKDIR /na
|
4
|
+
RUN gem install bundler:2.2
|
5
|
+
COPY ./docker/sources.list /etc/apt/sources.list
|
6
|
+
RUN apt-get update -y --allow-insecure-repositories || true
|
7
|
+
RUN apt-get install -y sudo || true
|
8
|
+
RUN sudo apt-get install -y less vim || true
|
9
|
+
COPY ./docker/inputrc /root/.inputrc
|
10
|
+
COPY ./docker/bash_profile /root/.bash_profile
|
11
|
+
CMD ["/na/scripts/runtests.sh"]
|
@@ -0,0 +1,11 @@
|
|
1
|
+
FROM ruby:3.0.0
|
2
|
+
RUN mkdir /na
|
3
|
+
WORKDIR /na
|
4
|
+
RUN gem install bundler:2.2
|
5
|
+
COPY ./docker/sources.list /etc/apt/sources.list
|
6
|
+
RUN apt-get update -y --allow-insecure-repositories || true
|
7
|
+
RUN apt-get install -y sudo || true
|
8
|
+
RUN sudo apt-get install -y less vim || true
|
9
|
+
COPY ./docker/inputrc /root/.inputrc
|
10
|
+
COPY ./docker/bash_profile /root/.bash_profile
|
11
|
+
CMD ["/na/scripts/runtests.sh"]
|