na 1.0.5 → 1.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -2
- data/bin/na +74 -22
- data/lib/na/next_action.rb +20 -16
- data/lib/na/prompt.rb +61 -0
- data/lib/na/version.rb +1 -1
- data/lib/na.rb +1 -0
- data/src/README.md +1 -2
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9aab87ece3f35afbe5280d6c1b777c9d8dece4916f796eedb3dfebdbbeb5032e
|
4
|
+
data.tar.gz: 46b7eef3d84ede3a69f9497e4d0bb4fcc772fd159fbffe23f9749f3172bc2cdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05bda9033431ce005ad32fb9cd1c711642504972ffa73131d1c989b90dd4b50089b53d9e6746df4555d87e9b65d8b3c2c3707023dd8a8287f8596f4583a727f5
|
7
|
+
data.tar.gz: d474663a84ca7ef0287f05c3c2ed6fa63b0fb3d6482337b33343bbfa71a7a7d1c4fe542f91d9c75797c48a4f073079b85a96ef6704d2056b193b7f891596cbf9
|
data/CHANGELOG.md
CHANGED
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.0.
|
12
|
+
The current version of `na` is 1.0.6.
|
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
|
|
@@ -293,7 +293,6 @@ Fish (in ~/.config/fish/conf.d/*.fish):
|
|
293
293
|
|
294
294
|
```fish
|
295
295
|
function __should_na --on-variable PWD
|
296
|
-
# function __should_na --on-event fish_prompt
|
297
296
|
test -s (basename $PWD)".taskpaper" && na
|
298
297
|
end
|
299
298
|
```
|
data/bin/na
CHANGED
@@ -48,7 +48,7 @@ class App
|
|
48
48
|
flag %i[d depth], type: :integer, must_match: /^\d+$/
|
49
49
|
|
50
50
|
desc 'Display verbose output'
|
51
|
-
switch %i[
|
51
|
+
switch %i[debug]
|
52
52
|
|
53
53
|
desc 'Show next actions'
|
54
54
|
arg_name 'OPTIONAL_QUERY'
|
@@ -91,14 +91,12 @@ class App
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
tag = options[:tag] ==
|
94
|
+
tag = options[:tag] == NA.na_tag ? nil : options[:tag]
|
95
95
|
files, actions = NA.parse_actions(depth: depth,
|
96
96
|
query: tokens,
|
97
|
-
extension: global_options[:ext],
|
98
|
-
na_tag: global_options[:na_tag],
|
99
97
|
tag: tag)
|
100
98
|
|
101
|
-
NA.output_actions(actions, depth,
|
99
|
+
NA.output_actions(actions, depth, files: files)
|
102
100
|
end
|
103
101
|
end
|
104
102
|
|
@@ -124,6 +122,9 @@ class App
|
|
124
122
|
c.arg_name 'TAG'
|
125
123
|
c.flag %i[t tag]
|
126
124
|
|
125
|
+
c.desc 'Don\'t add next action tag to new entry'
|
126
|
+
c.switch %i[x]
|
127
|
+
|
127
128
|
c.desc 'Specify the file to which the task should be added'
|
128
129
|
c.arg_name 'PATH'
|
129
130
|
c.flag %i[f file]
|
@@ -148,10 +149,15 @@ class App
|
|
148
149
|
action = "#{action.gsub(/@priority\(\d+\)/, '')} @priority(#{options[:priority]})"
|
149
150
|
end
|
150
151
|
|
151
|
-
na_tag =
|
152
|
-
|
152
|
+
na_tag = NA.na_tag
|
153
|
+
if options[:x]
|
154
|
+
na_tag = ''
|
155
|
+
else
|
156
|
+
na_tag = options[:tag] unless options[:tag].nil?
|
157
|
+
na_tag = " @#{na_tag}"
|
158
|
+
end
|
153
159
|
|
154
|
-
action = "#{action.gsub(
|
160
|
+
action = "#{action.gsub(/#{na_tag}/, '')}#{na_tag}"
|
155
161
|
|
156
162
|
note = if options[:note]
|
157
163
|
if TTY::Which.exist?('gum')
|
@@ -167,7 +173,7 @@ class App
|
|
167
173
|
print NA::Color.template('{by}Specified file not found, create it? {w}(y/{g}N{w}){x} ')
|
168
174
|
res = reader.read_char
|
169
175
|
if res =~ /y/i
|
170
|
-
basename = File.basename(target, ".#{
|
176
|
+
basename = File.basename(target, ".#{NA.extension}")
|
171
177
|
NA.create_todo(target, basename)
|
172
178
|
else
|
173
179
|
puts NA::Color.template('{r}Cancelled{x}')
|
@@ -175,15 +181,15 @@ class App
|
|
175
181
|
end
|
176
182
|
end
|
177
183
|
else
|
178
|
-
files = NA.find_files(depth: 1
|
184
|
+
files = NA.find_files(depth: 1)
|
179
185
|
if files.count == 0
|
180
186
|
print NA::Color.template('{by}No todo file found, create one? {w}(y/{g}N{w}){x} ')
|
181
187
|
res = reader.read_char
|
182
188
|
if res =~ /y/i
|
183
189
|
basename = File.expand_path('.').split('/').last
|
184
|
-
target = "#{basename}.#{
|
190
|
+
target = "#{basename}.#{NA.extension}"
|
185
191
|
NA.create_todo(target, basename)
|
186
|
-
files = NA.find_files(depth: 1
|
192
|
+
files = NA.find_files(depth: 1)
|
187
193
|
end
|
188
194
|
end
|
189
195
|
target = files.count > 1 ? NA.select_file(files) : files[0]
|
@@ -193,7 +199,6 @@ class App
|
|
193
199
|
end
|
194
200
|
end
|
195
201
|
|
196
|
-
NA.save_working_dir(File.expand_path(target))
|
197
202
|
NA.add_action(target, action, note)
|
198
203
|
end
|
199
204
|
end
|
@@ -236,10 +241,8 @@ class App
|
|
236
241
|
end
|
237
242
|
|
238
243
|
files, actions = NA.parse_actions(depth: depth,
|
239
|
-
extension: global_options[:ext],
|
240
|
-
na_tag: global_options[:na_tag],
|
241
244
|
search: tokens)
|
242
|
-
NA.output_actions(actions, depth,
|
245
|
+
NA.output_actions(actions, depth, files: files)
|
243
246
|
end
|
244
247
|
end
|
245
248
|
|
@@ -278,10 +281,8 @@ class App
|
|
278
281
|
end
|
279
282
|
|
280
283
|
files, actions = NA.parse_actions(depth: depth,
|
281
|
-
extension: global_options[:ext],
|
282
|
-
na_tag: global_options[:na_tag],
|
283
284
|
tag: tags)
|
284
|
-
NA.output_actions(actions, depth,
|
285
|
+
NA.output_actions(actions, depth, files: files)
|
285
286
|
end
|
286
287
|
end
|
287
288
|
|
@@ -300,7 +301,7 @@ class App
|
|
300
301
|
project = reader.read_line(NA::Color.template('{y}Project name {bw}> {x}'), value: project).strip
|
301
302
|
end
|
302
303
|
|
303
|
-
target = "#{project}.#{
|
304
|
+
target = "#{project}.#{NA.extension}"
|
304
305
|
|
305
306
|
if File.exist?(target)
|
306
307
|
print NA::Color.template("{r}File {bw}#{target}{r} already exists, overwrite it? {br}y{w}/{bg}N{x} ")
|
@@ -339,7 +340,7 @@ class App
|
|
339
340
|
else
|
340
341
|
options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
|
341
342
|
end
|
342
|
-
files = NA.find_files(depth: depth
|
343
|
+
files = NA.find_files(depth: depth)
|
343
344
|
file = if files.count > 1
|
344
345
|
NA.select_file(files)
|
345
346
|
else
|
@@ -354,8 +355,59 @@ class App
|
|
354
355
|
end
|
355
356
|
end
|
356
357
|
|
358
|
+
desc 'Show or install prompt hooks for the current shell'
|
359
|
+
long_desc 'Installing the prompt hook allows you to automatically
|
360
|
+
list next actions when you cd into a directory'
|
361
|
+
command %i[prompt] do |c|
|
362
|
+
c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to specify a shell (zsh, bash, fish)'
|
363
|
+
c.arg_name '[SHELL]'
|
364
|
+
c.command %i[show] do |s|
|
365
|
+
s.action do |global_options, options, args|
|
366
|
+
if args.count.positive?
|
367
|
+
shell = args[0]
|
368
|
+
else
|
369
|
+
shell = File.basename(ENV['SHELL'])
|
370
|
+
end
|
371
|
+
|
372
|
+
case shell
|
373
|
+
when /^f/i
|
374
|
+
NA::Prompt.show_prompt_hook(:fish)
|
375
|
+
when /^z/i
|
376
|
+
NA::Prompt.show_prompt_hook(:zsh)
|
377
|
+
when /^b/i
|
378
|
+
NA::Prompt.show_prompt_hook(:bash)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
c.desc 'Install the hook for the current shell to the appropriate startup file.'
|
384
|
+
c.arg_name '[SHELL]'
|
385
|
+
c.command %i[install] do |s|
|
386
|
+
s.action do |global_options, options, args|
|
387
|
+
if args.count.positive?
|
388
|
+
shell = args[0]
|
389
|
+
else
|
390
|
+
shell = File.basename(ENV['SHELL'])
|
391
|
+
end
|
392
|
+
|
393
|
+
case shell
|
394
|
+
when /^f/i
|
395
|
+
NA::Prompt.install_prompt_hook(:fish)
|
396
|
+
when /^z/i
|
397
|
+
NA::Prompt.install_prompt_hook(:zsh)
|
398
|
+
when /^b/i
|
399
|
+
NA::Prompt.install_prompt_hook(:bash)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
|
406
|
+
|
357
407
|
pre do |global, command, options, args|
|
358
|
-
NA.verbose = global[:
|
408
|
+
NA.verbose = global[:debug]
|
409
|
+
NA.extension = global[:ext]
|
410
|
+
NA.na_tag = global[:na_tag]
|
359
411
|
true
|
360
412
|
end
|
361
413
|
|
data/lib/na/next_action.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Next Action methods
|
4
4
|
module NA
|
5
5
|
class << self
|
6
|
-
attr_accessor :verbose
|
6
|
+
attr_accessor :verbose, :extension, :na_tag
|
7
7
|
|
8
8
|
def create_todo(target, basename)
|
9
9
|
File.open(target, 'w') do |f|
|
@@ -22,11 +22,13 @@ module NA
|
|
22
22
|
ENDCONTENT
|
23
23
|
f.puts(content)
|
24
24
|
end
|
25
|
-
puts NA::Color.template("{y}Created {bw}#{target}{x}")
|
25
|
+
$stderr.puts NA::Color.template("{y}Created {bw}#{target}{x}")
|
26
26
|
end
|
27
27
|
|
28
|
-
def find_files(depth: 1
|
29
|
-
`find . -name "*.#{extension}" -maxdepth #{depth}`.strip.split("\n")
|
28
|
+
def find_files(depth: 1)
|
29
|
+
files = `find . -name "*.#{NA.extension}" -maxdepth #{depth}`.strip.split("\n")
|
30
|
+
files.each { |f| save_working_dir(File.expand_path(f)) }
|
31
|
+
files
|
30
32
|
end
|
31
33
|
|
32
34
|
def select_file(files)
|
@@ -39,7 +41,7 @@ module NA
|
|
39
41
|
elsif TTY::Which.exist?('fzf')
|
40
42
|
res = choose_from(files, prompt: 'Use which file?')
|
41
43
|
unless res
|
42
|
-
puts 'No file selected, cancelled'
|
44
|
+
$stderr.puts 'No file selected, cancelled'
|
43
45
|
Process.exit 1
|
44
46
|
end
|
45
47
|
|
@@ -69,17 +71,17 @@ module NA
|
|
69
71
|
|
70
72
|
File.open(file, 'w') { |f| f.puts content }
|
71
73
|
|
72
|
-
puts NA::Color.template("{by}Task added to {bw}#{file}{x}")
|
74
|
+
$stderr.puts NA::Color.template("{by}Task added to {bw}#{file}{x}")
|
73
75
|
end
|
74
76
|
|
75
|
-
def output_actions(actions, depth,
|
77
|
+
def output_actions(actions, depth, files: nil)
|
76
78
|
template = if files&.count.positive?
|
77
79
|
if files.count == 1
|
78
80
|
'%parent%action'
|
79
81
|
else
|
80
82
|
'%filename%parent%action'
|
81
83
|
end
|
82
|
-
elsif NA.find_files(depth: depth
|
84
|
+
elsif NA.find_files(depth: depth).count > 1
|
83
85
|
if depth > 1
|
84
86
|
'%filename%parent%action'
|
85
87
|
else
|
@@ -89,13 +91,13 @@ module NA
|
|
89
91
|
'%parent%action'
|
90
92
|
end
|
91
93
|
if files && @verbose
|
92
|
-
puts files.map { |f| NA::Color.template("{dw}#{f}{x}") }
|
94
|
+
$stderr.puts files.map { |f| NA::Color.template("{dw}#{f}{x}") }
|
93
95
|
end
|
94
96
|
|
95
97
|
puts actions.map { |action| action.pretty(template: { output: template }) }
|
96
98
|
end
|
97
99
|
|
98
|
-
def parse_actions(depth: 1,
|
100
|
+
def parse_actions(depth: 1, query: nil, tag: nil, search: nil)
|
99
101
|
actions = []
|
100
102
|
required = []
|
101
103
|
optional = []
|
@@ -122,10 +124,10 @@ module NA
|
|
122
124
|
end
|
123
125
|
end
|
124
126
|
|
125
|
-
na_tag = "@#{na_tag.sub(/^@/, '')}"
|
127
|
+
na_tag = "@#{NA.na_tag.sub(/^@/, '')}"
|
126
128
|
|
127
129
|
if query.nil?
|
128
|
-
files = find_files(depth: depth
|
130
|
+
files = find_files(depth: depth)
|
129
131
|
else
|
130
132
|
files = match_working_dir(query)
|
131
133
|
end
|
@@ -151,13 +153,15 @@ module NA
|
|
151
153
|
indent_level = indent
|
152
154
|
end
|
153
155
|
elsif line =~ /^[ \t]*- / && line !~ / @done/
|
156
|
+
next unless line =~ /@#{NA.na_tag}\b/
|
157
|
+
|
154
158
|
unless optional.empty? && required.empty?
|
155
159
|
next unless line.matches(any: optional, all: required)
|
156
160
|
|
157
161
|
end
|
158
162
|
|
159
|
-
action = line.sub(/^[ \t]*- /, '').sub(/
|
160
|
-
new_action = NA::Action.new(file, File.basename(file, ".#{extension}"), parent.dup, action)
|
163
|
+
action = line.sub(/^[ \t]*- /, '').sub(/ @#{NA.na_tag}\b/, '')
|
164
|
+
new_action = NA::Action.new(file, File.basename(file, ".#{NA.extension}"), parent.dup, action)
|
161
165
|
actions.push(new_action)
|
162
166
|
end
|
163
167
|
end
|
@@ -220,7 +224,7 @@ module NA
|
|
220
224
|
dirs.delete_if { |d| !d.matches(any: optional, all: required) }
|
221
225
|
dirs.sort.uniq
|
222
226
|
else
|
223
|
-
puts NA::Color.template('{r}No na database found{x}')
|
227
|
+
$stderr.puts NA::Color.template('{r}No na database found{x}')
|
224
228
|
Process.exit 1
|
225
229
|
end
|
226
230
|
end
|
@@ -269,7 +273,7 @@ module NA
|
|
269
273
|
if 'xdg-open'.available?
|
270
274
|
`xdg-open #{Shellwords.escape(file)}`
|
271
275
|
else
|
272
|
-
puts NA::Color.template('{r}Unable to determine executable for `open`.{x}')
|
276
|
+
$stderr.puts NA::Color.template('{r}Unable to determine executable for `open`.{x}')
|
273
277
|
end
|
274
278
|
end
|
275
279
|
end
|
data/lib/na/prompt.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NA
|
4
|
+
# Prompt Hooks
|
5
|
+
module Prompt
|
6
|
+
class << self
|
7
|
+
def prompt_hook(shell)
|
8
|
+
case shell
|
9
|
+
when :zsh
|
10
|
+
<<~EOHOOK
|
11
|
+
# zsh prompt hook for na
|
12
|
+
chpwd() { na next }
|
13
|
+
EOHOOK
|
14
|
+
when :fish
|
15
|
+
<<~EOHOOK
|
16
|
+
# Fish Prompt Command
|
17
|
+
function __should_na --on-variable PWD
|
18
|
+
test -s (basename $PWD)".#{NA.extension}" && na next
|
19
|
+
end
|
20
|
+
EOHOOK
|
21
|
+
when :bash
|
22
|
+
<<~EOHOOK
|
23
|
+
# Bash PROMPT_COMMAND for na
|
24
|
+
last_command_was_cd() {
|
25
|
+
[[ $(history 1|sed -e "s/^[ ]*[0-9]*[ ]*//") =~ ^((cd|z|j|jump|g|f|pushd|popd|exit)([ ]|$)) ]] && na next
|
26
|
+
}
|
27
|
+
if [[ -z "$PROMPT_COMMAND" ]]; then
|
28
|
+
PROMPT_COMMAND="eval 'last_command_was_cd'"
|
29
|
+
else
|
30
|
+
echo $PROMPT_COMMAND | grep -v -q "last_command_was_cd" && PROMPT_COMMAND="$PROMPT_COMMAND;"'eval "last_command_was_cd"'
|
31
|
+
fi
|
32
|
+
EOHOOK
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def prompt_file(shell)
|
37
|
+
files = {
|
38
|
+
zsh: '~/.zshrc',
|
39
|
+
fish: '~/.config/fish/conf.d/na.fish',
|
40
|
+
bash: '~/.bash_profile'
|
41
|
+
}
|
42
|
+
|
43
|
+
files[shell]
|
44
|
+
end
|
45
|
+
|
46
|
+
def show_prompt_hook(shell)
|
47
|
+
file = prompt_file(shell)
|
48
|
+
|
49
|
+
$stderr.puts NA::Color.template("{bw}# Add this to {y}#{file}{x}")
|
50
|
+
puts prompt_hook(shell)
|
51
|
+
end
|
52
|
+
|
53
|
+
def install_prompt_hook(shell)
|
54
|
+
file = prompt_file(shell)
|
55
|
+
|
56
|
+
File.open(File.expand_path(file), 'a') { |f| f.puts prompt_hook(shell) }
|
57
|
+
$stderr.puts NA::Color.template("{y}Added {bw}#{shell}{xy} prompt hook to {bw}#{file}{x}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/na/version.rb
CHANGED
data/lib/na.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.0.
|
12
|
+
The current version of `na` is <!--VER-->1.0.6<!--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
|
|
@@ -293,7 +293,6 @@ Fish (in ~/.config/fish/conf.d/*.fish):
|
|
293
293
|
|
294
294
|
```fish
|
295
295
|
function __should_na --on-variable PWD
|
296
|
-
# function __should_na --on-event fish_prompt
|
297
296
|
test -s (basename $PWD)".taskpaper" && na
|
298
297
|
end
|
299
298
|
```
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: na
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
@@ -170,6 +170,7 @@ files:
|
|
170
170
|
- lib/na/action.rb
|
171
171
|
- lib/na/colors.rb
|
172
172
|
- lib/na/next_action.rb
|
173
|
+
- lib/na/prompt.rb
|
173
174
|
- lib/na/string.rb
|
174
175
|
- lib/na/version.rb
|
175
176
|
- na.gemspec
|