na 1.2.35 → 1.2.38
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/Gemfile.lock +1 -1
- data/README.md +156 -6
- data/bin/commands/add.rb +11 -13
- data/bin/commands/complete.rb +4 -0
- data/bin/commands/edit.rb +21 -24
- data/bin/commands/find.rb +36 -23
- data/bin/commands/init.rb +1 -1
- data/bin/commands/next.rb +70 -37
- data/bin/commands/projects.rb +1 -1
- data/bin/commands/prompt.rb +2 -0
- data/bin/commands/saved.rb +3 -4
- data/bin/commands/tagged.rb +37 -30
- data/bin/commands/todos.rb +24 -14
- data/bin/commands/undo.rb +22 -0
- data/bin/commands/update.rb +28 -21
- data/bin/na +2 -1
- data/lib/na/action.rb +39 -22
- data/lib/na/actions.rb +87 -0
- data/lib/na/colors.rb +23 -1
- data/lib/na/editor.rb +125 -0
- data/lib/na/hash.rb +31 -0
- data/lib/na/next_action.rb +237 -498
- data/lib/na/pager.rb +1 -1
- data/lib/na/prompt.rb +6 -6
- data/lib/na/string.rb +23 -3
- data/lib/na/theme.rb +71 -0
- data/lib/na/todo.rb +183 -0
- data/lib/na/version.rb +1 -1
- data/lib/na.rb +4 -0
- data/src/_README.md +45 -1
- metadata +7 -2
data/lib/na/pager.rb
CHANGED
data/lib/na/prompt.rb
CHANGED
@@ -14,7 +14,7 @@ module NA
|
|
14
14
|
when :tag
|
15
15
|
'na tagged $(basename "$PWD")'
|
16
16
|
else
|
17
|
-
NA.notify(
|
17
|
+
NA.notify("#{NA.theme[:error]}When using a global file, a prompt hook requires `--cwd_as [tag|project]`", exit_code: 1)
|
18
18
|
end
|
19
19
|
else
|
20
20
|
'na next'
|
@@ -31,7 +31,7 @@ module NA
|
|
31
31
|
when :tag
|
32
32
|
'na tagged (basename "$PWD")'
|
33
33
|
else
|
34
|
-
NA.notify(
|
34
|
+
NA.notify("#{NA.theme[:error]}When using a global file, a prompt hook requires `--cwd_as [tag|project]`", exit_code: 1)
|
35
35
|
end
|
36
36
|
else
|
37
37
|
'na next'
|
@@ -50,7 +50,7 @@ module NA
|
|
50
50
|
when :tag
|
51
51
|
'na tagged $(basename "$PWD")'
|
52
52
|
else
|
53
|
-
NA.notify(
|
53
|
+
NA.notify("#{NA.theme[:error]}When using a global file, a prompt hook requires `--cwd_as [tag|project]`", exit_code: 1)
|
54
54
|
end
|
55
55
|
else
|
56
56
|
'na next'
|
@@ -83,7 +83,7 @@ module NA
|
|
83
83
|
def show_prompt_hook(shell)
|
84
84
|
file = prompt_file(shell)
|
85
85
|
|
86
|
-
NA.notify("{
|
86
|
+
NA.notify("#{NA.theme[:warning]}# Add this to #{NA.theme[:filename]}#{file}")
|
87
87
|
puts prompt_hook(shell)
|
88
88
|
end
|
89
89
|
|
@@ -91,8 +91,8 @@ module NA
|
|
91
91
|
file = prompt_file(shell)
|
92
92
|
|
93
93
|
File.open(File.expand_path(file), 'a') { |f| f.puts prompt_hook(shell) }
|
94
|
-
NA.notify("{
|
95
|
-
NA.notify("{
|
94
|
+
NA.notify("#{NA.theme[:success]}Added #{NA.theme[:filename]}#{shell}{x}#{NA.theme[:success]} prompt hook to #{NA.theme[:filename]}#{file}#{NA.theme[:success]}.")
|
95
|
+
NA.notify("#{NA.theme[:warning]}You may need to close the current terminal and open a new one to enable the script.")
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
data/lib/na/string.rb
CHANGED
@@ -6,6 +6,15 @@ REGEX_TIME = /^#{REGEX_CLOCK}$/i.freeze
|
|
6
6
|
|
7
7
|
# String helpers
|
8
8
|
class ::String
|
9
|
+
##
|
10
|
+
## Insert a comment character at the start of every line
|
11
|
+
##
|
12
|
+
## @param char [String] The character to insert (default #)
|
13
|
+
##
|
14
|
+
def comment(char = "#")
|
15
|
+
split(/\n/).map { |l| "# #{l}" }.join("\n")
|
16
|
+
end
|
17
|
+
|
9
18
|
##
|
10
19
|
## Tests if object is nil or empty
|
11
20
|
##
|
@@ -75,6 +84,17 @@ class ::String
|
|
75
84
|
self =~ /@#{NA.na_tag}\b/
|
76
85
|
end
|
77
86
|
|
87
|
+
##
|
88
|
+
## Colorize the dirname and filename of a path
|
89
|
+
##
|
90
|
+
## @return Colorized string
|
91
|
+
##
|
92
|
+
def highlight_filename
|
93
|
+
dir = File.dirname(self).shorten_path
|
94
|
+
file = File.basename(self, ".#{NA.extension}")
|
95
|
+
"#{NA.theme[:dirname]}#{dir}/#{NA.theme[:filename]}#{file}{x}"
|
96
|
+
end
|
97
|
+
|
78
98
|
##
|
79
99
|
## Colorize @tags with ANSI escapes
|
80
100
|
##
|
@@ -88,7 +108,7 @@ class ::String
|
|
88
108
|
##
|
89
109
|
## @return [String] string with @tags highlighted
|
90
110
|
##
|
91
|
-
def highlight_tags(color:
|
111
|
+
def highlight_tags(color: NA.theme[:tags], value: NA.theme[:value], parens: NA.theme[:value_parens], last_color: NA.theme[:action])
|
92
112
|
tag_color = NA::Color.template(color)
|
93
113
|
paren_color = NA::Color.template(parens)
|
94
114
|
value_color = NA::Color.template(value)
|
@@ -106,9 +126,9 @@ class ::String
|
|
106
126
|
## @param last_color [String] Color to restore after
|
107
127
|
## highlight
|
108
128
|
##
|
109
|
-
def highlight_search(regexes, color:
|
129
|
+
def highlight_search(regexes, color: NA.theme[:search_highlight], last_color: NA.theme[:action])
|
110
130
|
string = dup
|
111
|
-
color = NA::Color.template(color)
|
131
|
+
color = NA::Color.template(color.dup)
|
112
132
|
regexes.each do |rx|
|
113
133
|
next if rx.nil?
|
114
134
|
|
data/lib/na/theme.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NA
|
4
|
+
module Theme
|
5
|
+
class << self
|
6
|
+
def template_help
|
7
|
+
<<~EOHELP
|
8
|
+
Use {X} placeholders to apply colors. Available colors are:
|
9
|
+
|
10
|
+
w: white, k: black, g: green, l: blue,
|
11
|
+
y: yellow, c: cyan, m: magenta, r: red,
|
12
|
+
W: bgwhite, K: bgblack, G: bggreen, L: bgblue,
|
13
|
+
Y: bgyellow, C: bgcyan, M: bgmagenta, R: bgred,
|
14
|
+
d: dark, b: bold, u: underline, i: italic, x: reset
|
15
|
+
|
16
|
+
Multiple placeholders can be combined in a single {} pair.
|
17
|
+
|
18
|
+
You can also use {#RGB} and {#RRGGBB} to specify hex colors.
|
19
|
+
Add a b before the # to make the hex a background color ({b#fa0}).
|
20
|
+
|
21
|
+
|
22
|
+
EOHELP
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_theme(template: {})
|
26
|
+
# Default colorization, can be overridden with full or partial template variable
|
27
|
+
default_template = {
|
28
|
+
parent: '{c}',
|
29
|
+
bracket: '{dc}',
|
30
|
+
parent_divider: '{xw}/',
|
31
|
+
action: '{bg}',
|
32
|
+
project: '{xbk}',
|
33
|
+
tags: '{m}',
|
34
|
+
value_parens: '{m}',
|
35
|
+
values: '{c}',
|
36
|
+
search_highlight: '{y}',
|
37
|
+
note: '{dw}',
|
38
|
+
dirname: '{dw}',
|
39
|
+
filename: '{xb}{#eccc87}',
|
40
|
+
prompt: '{m}',
|
41
|
+
success: '{bg}',
|
42
|
+
error: '{b}{#b61d2a}',
|
43
|
+
warning: '{by}',
|
44
|
+
debug: '{dw}',
|
45
|
+
templates: {
|
46
|
+
output: '%filename%parents| %action',
|
47
|
+
default: '%parent%action',
|
48
|
+
single_file: '%parent%action',
|
49
|
+
multi_file: '%filename%parent%action'
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
# Load custom theme
|
54
|
+
theme_file = NA.database_path(file: 'theme.yaml')
|
55
|
+
theme = if File.exist?(theme_file)
|
56
|
+
YAML.load(IO.read(theme_file)) || {}
|
57
|
+
else
|
58
|
+
{}
|
59
|
+
end
|
60
|
+
theme = default_template.merge(theme)
|
61
|
+
|
62
|
+
File.open(theme_file, 'w') do |f|
|
63
|
+
f.puts template_help.comment
|
64
|
+
f.puts YAML.dump(theme)
|
65
|
+
end
|
66
|
+
|
67
|
+
theme.merge(template)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/na/todo.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NA
|
4
|
+
class Todo
|
5
|
+
attr_accessor :actions, :projects, :files
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@files, @actions, @projects = parse(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
## Read a todo file and create a list of actions
|
13
|
+
##
|
14
|
+
## @param options The options
|
15
|
+
##
|
16
|
+
## @option depth [Number] The directory depth to
|
17
|
+
## search for files
|
18
|
+
## @option done [Boolean] include @done actions
|
19
|
+
## @option query [Hash] The todo file query
|
20
|
+
## @option tag [Array] Tags to search for
|
21
|
+
## @option search [String] A search string
|
22
|
+
## @option negate [Boolean] Invert results
|
23
|
+
## @option regex [Boolean] Interpret as regular
|
24
|
+
## expression
|
25
|
+
## @option project [String] The project
|
26
|
+
## @option require_na [Boolean] Require @na tag
|
27
|
+
## @option file_path [String] file path to parse
|
28
|
+
##
|
29
|
+
def parse(options)
|
30
|
+
defaults = {
|
31
|
+
depth: 1,
|
32
|
+
done: false,
|
33
|
+
file_path: nil,
|
34
|
+
negate: false,
|
35
|
+
project: nil,
|
36
|
+
query: nil,
|
37
|
+
regex: false,
|
38
|
+
require_na: true,
|
39
|
+
search: nil,
|
40
|
+
tag: nil
|
41
|
+
}
|
42
|
+
|
43
|
+
settings = defaults.merge(options)
|
44
|
+
|
45
|
+
actions = NA::Actions.new
|
46
|
+
required = []
|
47
|
+
optional = []
|
48
|
+
negated = []
|
49
|
+
required_tag = []
|
50
|
+
optional_tag = []
|
51
|
+
negated_tag = []
|
52
|
+
projects = []
|
53
|
+
|
54
|
+
NA.notify("Tags: #{settings[:tag]}", debug:true)
|
55
|
+
NA.notify("Search: #{settings[:search]}", debug:true)
|
56
|
+
|
57
|
+
settings[:tag]&.each do |t|
|
58
|
+
unless t[:tag].nil?
|
59
|
+
if settings[:negate]
|
60
|
+
optional_tag.push(t) if t[:negate]
|
61
|
+
required_tag.push(t) if t[:required] && t[:negate]
|
62
|
+
negated_tag.push(t) unless t[:negate]
|
63
|
+
else
|
64
|
+
optional_tag.push(t) unless t[:negate]
|
65
|
+
required_tag.push(t) if t[:required] && !t[:negate]
|
66
|
+
negated_tag.push(t) if t[:negate]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
unless settings[:search].nil? || settings[:search].empty?
|
72
|
+
if settings[:regex] || settings[:search].is_a?(String)
|
73
|
+
if settings[:negate]
|
74
|
+
negated.push(settings[:search])
|
75
|
+
else
|
76
|
+
optional.push(settings[:search])
|
77
|
+
required.push(settings[:search])
|
78
|
+
end
|
79
|
+
else
|
80
|
+
settings[:search].each do |t|
|
81
|
+
opt, req, neg = parse_search(t, settings[:negate])
|
82
|
+
optional.concat(opt)
|
83
|
+
required.concat(req)
|
84
|
+
negated.concat(neg)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
files = if !settings[:file_path].nil?
|
90
|
+
[settings[:file_path]]
|
91
|
+
elsif settings[:query].nil?
|
92
|
+
NA.find_files(depth: settings[:depth])
|
93
|
+
else
|
94
|
+
NA.match_working_dir(settings[:query])
|
95
|
+
end
|
96
|
+
|
97
|
+
files.each do |file|
|
98
|
+
NA.save_working_dir(File.expand_path(file))
|
99
|
+
content = file.read_file
|
100
|
+
indent_level = 0
|
101
|
+
parent = []
|
102
|
+
in_action = false
|
103
|
+
content.split(/\n/).each.with_index do |line, idx|
|
104
|
+
if line.project?
|
105
|
+
in_action = false
|
106
|
+
proj = line.project
|
107
|
+
indent = line.indent_level
|
108
|
+
|
109
|
+
if indent.zero? # top level project
|
110
|
+
parent = [proj]
|
111
|
+
elsif indent <= indent_level # if indent level is same or less, split parent before indent level and append
|
112
|
+
parent.slice!(indent, parent.count - indent)
|
113
|
+
parent.push(proj)
|
114
|
+
else # if indent level is greater, append project to parent
|
115
|
+
parent.push(proj)
|
116
|
+
end
|
117
|
+
|
118
|
+
projects.push(NA::Project.new(parent.join(':'), indent, idx, idx))
|
119
|
+
|
120
|
+
indent_level = indent
|
121
|
+
elsif line.blank?
|
122
|
+
in_action = false
|
123
|
+
elsif line.action?
|
124
|
+
in_action = false
|
125
|
+
|
126
|
+
action = line.action
|
127
|
+
new_action = NA::Action.new(file, File.basename(file, ".#{NA.extension}"), parent.dup, action, idx)
|
128
|
+
|
129
|
+
projects[-1].last_line = idx if projects.count.positive?
|
130
|
+
|
131
|
+
next if line.done? && !settings[:done]
|
132
|
+
|
133
|
+
next if settings[:require_na] && !line.na?
|
134
|
+
|
135
|
+
has_search = !optional.empty? || !required.empty? || !negated.empty?
|
136
|
+
|
137
|
+
next if has_search && !new_action.search_match?(any: optional,
|
138
|
+
all: required,
|
139
|
+
none: negated)
|
140
|
+
|
141
|
+
if settings[:project]
|
142
|
+
rx = settings[:project].split(%r{[/:]}).join('.*?/')
|
143
|
+
next unless parent.join('/') =~ Regexp.new("#{rx}.*?", Regexp::IGNORECASE)
|
144
|
+
end
|
145
|
+
|
146
|
+
has_tag = !optional_tag.empty? || !required_tag.empty? || !negated_tag.empty?
|
147
|
+
next if has_tag && !new_action.tags_match?(any: optional_tag,
|
148
|
+
all: required_tag,
|
149
|
+
none: negated_tag)
|
150
|
+
|
151
|
+
actions.push(new_action)
|
152
|
+
in_action = true
|
153
|
+
elsif in_action
|
154
|
+
actions[-1].note.push(line.strip) if actions.count.positive?
|
155
|
+
projects[-1].last_line = idx if projects.count.positive?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
projects = projects.dup
|
159
|
+
end
|
160
|
+
|
161
|
+
[files, actions, projects]
|
162
|
+
end
|
163
|
+
|
164
|
+
def parse_search(tag, negate)
|
165
|
+
required = []
|
166
|
+
optional = []
|
167
|
+
negated = []
|
168
|
+
new_rx = tag[:token].to_s.wildcard_to_rx
|
169
|
+
|
170
|
+
if negate
|
171
|
+
optional.push(new_rx) if tag[:negate]
|
172
|
+
required.push(new_rx) if tag[:required] && tag[:negate]
|
173
|
+
negated.push(new_rx) unless tag[:negate]
|
174
|
+
else
|
175
|
+
optional.push(new_rx) unless tag[:negate]
|
176
|
+
required.push(new_rx) if tag[:required] && !tag[:negate]
|
177
|
+
negated.push(new_rx) if tag[:negate]
|
178
|
+
end
|
179
|
+
|
180
|
+
[optional, required, negated]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
data/lib/na/version.rb
CHANGED
data/lib/na.rb
CHANGED
@@ -13,7 +13,11 @@ require 'na/hash'
|
|
13
13
|
require 'na/colors'
|
14
14
|
require 'na/string'
|
15
15
|
require 'na/array'
|
16
|
+
require 'na/theme'
|
17
|
+
require 'na/todo'
|
18
|
+
require 'na/actions'
|
16
19
|
require 'na/project'
|
17
20
|
require 'na/action'
|
21
|
+
require 'na/editor'
|
18
22
|
require 'na/next_action'
|
19
23
|
require 'na/prompt'
|
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.37<!--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
|
|
@@ -209,6 +209,50 @@ See the help output for a list of all available actions.
|
|
209
209
|
@cli(bundle exec bin/na help update)
|
210
210
|
```
|
211
211
|
|
212
|
+
##### changelog
|
213
|
+
|
214
|
+
View recent changes with `na changelog` or `na changes`.
|
215
|
+
|
216
|
+
```
|
217
|
+
@cli(bundle exec bin/na help changelog)
|
218
|
+
```
|
219
|
+
|
220
|
+
##### complete
|
221
|
+
|
222
|
+
Mark an action as complete, shortcut for `na update --finish`.
|
223
|
+
|
224
|
+
```
|
225
|
+
@cli(bundle exec bin/na help complete)
|
226
|
+
```
|
227
|
+
|
228
|
+
##### archive
|
229
|
+
|
230
|
+
Mark an action as complete and move to archive, shortcut for `na update --archive`.
|
231
|
+
|
232
|
+
```
|
233
|
+
@cli(bundle exec bin/na help archive)
|
234
|
+
```
|
235
|
+
|
236
|
+
##### tag
|
237
|
+
|
238
|
+
Add, remove, or modify tags.
|
239
|
+
|
240
|
+
Use `na tag TAGNAME --[search|tagged] SEARCH_STRING` to add a tag to matching action (use `--all` to apply to all matching actions). If you use `!TAGNAME` it will remove that tag (regardless of value). To change the value of an existing tag (or add it if it doesn't exist), use `~TAGNAME(NEW VALUE)`.
|
241
|
+
|
242
|
+
```
|
243
|
+
@cli(bundle exec bin/na help tag)
|
244
|
+
```
|
245
|
+
|
246
|
+
##### undo
|
247
|
+
|
248
|
+
Undoes the last file change resulting from an add or update command. If no argument is given, it undoes whatever the last change in history was. If an argument is provided, it's used to match against the change history, finding a specific file to restore from backup.
|
249
|
+
|
250
|
+
Only the most recent change can be undone.
|
251
|
+
|
252
|
+
```
|
253
|
+
@cli(bundle exec bin/na help undo)
|
254
|
+
```
|
255
|
+
|
212
256
|
### Configuration
|
213
257
|
|
214
258
|
Global options such as todo extension and default next action tag can be stored permanently by using the `na initconfig` command. Run na with the global options you'd like to set, and add `initconfig` at the end of the command. A file will be written to `~/.na.rc`. You can edit this manually, or just update it using the `initconfig --force` command to overwrite it with new settings.
|
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.38
|
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-
|
11
|
+
date: 2023-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -222,12 +222,15 @@ files:
|
|
222
222
|
- bin/commands/saved.rb
|
223
223
|
- bin/commands/tagged.rb
|
224
224
|
- bin/commands/todos.rb
|
225
|
+
- bin/commands/undo.rb
|
225
226
|
- bin/commands/update.rb
|
226
227
|
- bin/na
|
227
228
|
- lib/na.rb
|
228
229
|
- lib/na/action.rb
|
230
|
+
- lib/na/actions.rb
|
229
231
|
- lib/na/array.rb
|
230
232
|
- lib/na/colors.rb
|
233
|
+
- lib/na/editor.rb
|
231
234
|
- lib/na/hash.rb
|
232
235
|
- lib/na/help_monkey_patch.rb
|
233
236
|
- lib/na/next_action.rb
|
@@ -235,6 +238,8 @@ files:
|
|
235
238
|
- lib/na/project.rb
|
236
239
|
- lib/na/prompt.rb
|
237
240
|
- lib/na/string.rb
|
241
|
+
- lib/na/theme.rb
|
242
|
+
- lib/na/todo.rb
|
238
243
|
- lib/na/version.rb
|
239
244
|
- na.gemspec
|
240
245
|
- na.rdoc
|