na 1.1.9 → 1.1.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -2
- data/bin/na +30 -25
- data/lib/na/next_action.rb +66 -35
- data/lib/na/string.rb +11 -0
- data/lib/na/version.rb +1 -1
- data/src/README.md +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b309ba145cf0859f1a9011cf62268261766338d79cb956810fab9c64863c37f2
|
4
|
+
data.tar.gz: 6fc7e065d4d85bf70b9122634414274e6a44d640ffffeeee7409e9102115f11d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63ee0b1ac9370533379956e8cd5f6571c8d39731c12c02404b2912d5cb97f49d32c988e4d23c12c50d1b2080412296bc58351680122abd26281580c49d93f722
|
7
|
+
data.tar.gz: b7500123c7ea092acc42ccba0873942fe871bd201405e3ef2faa66f3d155c3d0ac60b530078d962114a4d0d66bdb8ea427b1452873c14e2b06419cb2675e822e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
### 1.1.11
|
2
|
+
|
3
|
+
2022-10-05 08:56
|
4
|
+
|
5
|
+
#### IMPROVED
|
6
|
+
|
7
|
+
- Respect na_tag setting when creating new todo file
|
8
|
+
- Code cleanup
|
9
|
+
|
10
|
+
### 1.1.10
|
11
|
+
|
12
|
+
2022-10-05 08:19
|
13
|
+
|
14
|
+
#### FIXED
|
15
|
+
|
16
|
+
- When adding a project, don't use Ruby #capitalize, which downcases the rest of the project name
|
17
|
+
|
1
18
|
### 1.1.9
|
2
19
|
|
3
20
|
2022-10-03 12:08
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
_If you're one of the rare people like me who find this useful, feel free to
|
10
10
|
[buy me some coffee][donate]._
|
11
11
|
|
12
|
-
The current version of `na` is 1.1.
|
12
|
+
The current version of `na` is 1.1.11
|
13
13
|
.
|
14
14
|
|
15
15
|
`na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
|
@@ -37,7 +37,7 @@ You can list next actions in files in the current directory by typing `na`. By d
|
|
37
37
|
|
38
38
|
#### Easy matching
|
39
39
|
|
40
|
-
`na` features intelligent project matching. Every time it locates a todo file, it adds the project to the database. Once a project is recorded, you can list its actions by using any portion of the parent directories or file names. If your project is in `~/Sites/dev/markedapp`, you could quickly list its next actions by typing `na dev mark`. It will always look for the shortest match.
|
40
|
+
`na` features intelligent project matching. Every time it locates a todo file, it adds the project to the database. Once a project is recorded, you can list its actions by using any portion of the parent directories or file names. If your project is in `~/Sites/dev/markedapp`, you could quickly list its next actions by typing `na next dev mark`. It will always look for the shortest match.
|
41
41
|
|
42
42
|
#### Recursion
|
43
43
|
|
data/bin/na
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
3
5
|
require 'gli'
|
4
6
|
require 'na'
|
5
7
|
|
8
|
+
# Main application
|
6
9
|
class App
|
7
10
|
extend GLI::App
|
8
11
|
|
@@ -123,7 +126,7 @@ class App
|
|
123
126
|
|
124
127
|
c.desc 'Add action to specific project'
|
125
128
|
c.default_value 'Inbox'
|
126
|
-
c.flag %[to]
|
129
|
+
c.flag %i[to]
|
127
130
|
|
128
131
|
c.desc 'Use a tag other than the default next action tag'
|
129
132
|
c.arg_name 'TAG'
|
@@ -136,7 +139,7 @@ class App
|
|
136
139
|
c.arg_name 'PATH'
|
137
140
|
c.flag %i[f file]
|
138
141
|
|
139
|
-
c.action do |
|
142
|
+
c.action do |_global_options, options, args|
|
140
143
|
reader = TTY::Reader.new
|
141
144
|
action = if args.count.positive?
|
142
145
|
args.join(' ').strip
|
@@ -168,7 +171,10 @@ class App
|
|
168
171
|
|
169
172
|
note = if options[:note]
|
170
173
|
if TTY::Which.exist?('gum')
|
171
|
-
|
174
|
+
args = ['--placeholder "Enter a note, CTRL-d to save"']
|
175
|
+
args << '--char-limit 0'
|
176
|
+
args << '--width $(tput cols)'
|
177
|
+
`gum write #{args.join(' ')}`.strip.split("\n")
|
172
178
|
else
|
173
179
|
puts NA::Color.template('{bm}Enter a note, {bw}CTRL-d{bm} to end editing{bw}')
|
174
180
|
reader.read_multiline
|
@@ -190,7 +196,7 @@ class App
|
|
190
196
|
end
|
191
197
|
else
|
192
198
|
files = NA.find_files(depth: 1)
|
193
|
-
if files.count
|
199
|
+
if files.count.zero?
|
194
200
|
print NA::Color.template('{by}No todo file found, create one? {w}(y/{g}N{w}){x} ')
|
195
201
|
res = reader.read_char
|
196
202
|
if res =~ /y/i
|
@@ -256,10 +262,10 @@ class App
|
|
256
262
|
end
|
257
263
|
end
|
258
264
|
|
259
|
-
|
260
265
|
desc 'Find actions matching a tag'
|
261
266
|
long_desc 'Finds actions with tags matching the arguments. An action is shown if it
|
262
|
-
|
267
|
+
contains any of the tags listed. Add a + before a tag to make it required.
|
268
|
+
You can specify values using TAG=VALUE pairs.'
|
263
269
|
arg_name 'TAG [VALUE]'
|
264
270
|
command %i[tagged] do |c|
|
265
271
|
c.example 'na tagged +maybe', desc: 'Show all actions tagged @maybe'
|
@@ -304,7 +310,7 @@ class App
|
|
304
310
|
c.example 'na init', desc: 'Generate a new todo file, prompting for project name'
|
305
311
|
c.example 'na init warpspeed', desc: 'Generate a new todo for a project called warpspeed'
|
306
312
|
|
307
|
-
c.action do |
|
313
|
+
c.action do |_global_options, _options, args|
|
308
314
|
reader = TTY::Reader.new
|
309
315
|
if args.count.positive?
|
310
316
|
project = args.join(' ')
|
@@ -346,7 +352,7 @@ class App
|
|
346
352
|
c.arg_name 'EDITOR'
|
347
353
|
c.flag %i[a app]
|
348
354
|
|
349
|
-
c.action do |global_options, options,
|
355
|
+
c.action do |global_options, options, _args|
|
350
356
|
depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
|
351
357
|
3
|
352
358
|
else
|
@@ -371,15 +377,16 @@ class App
|
|
371
377
|
long_desc 'Installing the prompt hook allows you to automatically
|
372
378
|
list next actions when you cd into a directory'
|
373
379
|
command %i[prompt] do |c|
|
374
|
-
c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to
|
380
|
+
c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to
|
381
|
+
specify a shell (zsh, bash, fish)'
|
375
382
|
c.arg_name '[SHELL]'
|
376
383
|
c.command %i[show] do |s|
|
377
|
-
s.action do |
|
378
|
-
if args.count.positive?
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
384
|
+
s.action do |_global_options, _options, args|
|
385
|
+
shell = if args.count.positive?
|
386
|
+
args[0]
|
387
|
+
else
|
388
|
+
File.basename(ENV['SHELL'])
|
389
|
+
end
|
383
390
|
|
384
391
|
case shell
|
385
392
|
when /^f/i
|
@@ -395,12 +402,12 @@ class App
|
|
395
402
|
c.desc 'Install the hook for the current shell to the appropriate startup file.'
|
396
403
|
c.arg_name '[SHELL]'
|
397
404
|
c.command %i[install] do |s|
|
398
|
-
s.action do |
|
399
|
-
if args.count.positive?
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
405
|
+
s.action do |_global_options, _options, args|
|
406
|
+
shell = if args.count.positive?
|
407
|
+
args[0]
|
408
|
+
else
|
409
|
+
File.basename(ENV['SHELL'])
|
410
|
+
end
|
404
411
|
|
405
412
|
case shell
|
406
413
|
when /^f/i
|
@@ -414,7 +421,7 @@ class App
|
|
414
421
|
end
|
415
422
|
end
|
416
423
|
|
417
|
-
pre do |global,
|
424
|
+
pre do |global, _command, _options, _args|
|
418
425
|
NA.verbose = global[:debug]
|
419
426
|
NA.extension = global[:ext]
|
420
427
|
NA.na_tag = global[:na_tag]
|
@@ -432,9 +439,7 @@ class App
|
|
432
439
|
case exception
|
433
440
|
when GLI::UnknownCommand
|
434
441
|
cmd = ['add']
|
435
|
-
if ARGV.count.positive?
|
436
|
-
cmd.concat(ARGV.unshift($first_arg))
|
437
|
-
end
|
442
|
+
cmd.concat(ARGV.unshift($first_arg)) if ARGV.count.positive?
|
438
443
|
|
439
444
|
exit run(cmd)
|
440
445
|
when SystemExit
|
data/lib/na/next_action.rb
CHANGED
@@ -5,10 +5,17 @@ module NA
|
|
5
5
|
class << self
|
6
6
|
attr_accessor :verbose, :extension, :na_tag
|
7
7
|
|
8
|
+
def notify(msg, exit_code: false)
|
9
|
+
$stderr.puts NA::Color.template("{x}#{msg}{x}")
|
10
|
+
if exit_code && exit_code.is_a?(Number)
|
11
|
+
Process.exit exit_code
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
8
15
|
def create_todo(target, basename)
|
9
16
|
File.open(target, 'w') do |f|
|
10
17
|
content = <<~ENDCONTENT
|
11
|
-
Inbox:
|
18
|
+
Inbox:
|
12
19
|
#{basename}:
|
13
20
|
\tFeature Requests:
|
14
21
|
\tIdeas:
|
@@ -18,11 +25,11 @@ module NA
|
|
18
25
|
\tTop Priority @search(@priority = 5 and not @done)
|
19
26
|
\tHigh Priority @search(@priority > 3 and not @done)
|
20
27
|
\tMaybe @search(@maybe)
|
21
|
-
\tNext @search(
|
28
|
+
\tNext @search(@#{NA.na_tag} and not @done and not project = \"Archive\")
|
22
29
|
ENDCONTENT
|
23
30
|
f.puts(content)
|
24
31
|
end
|
25
|
-
|
32
|
+
notify("{y}Created {bw}#{target}")
|
26
33
|
end
|
27
34
|
|
28
35
|
def find_files(depth: 1)
|
@@ -41,8 +48,7 @@ module NA
|
|
41
48
|
elsif TTY::Which.exist?('fzf')
|
42
49
|
res = choose_from(files, prompt: 'Use which file?')
|
43
50
|
unless res
|
44
|
-
|
45
|
-
Process.exit 1
|
51
|
+
notify('{r}No file selected, cancelled', exit_code: 1)
|
46
52
|
end
|
47
53
|
|
48
54
|
res.strip
|
@@ -60,18 +66,18 @@ module NA
|
|
60
66
|
def add_action(file, project, action, note = nil)
|
61
67
|
content = IO.read(file)
|
62
68
|
unless content =~ /^[ \t]*#{project}:/i
|
63
|
-
content = "#{project.
|
69
|
+
content = "#{project.cap_first}:\n#{content}"
|
64
70
|
end
|
65
71
|
|
66
72
|
content.sub!(/^([ \t]*)#{project}:(.*?)$/i) do
|
67
73
|
m = Regexp.last_match
|
68
74
|
note = note.nil? ? '' : "\n#{m[1]}\t\t#{note.join('').strip}"
|
69
|
-
"#{m[1]}#{project.
|
75
|
+
"#{m[1]}#{project.cap_first}:#{m[2]}\n#{m[1]}\t- #{action}#{note}"
|
70
76
|
end
|
71
77
|
|
72
78
|
File.open(file, 'w') { |f| f.puts content }
|
73
79
|
|
74
|
-
|
80
|
+
notify("{by}Task added to {bw}#{file}")
|
75
81
|
end
|
76
82
|
|
77
83
|
def output_actions(actions, depth, files: nil)
|
@@ -91,7 +97,7 @@ module NA
|
|
91
97
|
'%parent%action'
|
92
98
|
end
|
93
99
|
if files && @verbose
|
94
|
-
|
100
|
+
files.map { |f| notify("{dw}#{f}") }
|
95
101
|
end
|
96
102
|
|
97
103
|
puts actions.map { |action| action.pretty(template: { output: template }) }
|
@@ -188,11 +194,7 @@ module NA
|
|
188
194
|
def choose_from(options, prompt: 'Make a selection: ', multiple: false, sorted: true, fzf_args: [])
|
189
195
|
return nil unless $stdout.isatty
|
190
196
|
|
191
|
-
|
192
|
-
default_args = []
|
193
|
-
default_args << %(--prompt="#{prompt}")
|
194
|
-
default_args << "--height=#{options.count + 2}"
|
195
|
-
default_args << '--info=inline'
|
197
|
+
default_args = [%(--prompt="#{prompt}"), "--height=#{options.count + 2}", '--info=inline']
|
196
198
|
default_args << '--multi' if multiple
|
197
199
|
header = "esc: cancel,#{multiple ? ' tab: multi-select, ctrl-a: select all,' : ''} return: confirm"
|
198
200
|
default_args << %(--header="#{header}")
|
@@ -205,33 +207,50 @@ module NA
|
|
205
207
|
res
|
206
208
|
end
|
207
209
|
|
210
|
+
##
|
211
|
+
## Get path to database of known todo files
|
212
|
+
##
|
213
|
+
## @return [String] File path
|
214
|
+
##
|
208
215
|
def database_path
|
209
216
|
db_dir = File.expand_path('~/.local/share/na')
|
217
|
+
# Create directory if needed
|
210
218
|
FileUtils.mkdir_p(db_dir) unless File.directory?(db_dir)
|
211
219
|
db_file = 'tdlist.txt'
|
212
220
|
File.join(db_dir, db_file)
|
213
221
|
end
|
214
222
|
|
215
|
-
|
223
|
+
##
|
224
|
+
## Find a matching path using semi-fuzzy matching.
|
225
|
+
## Search tokens can include ! and + to negate or make
|
226
|
+
## required.
|
227
|
+
##
|
228
|
+
## @param search [Array] search tokens to match
|
229
|
+
## @param distance [Integer] allowed distance
|
230
|
+
## between characters
|
231
|
+
##
|
232
|
+
def match_working_dir(search, distance: 1)
|
216
233
|
optional = []
|
217
234
|
required = []
|
218
235
|
|
219
236
|
search&.each do |t|
|
220
|
-
|
237
|
+
# Make "search" into "s.{0,1}e.{0,1}a.{0,1}r.{0,1}c.{0,1}h"
|
238
|
+
new_rx = t[:token].to_s.split('').join(".{0,#{distance}}")
|
221
239
|
|
222
240
|
optional.push(new_rx)
|
223
241
|
required.push(new_rx) if t[:required]
|
224
242
|
end
|
225
243
|
|
244
|
+
match_dir(optional, required)
|
245
|
+
end
|
246
|
+
|
247
|
+
def match_dir(optional, required)
|
226
248
|
file = database_path
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
$stderr.puts NA::Color.template('{r}No na database found{x}')
|
233
|
-
Process.exit 1
|
234
|
-
end
|
249
|
+
notify('{r}No na database found', exit_code: 1) unless File.exist?(file)
|
250
|
+
|
251
|
+
dirs = IO.read(file).split("\n")
|
252
|
+
dirs.delete_if { |d| !d.matches(any: optional, all: required) }
|
253
|
+
dirs.sort.uniq
|
235
254
|
end
|
236
255
|
|
237
256
|
def save_working_dir(todo_file)
|
@@ -258,6 +277,26 @@ module NA
|
|
258
277
|
os_open(file, app: app) if file && File.exist?(file)
|
259
278
|
end
|
260
279
|
|
280
|
+
def darwin_open(file, app: nil)
|
281
|
+
if app
|
282
|
+
`open -a "#{app}" #{Shellwords.escape(file)}`
|
283
|
+
else
|
284
|
+
`open #{Shellwords.escape(file)}`
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def win_open(file)
|
289
|
+
`start #{Shellwords.escape(file)}`
|
290
|
+
end
|
291
|
+
|
292
|
+
def linux_open(file)
|
293
|
+
if TTY::Which.exist?('xdg-open')
|
294
|
+
`xdg-open #{Shellwords.escape(file)}`
|
295
|
+
else
|
296
|
+
notify('{r}Unable to determine executable for `xdg-open`.')
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
261
300
|
##
|
262
301
|
## Platform-agnostic open command
|
263
302
|
##
|
@@ -267,19 +306,11 @@ module NA
|
|
267
306
|
os = RbConfig::CONFIG['target_os']
|
268
307
|
case os
|
269
308
|
when /darwin.*/i
|
270
|
-
|
271
|
-
`open -a "#{app}" #{Shellwords.escape(file)}`
|
272
|
-
else
|
273
|
-
`open #{Shellwords.escape(file)}`
|
274
|
-
end
|
309
|
+
darwin_open(file, app: app)
|
275
310
|
when /mingw|mswin/i
|
276
|
-
|
311
|
+
win_open(file)
|
277
312
|
else
|
278
|
-
|
279
|
-
`xdg-open #{Shellwords.escape(file)}`
|
280
|
-
else
|
281
|
-
$stderr.puts NA::Color.template('{r}Unable to determine executable for `open`.{x}')
|
282
|
-
end
|
313
|
+
linux_open(file)
|
283
314
|
end
|
284
315
|
end
|
285
316
|
end
|
data/lib/na/string.rb
CHANGED
data/lib/na/version.rb
CHANGED
data/src/README.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
_If you're one of the rare people like me who find this useful, feel free to
|
10
10
|
[buy me some coffee][donate]._
|
11
11
|
|
12
|
-
The current version of `na` is <!--VER-->1.1.
|
12
|
+
The current version of `na` is <!--VER-->1.1.10<!--END VER-->.
|
13
13
|
|
14
14
|
`na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
|
15
15
|
|
@@ -36,7 +36,7 @@ You can list next actions in files in the current directory by typing `na`. By d
|
|
36
36
|
|
37
37
|
#### Easy matching
|
38
38
|
|
39
|
-
`na` features intelligent project matching. Every time it locates a todo file, it adds the project to the database. Once a project is recorded, you can list its actions by using any portion of the parent directories or file names. If your project is in `~/Sites/dev/markedapp`, you could quickly list its next actions by typing `na dev mark`. It will always look for the shortest match.
|
39
|
+
`na` features intelligent project matching. Every time it locates a todo file, it adds the project to the database. Once a project is recorded, you can list its actions by using any portion of the parent directories or file names. If your project is in `~/Sites/dev/markedapp`, you could quickly list its next actions by typing `na next dev mark`. It will always look for the shortest match.
|
40
40
|
|
41
41
|
#### Recursion
|
42
42
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: na
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|