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.
data/Rakefile CHANGED
@@ -1,70 +1,70 @@
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"
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 = ["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
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}" : "Docker error"
23
- super(msg)
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 "Run test suite"
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 = "--format documentation"
33
+ t.rspec_opts = '--format documentation'
34
34
  end
35
35
 
36
36
  RuboCop::RakeTask.new do |t|
37
- t.formatters = ["progress"]
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 = "README.rdoc"
44
- rd.rdoc_files.include("README.rdoc", "lib/**/*.rb", "bin/**/*")
45
- rd.title = "na"
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("na.gemspec"))
48
+ spec = eval(File.read('na.gemspec'))
49
49
 
50
50
  Gem::PackageTask.new(spec) do |pkg|
51
51
  end
52
- require "rake/testtask"
52
+ require 'rake/testtask'
53
53
  Rake::TestTask.new do |t|
54
- t.libs << "test"
55
- t.test_files = FileList["test/*_test.rb"]
54
+ t.libs << 'test'
55
+ t.test_files = FileList['test/*_test.rb']
56
56
  end
57
57
 
58
- desc "Install current gem in all versions of asdf-controlled ruby"
58
+ desc 'Install current gem in all versions of asdf-controlled ruby'
59
59
  task :install do
60
- Rake::Task["clobber"].invoke
61
- Rake::Task["package"].invoke
62
- Dir.chdir "pkg"
63
- file = Dir.glob("*.gem").last
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(/^*/, "") }.each do |ruby|
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 "Development version check"
75
+ desc 'Development version check'
76
76
  task :ver do
77
77
  gver = `git ver`
78
- cver = IO.read(File.join(File.dirname(__FILE__), "CHANGELOG.md")).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
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 "Changelog version check"
86
+ desc 'Changelog version check'
87
87
  task :cver do
88
- puts IO.read(File.join(File.dirname(__FILE__), "CHANGELOG.md")).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
88
+ puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
89
89
  end
90
90
 
91
- desc "Bump incremental version number"
91
+ desc 'Bump incremental version number'
92
92
  task :bump, :type do |_, args|
93
- args.with_defaults(type: "inc")
94
- version_file = "lib/na/version.rb"
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["major"].to_i
99
- minor = m["minor"].to_i
100
- inc = m["inc"].to_i
101
- pre = m["pre"]
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, "w+") { |f| f.puts content }
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 "Remove packages"
123
+ desc 'Remove packages'
124
124
  task :clobber_packages do
125
- FileUtils.rm_f "pkg/*"
125
+ FileUtils.rm_f 'pkg/*'
126
126
  end
127
127
  # Make a prerequisite of the preexisting clobber task
128
- desc "Clobber files"
128
+ desc 'Clobber files'
129
129
  task clobber: :clobber_packages
130
130
 
131
- desc "Get Script Version"
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 "Run tests in Docker"
138
+ desc 'Run tests in Docker'
139
139
  task :dockertest, :version, :login, :attempt do |_, args|
140
- args.with_defaults(version: "all", login: false, attempt: 1)
140
+ args.with_defaults(version: 'all', login: false, attempt: 1)
141
141
  `open -a Docker`
142
142
 
143
- Rake::Task["clobber"].reenable
144
- Rake::Task["clobber"].invoke
145
- Rake::Task["build"].reenable
146
- Rake::Task["build"].invoke
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["dockertest"].reenable
152
- Rake::Task["dockertest"].invoke(v, false)
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 = "natest33"
157
- file = "docker/Dockerfile-3.3"
156
+ img = 'natest33'
157
+ file = 'docker/Dockerfile-3.3'
158
158
  when /^3/
159
- version = "3.0"
160
- img = "natest3"
161
- file = "docker/Dockerfile-3.0"
159
+ version = '3.0'
160
+ img = 'natest3'
161
+ file = 'docker/Dockerfile-3.0'
162
162
  when /6$/
163
- version = "2.6"
164
- img = "natest26"
165
- file = "docker/Dockerfile-2.6"
163
+ version = '2.6'
164
+ img = 'natest26'
165
+ file = 'docker/Dockerfile-2.6'
166
166
  when /(^2|7$)/
167
- version = "2.7"
168
- img = "natest27"
169
- file = "docker/Dockerfile-2.7"
167
+ version = '2.7'
168
+ img = 'natest27'
169
+ file = 'docker/Dockerfile-2.7'
170
170
  else
171
- version = "3.0.1"
172
- img = "natest"
173
- file = "docker/Dockerfile"
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, "Error building docker image" unless $CHILD_STATUS.success?
178
+ raise DockerError, 'Error building docker image' unless $CHILD_STATUS.success?
179
179
 
180
180
  dirs = {
181
- File.dirname(__FILE__) => "/na",
182
- File.expand_path("~/.config") => "/root/.config"
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("Docker not responding") if args[:attempt] > 3
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["dockertest"].reenable
205
- Rake::Task["dockertest"].invoke(args[:version], args[:login], args[:attempt] + 1)
204
+ Rake::Task['dockertest'].reenable
205
+ Rake::Task['dockertest'].invoke(args[:version], args[:login], args[:attempt] + 1)
206
206
  end
207
207
 
208
- desc "alias for build"
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
- NA.add_action(target, options[:project], action, note, finish: options[:finish], append: append)
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
@@ -9,6 +9,7 @@ class App
9
9
  pagers = [
10
10
  'mdless',
11
11
  'mdcat',
12
+ 'glow',
12
13
  'bat',
13
14
  ENV['PAGER'],
14
15
  'less -FXr',
@@ -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
- options[:done] = true if tags.any? { |tag| tag[:tag] =~ /done/ }
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