na 1.2.33 → 1.2.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/Gemfile.lock +1 -1
- data/README.md +4 -3
- data/bin/commands/complete.rb +5 -1
- data/bin/commands/find.rb +28 -8
- data/bin/commands/restore.rb +56 -0
- data/bin/commands/tagged.rb +7 -3
- data/lib/na/action.rb +18 -1
- data/lib/na/next_action.rb +10 -2
- data/lib/na/version.rb +1 -1
- data/src/_README.md +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f8982f5720115576a78ab96a430d1d83f501bb1b477b2cff1a694648a186afc
|
4
|
+
data.tar.gz: 86dd520a3265473471b6d09fc05136dd07b53ded24ab2c82936074fa50595b67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ea9bb861c82251c1ec52925d6527f0e4efa1eef0175d268b8a8bb307fafcc407dc888e5e5cd6d588e5e4a500a96ae64765106f23d9c4acda6e0667c71247d62
|
7
|
+
data.tar.gz: e5f603160851411f66dd50ae47a65369aaa1e51352cf2eb6313674d95d64db8914d3a870f816dcc4303fbd5b4e675f213020fa6a7fd93805af4692f04eb99aed
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
### 1.2.35
|
2
|
+
|
3
|
+
2023-08-30 11:59
|
4
|
+
|
5
|
+
#### IMPROVED
|
6
|
+
|
7
|
+
- If a search string contains @tags and --exact or --regex isn't specified, the @tags will be extracted and passed as a --tagged search.
|
8
|
+
- Tag handling (including values and comparisons) in tags extracted from search strings
|
9
|
+
- `na complete --project PROJ` flag to move to a specific project
|
10
|
+
- `na restore --project PROJ` flag to move restored action to
|
11
|
+
- Exit gracefully if tagged command is run with invalid
|
12
|
+
- Display action affected when using update command
|
13
|
+
|
14
|
+
#### FIXED
|
15
|
+
|
16
|
+
- Escape search for tokens to allow parenthesis and other
|
17
|
+
|
18
|
+
### 1.2.34
|
19
|
+
|
20
|
+
2023-08-30 09:22
|
21
|
+
|
22
|
+
#### NEW
|
23
|
+
|
24
|
+
- `na restore SEARCH` to remove @done tag from result(s) (aliased as `unfinish`)
|
25
|
+
|
1
26
|
### 1.2.33
|
2
27
|
|
3
28
|
2023-08-29 13:45
|
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.2.
|
12
|
+
The current version of `na` is 1.2.35
|
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.
|
@@ -77,7 +77,7 @@ SYNOPSIS
|
|
77
77
|
na [global options] command [command options] [arguments...]
|
78
78
|
|
79
79
|
VERSION
|
80
|
-
1.2.
|
80
|
+
1.2.35
|
81
81
|
|
82
82
|
GLOBAL OPTIONS
|
83
83
|
-a, --add - Add a next action (deprecated, for backwards compatibility)
|
@@ -104,7 +104,7 @@ COMMANDS
|
|
104
104
|
complete, finish - Find and mark an action as @done
|
105
105
|
completed, finished - Display completed actions
|
106
106
|
edit - Edit an existing action
|
107
|
-
find, grep
|
107
|
+
find, grep, search - Find actions matching a search pattern
|
108
108
|
help - Shows a list of commands or help for one command
|
109
109
|
init, create - Create a new todo file in the current directory
|
110
110
|
initconfig - Initialize the config file using current global options
|
@@ -112,6 +112,7 @@ COMMANDS
|
|
112
112
|
open - Open a todo file in the default editor
|
113
113
|
projects - Show list of projects for a file
|
114
114
|
prompt - Show or install prompt hooks for the current shell
|
115
|
+
restore, unfinish - Find and remove @done tag from an action
|
115
116
|
saved - Execute a saved search
|
116
117
|
tagged - Find actions matching a tag
|
117
118
|
todos - Show list of known todo files
|
data/bin/commands/complete.rb
CHANGED
@@ -20,6 +20,10 @@ class App
|
|
20
20
|
c.desc 'Add a @done tag to action and move to Archive'
|
21
21
|
c.switch %i[a archive], negatable: false
|
22
22
|
|
23
|
+
c.desc 'Move action to specific project'
|
24
|
+
c.arg_name 'PROJECT'
|
25
|
+
c.flag %i[to project proj]
|
26
|
+
|
23
27
|
c.desc 'Specify the file to search for the task'
|
24
28
|
c.arg_name 'PATH'
|
25
29
|
c.flag %i[file]
|
@@ -44,7 +48,7 @@ class App
|
|
44
48
|
c.action do |global, options, args|
|
45
49
|
options[:finish] = true
|
46
50
|
options[:f] = true
|
47
|
-
options[:project] = 'Archive' if options[:archive]
|
51
|
+
options[:project] = 'Archive' if options[:archive] && !options[:project]
|
48
52
|
|
49
53
|
cmd = commands[:update]
|
50
54
|
action = cmd.send(:get_action, nil)
|
data/bin/commands/find.rb
CHANGED
@@ -7,7 +7,7 @@ class App
|
|
7
7
|
(partial matches allowed). Add a + before a token to make it required, e.g. `na find +feature +maybe`,
|
8
8
|
add a - or ! to ignore matches containing that token.'
|
9
9
|
arg_name 'PATTERN'
|
10
|
-
command %i[find grep] do |c|
|
10
|
+
command %i[find grep search] do |c|
|
11
11
|
c.example 'na find feature idea swift', desc: 'Find all actions containing feature, idea, and swift'
|
12
12
|
c.example 'na find feature idea -swift', desc: 'Find all actions containing feature and idea but NOT swift'
|
13
13
|
c.example 'na find -x feature idea', desc: 'Find all actions containing the exact text "feature idea"'
|
@@ -64,17 +64,33 @@ class App
|
|
64
64
|
NA.save_search(title, "#{NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.join(' ')}")
|
65
65
|
end
|
66
66
|
|
67
|
-
|
68
67
|
depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
|
69
68
|
3
|
70
69
|
else
|
71
70
|
options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
|
72
71
|
end
|
73
72
|
|
73
|
+
if options[:exact] || options[:regex]
|
74
|
+
search = args.join(' ')
|
75
|
+
else
|
76
|
+
search = args.join(' ').gsub(/(?<=\A|[ ,])(?<req>[+\-!])?@(?<tag>[^ *=<>$\^,@(]+)(?:\((?<value>.*?)\)| *(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?(?=\Z|[,@])))?/) do |arg|
|
77
|
+
m = Regexp.last_match
|
78
|
+
string = if m['value']
|
79
|
+
"#{m['req']}#{m['tag']}=#{m['value']}"
|
80
|
+
else
|
81
|
+
m[0]
|
82
|
+
end
|
83
|
+
options[:tagged] << string.sub(/@/, '')
|
84
|
+
''
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
search = search.gsub(/ +/, ' ').strip
|
89
|
+
|
74
90
|
all_req = options[:tagged].join(' ') !~ /[+!\-]/ && !options[:or]
|
75
91
|
tags = []
|
76
92
|
options[:tagged].join(',').split(/ *, */).each do |arg|
|
77
|
-
m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
|
93
|
+
m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?) *(?:(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
|
78
94
|
|
79
95
|
tags.push({
|
80
96
|
tag: m['tag'].wildcard_to_rx,
|
@@ -85,19 +101,23 @@ class App
|
|
85
101
|
})
|
86
102
|
end
|
87
103
|
|
104
|
+
search_for_done = false
|
105
|
+
tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
|
106
|
+
options[:done] = true if search_for_done
|
107
|
+
|
88
108
|
tokens = nil
|
89
109
|
if options[:exact]
|
90
|
-
tokens =
|
110
|
+
tokens = search
|
91
111
|
elsif options[:regex]
|
92
|
-
tokens = Regexp.new(
|
112
|
+
tokens = Regexp.new(search, Regexp::IGNORECASE)
|
93
113
|
else
|
94
114
|
tokens = []
|
95
|
-
all_req =
|
115
|
+
all_req = search !~ /[+!\-]/ && !options[:or]
|
96
116
|
|
97
|
-
|
117
|
+
search.split(/ /).each do |arg|
|
98
118
|
m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
|
99
119
|
tokens.push({
|
100
|
-
token: m['tok'],
|
120
|
+
token: Regexp.escape(m['tok']),
|
101
121
|
required: all_req || (!m['req'].nil? && m['req'] == '+'),
|
102
122
|
negate: !m['req'].nil? && m['req'] =~ /[!\-]/
|
103
123
|
})
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class App
|
4
|
+
extend GLI::App
|
5
|
+
desc 'Find and remove @done tag from an action'
|
6
|
+
arg_name 'PATTERN'
|
7
|
+
command %i[restore unfinish] do |c|
|
8
|
+
c.example 'na restore "An existing task"',
|
9
|
+
desc: 'Find "An existing task" and remove @done'
|
10
|
+
c.example 'na unfinish "An existing task"',
|
11
|
+
desc: 'Alias for restore'
|
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 'Move action to specific project'
|
21
|
+
c.arg_name 'PROJECT'
|
22
|
+
c.flag %i[to project proj]
|
23
|
+
|
24
|
+
c.desc 'Specify the file to search for the task'
|
25
|
+
c.arg_name 'PATH'
|
26
|
+
c.flag %i[file]
|
27
|
+
|
28
|
+
c.desc 'Search for files X directories deep'
|
29
|
+
c.arg_name 'DEPTH'
|
30
|
+
c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
|
31
|
+
|
32
|
+
c.desc 'Match actions containing tag. Allows value comparisons'
|
33
|
+
c.arg_name 'TAG'
|
34
|
+
c.flag %i[tagged], multiple: true
|
35
|
+
|
36
|
+
c.desc 'Act on all matches immediately (no menu)'
|
37
|
+
c.switch %i[all], negatable: false
|
38
|
+
|
39
|
+
c.desc 'Interpret search pattern as regular expression'
|
40
|
+
c.switch %i[e regex], negatable: false
|
41
|
+
|
42
|
+
c.desc 'Match pattern exactly'
|
43
|
+
c.switch %i[x exact], negatable: false
|
44
|
+
|
45
|
+
c.action do |global, options, args|
|
46
|
+
options[:remove] = ['done']
|
47
|
+
options[:done] = true
|
48
|
+
options[:finish] = false
|
49
|
+
options[:f] = false
|
50
|
+
|
51
|
+
cmd = commands[:update]
|
52
|
+
action = cmd.send(:get_action, nil)
|
53
|
+
action.call(global, options, args)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/bin/commands/tagged.rb
CHANGED
@@ -80,10 +80,11 @@ class App
|
|
80
80
|
|
81
81
|
all_req = args.join(' ') !~ /[+!\-]/ && !options[:or]
|
82
82
|
args.join(',').split(/ *, */).each do |arg|
|
83
|
-
m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
|
83
|
+
m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?) *(?:(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
|
84
|
+
next if m.nil?
|
84
85
|
|
85
86
|
tags.push({
|
86
|
-
tag: m['tag'].wildcard_to_rx,
|
87
|
+
tag: m['tag'].sub(/^@/, '').wildcard_to_rx,
|
87
88
|
comp: m['op'],
|
88
89
|
value: m['val'],
|
89
90
|
required: all_req || (!m['req'].nil? && m['req'] == '+'),
|
@@ -93,7 +94,8 @@ class App
|
|
93
94
|
|
94
95
|
search_for_done = false
|
95
96
|
tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
|
96
|
-
tags.push({ tag: 'done', value: nil, negate: true}) unless search_for_done
|
97
|
+
tags.push({ tag: 'done', value: nil, negate: true}) unless search_for_done || options[:done]
|
98
|
+
options[:done] = true if search_for_done
|
97
99
|
|
98
100
|
tokens = nil
|
99
101
|
if options[:search]
|
@@ -129,6 +131,8 @@ class App
|
|
129
131
|
end
|
130
132
|
end
|
131
133
|
|
134
|
+
NA.notify('{br}No actions matched search', exit_code: 1) if tags.empty? && tokens.empty?
|
135
|
+
|
132
136
|
files, actions, = NA.parse_actions(depth: depth,
|
133
137
|
done: options[:done],
|
134
138
|
query: todo,
|
data/lib/na/action.rb
CHANGED
@@ -37,7 +37,18 @@ module NA
|
|
37
37
|
EOINSPECT
|
38
38
|
end
|
39
39
|
|
40
|
+
##
|
41
|
+
## Pretty print an action
|
42
|
+
##
|
43
|
+
## @param extension [String] The file extension
|
44
|
+
## @param template [Hash] The template to use for
|
45
|
+
## colorization
|
46
|
+
## @param regexes [Array] The regexes to
|
47
|
+
## highlight (searches)
|
48
|
+
## @param notes [Boolean] Include notes
|
49
|
+
##
|
40
50
|
def pretty(extension: 'taskpaper', template: {}, regexes: [], notes: false)
|
51
|
+
# Default colorization, can be overridden with full or partial template variable
|
41
52
|
default_template = {
|
42
53
|
file: '{xbk}',
|
43
54
|
parent: '{c}',
|
@@ -51,32 +62,38 @@ module NA
|
|
51
62
|
note: '{dw}'
|
52
63
|
}
|
53
64
|
template = default_template.merge(template)
|
54
|
-
|
65
|
+
# Create the hierarchical parent string
|
55
66
|
parents = @parent.map do |par|
|
56
67
|
NA::Color.template("#{template[:parent]}#{par}")
|
57
68
|
end.join(NA::Color.template(template[:parent_divider]))
|
58
69
|
parents = "{dc}[{x}#{parents}{dc}]{x} "
|
59
70
|
|
71
|
+
# Create the project string
|
60
72
|
project = NA::Color.template("#{template[:project]}#{@project}{x} ")
|
61
73
|
|
74
|
+
# Create the source filename string, substituting ~ for HOME and removing extension
|
62
75
|
file = @file.sub(%r{^\./}, '').sub(/#{ENV['HOME']}/, '~')
|
63
76
|
file = file.sub(/\.#{extension}$/, '')
|
77
|
+
# colorize the basename
|
64
78
|
file = file.sub(/#{File.basename(@file, ".#{extension}")}$/, "{dw}#{File.basename(@file, ".#{extension}")}{x}")
|
65
79
|
file_tpl = "#{template[:file]}#{file} {x}"
|
66
80
|
filename = NA::Color.template(file_tpl)
|
67
81
|
|
82
|
+
# Add notes if needed
|
68
83
|
note = if notes && @note.count.positive?
|
69
84
|
NA::Color.template("\n#{@note.map { |l| " #{template[:note]}• #{l}{x}" }.join("\n")}")
|
70
85
|
else
|
71
86
|
''
|
72
87
|
end
|
73
88
|
|
89
|
+
# colorize the action and highlight tags
|
74
90
|
action = NA::Color.template("#{template[:action]}#{@action.sub(/ @#{NA.na_tag}\b/, '')}{x}")
|
75
91
|
action = action.highlight_tags(color: template[:tags],
|
76
92
|
parens: template[:value_parens],
|
77
93
|
value: template[:values],
|
78
94
|
last_color: template[:action])
|
79
95
|
|
96
|
+
# Replace variables in template string and output colorized
|
80
97
|
NA::Color.template(template[:output].gsub(/%filename/, filename)
|
81
98
|
.gsub(/%project/, project)
|
82
99
|
.gsub(/%parents?/, parents)
|
data/lib/na/next_action.rb
CHANGED
@@ -420,7 +420,7 @@ module NA
|
|
420
420
|
projects.select { |proj| proj.project =~ /^#{action.parent.join(':')}$/ }.first
|
421
421
|
end
|
422
422
|
|
423
|
-
NA.notify("{r}Error parsing project #{
|
423
|
+
NA.notify("{r}Error parsing project #{target}", exit_code: 1) if target_proj.nil?
|
424
424
|
|
425
425
|
indent = "\t" * target_proj.indent
|
426
426
|
note = note.split("\n") unless note.is_a?(Array)
|
@@ -450,6 +450,8 @@ module NA
|
|
450
450
|
end
|
451
451
|
|
452
452
|
contents.insert(target_line, "#{indent}\t- #{action.action}#{note}")
|
453
|
+
|
454
|
+
notify(action.pretty)
|
453
455
|
else
|
454
456
|
_, actions = find_actions(target, search, tagged, done: done, all: all)
|
455
457
|
|
@@ -502,7 +504,10 @@ module NA
|
|
502
504
|
target_line = target_proj.line + 1
|
503
505
|
end
|
504
506
|
|
507
|
+
|
505
508
|
contents.insert(target_line, "#{indent}\t- #{action.action}#{note}")
|
509
|
+
|
510
|
+
notify(action.pretty)
|
506
511
|
end
|
507
512
|
end
|
508
513
|
|
@@ -692,6 +697,9 @@ module NA
|
|
692
697
|
negated_tag = []
|
693
698
|
projects = []
|
694
699
|
|
700
|
+
notify("{dw}Tags: #{tag}", debug:true)
|
701
|
+
notify("{dw}Search: #{search}", debug:true)
|
702
|
+
|
695
703
|
tag&.each do |t|
|
696
704
|
unless t[:tag].nil?
|
697
705
|
if negate
|
@@ -706,7 +714,7 @@ module NA
|
|
706
714
|
end
|
707
715
|
end
|
708
716
|
|
709
|
-
unless search.nil?
|
717
|
+
unless search.nil? || search.empty?
|
710
718
|
if regex || search.is_a?(String)
|
711
719
|
if negate
|
712
720
|
negated.push(search)
|
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.2.
|
12
|
+
The current version of `na` is <!--VER-->1.2.34<!--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
|
|
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.2.
|
4
|
+
version: 1.2.35
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -218,6 +218,7 @@ files:
|
|
218
218
|
- bin/commands/open.rb
|
219
219
|
- bin/commands/projects.rb
|
220
220
|
- bin/commands/prompt.rb
|
221
|
+
- bin/commands/restore.rb
|
221
222
|
- bin/commands/saved.rb
|
222
223
|
- bin/commands/tagged.rb
|
223
224
|
- bin/commands/todos.rb
|