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.
data/bin/commands/next.rb CHANGED
@@ -2,108 +2,108 @@
2
2
 
3
3
  class App
4
4
  extend GLI::App
5
- desc 'Show next actions'
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 'QUERY', optional: true
12
+ arg_name "QUERY", optional: true
13
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'
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 'Recurse to depth'
19
- c.arg_name 'DEPTH'
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 'Show next actions from all known todo files (in any directory)'
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 'Display matches from a known todo file anywhere in history (short name)'
26
- c.arg_name 'TODO'
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 'Display matches from specific todo file ([relative] path)'
30
- c.arg_name 'TODO_FILE'
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 'Alternate tag to search for'
34
- c.arg_name 'TAG'
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 'Show actions from a specific project'
38
- c.arg_name 'PROJECT[/SUBPROJECT]'
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 'Match actions containing tag. Allows value comparisons'
42
- c.arg_name 'TAG'
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 'Match actions with priority, allows <>= comparison'
46
- c.arg_name 'PRIORITY'
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 'Filter results using search terms'
50
- c.arg_name 'QUERY'
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 'Include notes in search'
53
+ c.desc "Include notes in search"
54
54
  c.switch %i[search_notes], negatable: true, default_value: true
55
55
 
56
- c.desc 'Search query is regular expression'
56
+ c.desc "Search query is regular expression"
57
57
  c.switch %i[regex], negatable: false
58
58
 
59
- c.desc 'Search query is exact text match (not tokens)'
59
+ c.desc "Search query is exact text match (not tokens)"
60
60
  c.switch %i[exact], negatable: false
61
61
 
62
- c.desc 'Include notes in output'
62
+ c.desc "Include notes in output"
63
63
  c.switch %i[notes], negatable: true, default_value: false
64
64
 
65
- c.desc 'Include @done actions'
65
+ c.desc "Include @done actions"
66
66
  c.switch %i[done]
67
67
 
68
- c.desc 'Output actions nested by file'
68
+ c.desc "Output actions nested by file"
69
69
  c.switch %i[nest], negatable: false
70
70
 
71
- c.desc 'No filename in output'
71
+ c.desc "No filename in output"
72
72
  c.switch %i[no_file], negatable: false
73
73
 
74
- c.desc 'Output actions nested by file and project'
74
+ c.desc "Output actions nested by file and project"
75
75
  c.switch %i[omnifocus], negatable: false
76
76
 
77
- c.desc 'Save this search for future use'
78
- c.arg_name 'TITLE'
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 = ['add']
85
- cmd.push('--note') if global_options[:note]
86
- cmd.concat(['--priority', global_options[:priority]]) if global_options[:priority]
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]/, '_').gsub(/_+/, '_')
94
- NA.save_search(title, "#{NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.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
- 3
101
- else
102
- options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
103
- end
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(' ').gsub(Regexp.new(rx)) do
119
+ search = options[:search].join(" ").gsub(Regexp.new(rx)) do
120
120
  m = Regexp.last_match
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
- ''
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(/,/, '').gsub(/ +/, ' ') unless search.nil?
131
+ search = search.gsub(/,/, "").gsub(/ +/, " ") unless search.nil?
132
132
 
133
133
  if options[:priority].count.positive?
134
- prios = options[:priority].join(',').split(/,/)
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 'h'
140
- NA.priority_map['h']
141
- when 'm'
142
- NA.priority_map['m']
143
- when 'l'
144
- NA.priority_map['l']
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
- "priority#{p}"
151
- else
152
- "priority=#{p}"
153
- end
150
+ "priority#{p}"
151
+ else
152
+ "priority=#{p}"
153
+ end
154
154
  end
155
155
  end
156
156
 
157
- all_req = options[:tagged].join(' ') !~ /(?<=[, ])[+!-]/ && !options[:or]
157
+ all_req = options[:tagged].join(" ") !~ /(?<=[, ])[+!-]/ && !options[:or]
158
158
  tags = []
159
- options[:tagged].join(',').split(/ *, */).each do |arg|
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['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
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 << '*' if options[:all]
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['tok'],
182
- required: !m['req'].nil? && m['req'] == '+',
183
- negate: !m['req'].nil? && m['req'] =~ /[!-]/ ? true : false
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['tok'],
204
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
205
- negate: !m['req'].nil? && m['req'] =~ /[!-]/ ? true : false
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: 'done', value: nil, negate: true } unless options[:done]
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
- nest: options[:nest],
236
- nest_projects: options[:omnifocus],
237
- notes: options[:notes],
238
- no_files: options[:no_file])
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
@@ -2,121 +2,124 @@
2
2
 
3
3
  class App
4
4
  extend GLI::App
5
- desc 'Find actions matching a tag'
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 'TAG[=VALUE]'
12
+ arg_name "TAG[=VALUE]"
13
13
  command %i[tagged] do |c|
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'
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 'Show actions from a specific todo file in history. May use wildcards (* and ?)'
26
- c.arg_name 'TODO_PATH'
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 'Include notes in output'
29
+ c.desc "Include notes in output"
30
30
  c.switch %i[notes], negatable: true, default_value: false
31
31
 
32
- c.desc 'Combine tags with OR, displaying actions matching ANY of the tags'
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 'Show actions from a specific project'
36
- c.arg_name 'PROJECT[/SUBPROJECT]'
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 'Filter results using search terms'
40
- c.arg_name 'QUERY'
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 'Include notes in search'
43
+ c.desc "Include notes in search"
44
44
  c.switch %i[search_notes], negatable: true, default_value: true
45
45
 
46
- c.desc 'Search query is regular expression'
46
+ c.desc "Search query is regular expression"
47
47
  c.switch %i[regex], negatable: false
48
48
 
49
- c.desc 'Search query is exact text match (not tokens)'
49
+ c.desc "Search query is exact text match (not tokens)"
50
50
  c.switch %i[exact], negatable: false
51
51
 
52
- c.desc 'Include @done actions'
52
+ c.desc "Include @done actions"
53
53
  c.switch %i[done]
54
54
 
55
- c.desc 'Show actions not matching tags'
55
+ c.desc "Show actions not matching tags"
56
56
  c.switch %i[v invert], negatable: false
57
57
 
58
- c.desc 'Save this search for future use'
59
- c.arg_name 'TITLE'
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 'Output actions nested by file'
62
+ c.desc "Output actions nested by file"
63
63
  c.switch %i[nest], negatable: false
64
64
 
65
- c.desc 'Output actions nested by file and project'
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]/, '_').gsub(/_+/, '_')
73
- cmd = NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.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
- 3
79
- else
80
- options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
81
- end
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(' ') !~ /(?<=[, ])[+!-]/ && !options[:or]
86
- args.join(',').split(/ *, */).each do |arg|
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['tag'].sub(/^@/, '').wildcard_to_rx,
92
- comp: m['op'],
93
- value: m['val'],
94
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
95
- negate: !m['req'].nil? && m['req'] =~ /[!-]/
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: 'done', value: nil, negate: true }) unless search_for_done || options[:done]
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(' '), Regexp::IGNORECASE)
112
+ tokens = Regexp.new(options[:search].join(" "), Regexp::IGNORECASE)
110
113
  else
111
114
  tokens = []
112
- all_req = options[:search].join(' ') !~ /(?<=[, ])[+!-]/ && !options[:or]
115
+ all_req = options[:search].join(" ") !~ /(?<=[, ])[+!-]/ && !options[:or]
113
116
 
114
- options[:search].join(' ').split(/ /).each do |arg|
117
+ options[:search].join(" ").split(/ /).each do |arg|
115
118
  m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
116
119
  tokens.push({
117
- token: m['tok'],
118
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
119
- negate: !m['req'].nil? && m['req'] =~ /[!-]/
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['tok'],
133
- required: all_req || (!m['req'].nil? && m['req'] == '+'),
134
- negate: !m['req'].nil? && m['req'] =~ /[!-]/
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
- tokens.delete_if { |token| token[:negate] }.map { |token| token[:token] }
153
- else
154
- [tokens]
155
- end
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
- regexes: regexes,
159
- notes: options[:notes],
160
- nest: options[:nest],
161
- nest_projects: options[:omnifocus])
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"]