na 1.2.86 → 1.2.87
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.
- checksums.yaml +4 -4
- data/.cursor/commands/changelog.md +4 -0
- data/.rubocop_todo.yml +22 -17
- data/CHANGELOG.md +58 -1
- data/Gemfile +7 -1
- data/Gemfile.lock +37 -1
- data/README.md +66 -2
- data/Rakefile +78 -78
- data/bin/commands/add.rb +31 -1
- data/bin/commands/changes.rb +1 -0
- data/bin/commands/complete.rb +11 -0
- data/bin/commands/find.rb +9 -1
- data/bin/commands/next.rb +35 -2
- data/bin/commands/tagged.rb +91 -58
- data/bin/commands/update.rb +36 -4
- data/bin/na +6 -0
- data/lib/na/action.rb +26 -3
- data/lib/na/actions.rb +136 -6
- data/lib/na/next_action.rb +88 -31
- data/lib/na/string.rb +9 -2
- data/lib/na/theme.rb +1 -0
- data/lib/na/types.rb +190 -0
- data/lib/na/version.rb +1 -1
- data/lib/na.rb +1 -0
- data/src/_README.md +44 -1
- metadata +3 -1
data/Rakefile
CHANGED
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
require
|
|
1
|
+
require 'rake/clean'
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'rubygems/package_task'
|
|
4
|
+
require 'rdoc/task'
|
|
5
|
+
require 'bump/tasks'
|
|
6
|
+
require 'bundler/gem_tasks'
|
|
7
|
+
require 'rspec/core/rake_task'
|
|
8
|
+
require 'rubocop/rake_task'
|
|
9
|
+
require 'yard'
|
|
10
|
+
require 'tty-spinner'
|
|
11
|
+
require 'English'
|
|
12
12
|
|
|
13
13
|
YARD::Rake::YardocTask.new do |t|
|
|
14
|
-
t.files = [
|
|
15
|
-
t.options = [
|
|
16
|
-
t.stats_options = [
|
|
14
|
+
t.files = ['lib/na/*.rb']
|
|
15
|
+
t.options = ['--markup-provider=redcarpet', '--markup=markdown', '--no-private', '-p', 'yard_templates']
|
|
16
|
+
t.stats_options = ['--list-undoc'] # Uncommented this line for stats options
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
## Docker error class
|
|
20
20
|
class DockerError < StandardError
|
|
21
21
|
def initialize(msg = nil)
|
|
22
|
-
msg = msg ? "Docker error: #{msg}" :
|
|
23
|
-
super
|
|
22
|
+
msg = msg ? "Docker error: #{msg}" : 'Docker error'
|
|
23
|
+
super
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
task default: %i[test yard]
|
|
28
28
|
|
|
29
|
-
desc
|
|
29
|
+
desc 'Run test suite'
|
|
30
30
|
task test: %i[rubocop spec]
|
|
31
31
|
|
|
32
32
|
RSpec::Core::RakeTask.new do |t|
|
|
33
|
-
t.rspec_opts =
|
|
33
|
+
t.rspec_opts = '--format documentation'
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
RuboCop::RakeTask.new do |t|
|
|
37
|
-
t.formatters = [
|
|
37
|
+
t.formatters = ['progress']
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
task :doc, [*Rake.application[:yard].arg_names] => [:yard]
|
|
41
41
|
|
|
42
42
|
Rake::RDocTask.new do |rd|
|
|
43
|
-
rd.main =
|
|
44
|
-
rd.rdoc_files.include(
|
|
45
|
-
rd.title =
|
|
43
|
+
rd.main = 'README.rdoc'
|
|
44
|
+
rd.rdoc_files.include('README.rdoc', 'lib/**/*.rb', 'bin/**/*')
|
|
45
|
+
rd.title = 'na'
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
spec = eval(File.read(
|
|
48
|
+
spec = eval(File.read('na.gemspec'))
|
|
49
49
|
|
|
50
50
|
Gem::PackageTask.new(spec) do |pkg|
|
|
51
51
|
end
|
|
52
|
-
require
|
|
52
|
+
require 'rake/testtask'
|
|
53
53
|
Rake::TestTask.new do |t|
|
|
54
|
-
t.libs <<
|
|
55
|
-
t.test_files = FileList[
|
|
54
|
+
t.libs << 'test'
|
|
55
|
+
t.test_files = FileList['test/*_test.rb']
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
desc
|
|
58
|
+
desc 'Install current gem in all versions of asdf-controlled ruby'
|
|
59
59
|
task :install do
|
|
60
|
-
Rake::Task[
|
|
61
|
-
Rake::Task[
|
|
62
|
-
Dir.chdir
|
|
63
|
-
file = Dir.glob(
|
|
60
|
+
Rake::Task['clobber'].invoke
|
|
61
|
+
Rake::Task['package'].invoke
|
|
62
|
+
Dir.chdir 'pkg'
|
|
63
|
+
file = Dir.glob('*.gem').last
|
|
64
64
|
|
|
65
65
|
current_ruby = `asdf current ruby`.match(/(\d.\d+.\d+)/)[1]
|
|
66
66
|
|
|
67
|
-
`asdf list ruby`.split.map { |ruby| ruby.strip.sub(/^*/,
|
|
67
|
+
`asdf list ruby`.split.map { |ruby| ruby.strip.sub(/^*/, '') }.each do |ruby|
|
|
68
68
|
`asdf shell ruby #{ruby}`
|
|
69
69
|
puts `gem install #{file}`
|
|
70
70
|
end
|
|
@@ -72,10 +72,10 @@ task :install do
|
|
|
72
72
|
`asdf shell ruby #{current_ruby}`
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
desc
|
|
75
|
+
desc 'Development version check'
|
|
76
76
|
task :ver do
|
|
77
77
|
gver = `git ver`
|
|
78
|
-
cver = IO.read(File.join(File.dirname(__FILE__),
|
|
78
|
+
cver = IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
|
|
79
79
|
res = `grep VERSION lib/na/version.rb`
|
|
80
80
|
version = res.match(/VERSION *= *['"](\d+\.\d+\.\d+(\w+)?)/)[1]
|
|
81
81
|
puts "git tag: #{gver}"
|
|
@@ -83,22 +83,22 @@ task :ver do
|
|
|
83
83
|
puts "changelog: #{cver}"
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
desc
|
|
86
|
+
desc 'Changelog version check'
|
|
87
87
|
task :cver do
|
|
88
|
-
puts IO.read(File.join(File.dirname(__FILE__),
|
|
88
|
+
puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
desc
|
|
91
|
+
desc 'Bump incremental version number'
|
|
92
92
|
task :bump, :type do |_, args|
|
|
93
|
-
args.with_defaults(type:
|
|
94
|
-
version_file =
|
|
93
|
+
args.with_defaults(type: 'inc')
|
|
94
|
+
version_file = 'lib/na/version.rb'
|
|
95
95
|
content = IO.read(version_file)
|
|
96
96
|
content.sub!(/VERSION = '(?<major>\d+)\.(?<minor>\d+)\.(?<inc>\d+)(?<pre>\S+)?'/) do
|
|
97
97
|
m = Regexp.last_match
|
|
98
|
-
major = m[
|
|
99
|
-
minor = m[
|
|
100
|
-
inc = m[
|
|
101
|
-
pre = m[
|
|
98
|
+
major = m['major'].to_i
|
|
99
|
+
minor = m['minor'].to_i
|
|
100
|
+
inc = m['inc'].to_i
|
|
101
|
+
pre = m['pre']
|
|
102
102
|
|
|
103
103
|
case args[:type]
|
|
104
104
|
when /^maj/
|
|
@@ -115,73 +115,73 @@ task :bump, :type do |_, args|
|
|
|
115
115
|
$stdout.puts "At version #{major}.#{minor}.#{inc}#{pre}"
|
|
116
116
|
"VERSION = '#{major}.#{minor}.#{inc}#{pre}'"
|
|
117
117
|
end
|
|
118
|
-
File.open(version_file,
|
|
118
|
+
File.open(version_file, 'w+') { |f| f.puts content }
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
# task default: %i[test clobber package]
|
|
122
122
|
|
|
123
|
-
desc
|
|
123
|
+
desc 'Remove packages'
|
|
124
124
|
task :clobber_packages do
|
|
125
|
-
FileUtils.rm_f
|
|
125
|
+
FileUtils.rm_f 'pkg/*'
|
|
126
126
|
end
|
|
127
127
|
# Make a prerequisite of the preexisting clobber task
|
|
128
|
-
desc
|
|
128
|
+
desc 'Clobber files'
|
|
129
129
|
task clobber: :clobber_packages
|
|
130
130
|
|
|
131
|
-
desc
|
|
131
|
+
desc 'Get Script Version'
|
|
132
132
|
task :sver do
|
|
133
133
|
res = `grep VERSION lib/na/version.rb`
|
|
134
134
|
version = res.match(/VERSION *= *['"](\d+\.\d+\.\d+(\w+)?)/)[1]
|
|
135
135
|
print version
|
|
136
136
|
end
|
|
137
137
|
|
|
138
|
-
desc
|
|
138
|
+
desc 'Run tests in Docker'
|
|
139
139
|
task :dockertest, :version, :login, :attempt do |_, args|
|
|
140
|
-
args.with_defaults(version:
|
|
140
|
+
args.with_defaults(version: 'all', login: false, attempt: 1)
|
|
141
141
|
`open -a Docker`
|
|
142
142
|
|
|
143
|
-
Rake::Task[
|
|
144
|
-
Rake::Task[
|
|
145
|
-
Rake::Task[
|
|
146
|
-
Rake::Task[
|
|
143
|
+
Rake::Task['clobber'].reenable
|
|
144
|
+
Rake::Task['clobber'].invoke
|
|
145
|
+
Rake::Task['build'].reenable
|
|
146
|
+
Rake::Task['build'].invoke
|
|
147
147
|
|
|
148
148
|
case args[:version]
|
|
149
149
|
when /^a/
|
|
150
150
|
%w[6 7 3].each do |v|
|
|
151
|
-
Rake::Task[
|
|
152
|
-
Rake::Task[
|
|
151
|
+
Rake::Task['dockertest'].reenable
|
|
152
|
+
Rake::Task['dockertest'].invoke(v, false)
|
|
153
153
|
end
|
|
154
154
|
Process.exit 0
|
|
155
155
|
when /^3\.?3/
|
|
156
|
-
img =
|
|
157
|
-
file =
|
|
156
|
+
img = 'natest33'
|
|
157
|
+
file = 'docker/Dockerfile-3.3'
|
|
158
158
|
when /^3/
|
|
159
|
-
version =
|
|
160
|
-
img =
|
|
161
|
-
file =
|
|
159
|
+
version = '3.0'
|
|
160
|
+
img = 'natest3'
|
|
161
|
+
file = 'docker/Dockerfile-3.0'
|
|
162
162
|
when /6$/
|
|
163
|
-
version =
|
|
164
|
-
img =
|
|
165
|
-
file =
|
|
163
|
+
version = '2.6'
|
|
164
|
+
img = 'natest26'
|
|
165
|
+
file = 'docker/Dockerfile-2.6'
|
|
166
166
|
when /(^2|7$)/
|
|
167
|
-
version =
|
|
168
|
-
img =
|
|
169
|
-
file =
|
|
167
|
+
version = '2.7'
|
|
168
|
+
img = 'natest27'
|
|
169
|
+
file = 'docker/Dockerfile-2.7'
|
|
170
170
|
else
|
|
171
|
-
version =
|
|
172
|
-
img =
|
|
173
|
-
file =
|
|
171
|
+
version = '3.0.1'
|
|
172
|
+
img = 'natest'
|
|
173
|
+
file = 'docker/Dockerfile'
|
|
174
174
|
end
|
|
175
175
|
|
|
176
176
|
puts `docker build . --file #{file} -t #{img}`
|
|
177
177
|
|
|
178
|
-
raise DockerError,
|
|
178
|
+
raise DockerError, 'Error building docker image' unless $CHILD_STATUS.success?
|
|
179
179
|
|
|
180
180
|
dirs = {
|
|
181
|
-
File.dirname(__FILE__) =>
|
|
182
|
-
File.expand_path(
|
|
181
|
+
File.dirname(__FILE__) => '/na',
|
|
182
|
+
File.expand_path('~/.config') => '/root/.config'
|
|
183
183
|
}
|
|
184
|
-
dir_args = dirs.map { |s, d| " -v '#{s}:#{d}'" }.join(
|
|
184
|
+
dir_args = dirs.map { |s, d| " -v '#{s}:#{d}'" }.join(' ')
|
|
185
185
|
exec "docker run #{dir_args} -it #{img} /bin/bash -l" if args[:login]
|
|
186
186
|
|
|
187
187
|
spinner = TTY::Spinner.new("[:spinner] Running tests (#{version})...", hide_cursor: true)
|
|
@@ -197,15 +197,15 @@ task :dockertest, :version, :login, :attempt do |_, args|
|
|
|
197
197
|
# puts res
|
|
198
198
|
# puts commit&.empty? ? "Error commiting Docker tag #{img}" : "Committed Docker tag #{img}"
|
|
199
199
|
rescue DockerError
|
|
200
|
-
raise StandardError.new(
|
|
200
|
+
raise StandardError.new('Docker not responding') if args[:attempt] > 3
|
|
201
201
|
|
|
202
202
|
`open -a Docker`
|
|
203
203
|
sleep 3
|
|
204
|
-
Rake::Task[
|
|
205
|
-
Rake::Task[
|
|
204
|
+
Rake::Task['dockertest'].reenable
|
|
205
|
+
Rake::Task['dockertest'].invoke(args[:version], args[:login], args[:attempt] + 1)
|
|
206
206
|
end
|
|
207
207
|
|
|
208
|
-
desc
|
|
208
|
+
desc 'alias for build'
|
|
209
209
|
task package: :build
|
|
210
210
|
|
|
211
211
|
desc 'Run tests with coverage'
|
data/bin/commands/add.rb
CHANGED
|
@@ -11,6 +11,17 @@ class App
|
|
|
11
11
|
allow you to pick to which file the action gets added.'
|
|
12
12
|
arg_name "ACTION"
|
|
13
13
|
command :add do |c|
|
|
14
|
+
c.desc "Started time (natural language or ISO)"
|
|
15
|
+
c.arg_name "DATE"
|
|
16
|
+
c.flag %i[started], type: :date_begin
|
|
17
|
+
|
|
18
|
+
c.desc "End/Finished time (natural language or ISO)"
|
|
19
|
+
c.arg_name "DATE"
|
|
20
|
+
c.flag %i[end finished], type: :date_end
|
|
21
|
+
|
|
22
|
+
c.desc "Duration (e.g. 45m, 2h, 1d2h30m, or minutes)"
|
|
23
|
+
c.arg_name "DURATION"
|
|
24
|
+
c.flag %i[duration], type: :duration
|
|
14
25
|
c.example 'na add "A cool feature I thought of @idea"', desc: "Add a new action to the Inbox, including a tag"
|
|
15
26
|
c.example 'na add "A bug I need to fix" -p 4 -n',
|
|
16
27
|
desc: "Add a new action to the Inbox, set its @priority to 4, and prompt for an additional note."
|
|
@@ -181,7 +192,26 @@ class App
|
|
|
181
192
|
note.<< split_note unless split_note.nil?
|
|
182
193
|
note.concat(line_note) unless line_note.nil?
|
|
183
194
|
|
|
184
|
-
|
|
195
|
+
# Compute started/done based on flags
|
|
196
|
+
started_at = options[:started]
|
|
197
|
+
started_at = NA::Types.parse_date_begin(started_at) if started_at && !started_at.is_a?(Time)
|
|
198
|
+
done_at = options[:end] || options[:finished]
|
|
199
|
+
done_at = NA::Types.parse_date_end(done_at) if done_at && !done_at.is_a?(Time)
|
|
200
|
+
duration_seconds = options[:duration]
|
|
201
|
+
|
|
202
|
+
NA.notify("ADD parsed started_at=#{started_at.inspect} done_at=#{done_at.inspect} duration=#{duration_seconds.inspect}", debug: true)
|
|
203
|
+
|
|
204
|
+
# Ensure @started is present in the action text if a start time was provided
|
|
205
|
+
if started_at
|
|
206
|
+
started_str = started_at.strftime('%Y-%m-%d %H:%M')
|
|
207
|
+
# remove any existing @start/@started tag before appending
|
|
208
|
+
action = action.gsub(/(?<=\A| )@start(?:ed)?\(.*?\)/i, '').strip
|
|
209
|
+
action = "#{action} @started(#{started_str})"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
NA.add_action(target, options[:project], action, note,
|
|
213
|
+
finish: options[:finish], append: append,
|
|
214
|
+
started_at: started_at, done_at: done_at, duration_seconds: duration_seconds)
|
|
185
215
|
end
|
|
186
216
|
end
|
|
187
217
|
end
|
data/bin/commands/changes.rb
CHANGED
data/bin/commands/complete.rb
CHANGED
|
@@ -5,6 +5,17 @@ class App
|
|
|
5
5
|
desc 'Find and mark an action as @done'
|
|
6
6
|
arg_name 'ACTION'
|
|
7
7
|
command %i[complete finish] do |c|
|
|
8
|
+
c.desc 'Started time (natural language or ISO)'
|
|
9
|
+
c.arg_name 'DATE'
|
|
10
|
+
c.flag %i[started], type: :date_begin
|
|
11
|
+
|
|
12
|
+
c.desc 'End/Finished time (natural language or ISO)'
|
|
13
|
+
c.arg_name 'DATE'
|
|
14
|
+
c.flag %i[end finished], type: :date_end
|
|
15
|
+
|
|
16
|
+
c.desc 'Duration (e.g. 45m, 2h, 1d2h30m, or minutes)'
|
|
17
|
+
c.arg_name 'DURATION'
|
|
18
|
+
c.flag %i[duration], type: :duration
|
|
8
19
|
c.example 'na complete "An existing task"',
|
|
9
20
|
desc: 'Find "An existing task" and mark @done'
|
|
10
21
|
c.example 'na finish "An existing task"',
|
data/bin/commands/find.rb
CHANGED
|
@@ -29,6 +29,12 @@ class App
|
|
|
29
29
|
c.desc "Include notes in output"
|
|
30
30
|
c.switch %i[notes], negatable: true, default_value: false
|
|
31
31
|
|
|
32
|
+
c.desc "Show per-action durations and total"
|
|
33
|
+
c.switch %i[times], negatable: false
|
|
34
|
+
|
|
35
|
+
c.desc "Format durations in human-friendly form"
|
|
36
|
+
c.switch %i[human], negatable: false
|
|
37
|
+
|
|
32
38
|
c.desc "Include notes in search"
|
|
33
39
|
c.switch %i[search_notes], negatable: true, default_value: true
|
|
34
40
|
|
|
@@ -175,7 +181,9 @@ class App
|
|
|
175
181
|
notes: options[:notes],
|
|
176
182
|
nest: options[:nest],
|
|
177
183
|
nest_projects: options[:omnifocus],
|
|
178
|
-
no_files: options[:no_file]
|
|
184
|
+
no_files: options[:no_file],
|
|
185
|
+
times: options[:times],
|
|
186
|
+
human: options[:human] })
|
|
179
187
|
end
|
|
180
188
|
end
|
|
181
189
|
end
|
data/bin/commands/next.rb
CHANGED
|
@@ -65,6 +65,21 @@ class App
|
|
|
65
65
|
c.desc "Include notes in output"
|
|
66
66
|
c.switch %i[notes], negatable: true, default_value: false
|
|
67
67
|
|
|
68
|
+
c.desc "Show per-action durations and total"
|
|
69
|
+
c.switch %i[times], negatable: false
|
|
70
|
+
|
|
71
|
+
c.desc "Format durations in human-friendly form"
|
|
72
|
+
c.switch %i[human], negatable: false
|
|
73
|
+
|
|
74
|
+
c.desc "Show only actions that have a duration (@started and @done)"
|
|
75
|
+
c.switch %i[only_timed], negatable: false
|
|
76
|
+
|
|
77
|
+
c.desc "Output times as JSON object (implies --times and --done)"
|
|
78
|
+
c.switch %i[json_times], negatable: false
|
|
79
|
+
|
|
80
|
+
c.desc "Output only elapsed time totals (implies --times and --done)"
|
|
81
|
+
c.switch %i[only_times], negatable: false
|
|
82
|
+
|
|
68
83
|
c.desc "Include @done actions"
|
|
69
84
|
c.switch %i[done]
|
|
70
85
|
|
|
@@ -189,7 +204,20 @@ class App
|
|
|
189
204
|
end
|
|
190
205
|
end
|
|
191
206
|
|
|
192
|
-
|
|
207
|
+
if options[:json_times]
|
|
208
|
+
options[:times] = true
|
|
209
|
+
options[:done] = true
|
|
210
|
+
elsif options[:only_times]
|
|
211
|
+
options[:times] = true
|
|
212
|
+
options[:done] = true
|
|
213
|
+
elsif options[:only_timed]
|
|
214
|
+
options[:times] = true
|
|
215
|
+
options[:done] = true
|
|
216
|
+
elsif options[:times]
|
|
217
|
+
options[:done] = true
|
|
218
|
+
else
|
|
219
|
+
options[:done] = true if tags.any? { |tag| tag[:tag] =~ /done/ }
|
|
220
|
+
end
|
|
193
221
|
|
|
194
222
|
search_tokens = nil
|
|
195
223
|
if options[:exact]
|
|
@@ -239,7 +267,12 @@ class App
|
|
|
239
267
|
nest: options[:nest],
|
|
240
268
|
nest_projects: options[:omnifocus],
|
|
241
269
|
notes: options[:notes],
|
|
242
|
-
no_files: options[:no_file]
|
|
270
|
+
no_files: options[:no_file],
|
|
271
|
+
times: options[:times],
|
|
272
|
+
human: options[:human],
|
|
273
|
+
only_timed: options[:only_timed],
|
|
274
|
+
json_times: options[:json_times],
|
|
275
|
+
only_times: options[:only_times] })
|
|
243
276
|
end
|
|
244
277
|
end
|
|
245
278
|
end
|