doing 2.0.23 → 2.1.1pre
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/.yardoc/checksums +19 -15
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +40 -1
- data/Gemfile.lock +8 -1
- data/README.md +7 -1
- data/Rakefile +23 -4
- data/bin/doing +431 -256
- data/doc/Array.html +354 -1
- data/doc/Doing/Color.html +104 -92
- data/doc/Doing/Completion.html +216 -0
- data/doc/Doing/Configuration.html +340 -5
- data/doc/Doing/Content.html +229 -0
- data/doc/Doing/Errors/DoingNoTraceError.html +1 -1
- data/doc/Doing/Errors/DoingRuntimeError.html +1 -1
- data/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/doc/Doing/Errors/EmptyInput.html +1 -1
- data/doc/Doing/Errors/NoResults.html +1 -1
- data/doc/Doing/Errors/PluginException.html +1 -1
- data/doc/Doing/Errors/UserCancelled.html +1 -1
- data/doc/Doing/Errors/WrongCommand.html +1 -1
- data/doc/Doing/Errors.html +1 -1
- data/doc/Doing/Hooks.html +1 -1
- data/doc/Doing/Item.html +337 -49
- data/doc/Doing/Items.html +444 -35
- data/doc/Doing/LogAdapter.html +139 -51
- data/doc/Doing/Note.html +253 -22
- data/doc/Doing/Pager.html +74 -36
- data/doc/Doing/Plugins.html +1 -1
- data/doc/Doing/Prompt.html +674 -0
- data/doc/Doing/Section.html +354 -0
- data/doc/Doing/Util.html +57 -1
- data/doc/Doing/WWID.html +517 -890
- data/doc/Doing/WWIDFile.html +398 -0
- data/doc/Doing.html +5 -5
- data/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/doc/GLI/Commands.html +1 -1
- data/doc/GLI.html +1 -1
- data/doc/Hash.html +97 -1
- data/doc/Status.html +37 -3
- data/doc/String.html +833 -53
- data/doc/Symbol.html +3 -3
- data/doc/Time.html +1 -1
- data/doc/_index.html +22 -1
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +8 -2
- data/doc/index.html +8 -2
- data/doc/method_list.html +460 -180
- data/doc/top-level-namespace.html +1 -1
- data/doing.gemspec +3 -0
- data/doing.rdoc +163 -44
- data/example_plugin.rb +5 -5
- data/lib/completion/_doing.zsh +42 -42
- data/lib/completion/doing.bash +21 -21
- data/lib/completion/doing.fish +1 -280
- data/lib/doing/array.rb +36 -0
- data/lib/doing/colors.rb +70 -66
- data/lib/doing/completion/bash_completion.rb +1 -2
- data/lib/doing/completion/fish_completion.rb +1 -1
- data/lib/doing/completion/zsh_completion.rb +1 -1
- data/lib/doing/completion.rb +6 -0
- data/lib/doing/configuration.rb +134 -23
- data/lib/doing/hash.rb +37 -0
- data/lib/doing/item.rb +77 -12
- data/lib/doing/items.rb +125 -0
- data/lib/doing/log_adapter.rb +58 -4
- data/lib/doing/note.rb +53 -1
- data/lib/doing/pager.rb +49 -38
- data/lib/doing/plugins/export/markdown_export.rb +4 -4
- data/lib/doing/plugins/export/template_export.rb +2 -2
- data/lib/doing/plugins/import/calendar_import.rb +4 -4
- data/lib/doing/plugins/import/doing_import.rb +5 -7
- data/lib/doing/plugins/import/timing_import.rb +3 -3
- data/lib/doing/prompt.rb +206 -0
- data/lib/doing/section.rb +30 -0
- data/lib/doing/string.rb +123 -35
- data/lib/doing/string_chronify.rb +81 -0
- data/lib/doing/util.rb +14 -6
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +387 -685
- data/lib/doing.rb +7 -2
- data/lib/examples/plugins/capture_thing_import.rb +162 -0
- data/rdoc_to_mmd.rb +14 -8
- data/scripts/generate_bash_completions.rb +1 -1
- data/scripts/generate_fish_completions.rb +1 -1
- data/scripts/generate_zsh_completions.rb +1 -1
- metadata +74 -5
- data/lib/doing/wwidfile.rb +0 -117
data/bin/doing
CHANGED
|
@@ -35,8 +35,10 @@ colors = Doing::Color
|
|
|
35
35
|
wwid = Doing::WWID.new
|
|
36
36
|
|
|
37
37
|
Doing.logger.log_level = :info
|
|
38
|
+
env_log_level = nil
|
|
38
39
|
|
|
39
40
|
if ENV['DOING_LOG_LEVEL'] || ENV['DOING_DEBUG'] || ENV['DOING_QUIET'] || ENV['DOING_VERBOSE'] || ENV['DOING_PLUGIN_DEBUG']
|
|
41
|
+
env_log_level = true
|
|
40
42
|
# Quiet always wins
|
|
41
43
|
if ENV['DOING_QUIET'] && ENV['DOING_QUIET'].truthy?
|
|
42
44
|
Doing.logger.log_level = :error
|
|
@@ -82,6 +84,12 @@ switch %i[p pager], default_value: settings['paginate']
|
|
|
82
84
|
desc 'Answer yes/no menus with default option'
|
|
83
85
|
switch [:default], default_value: false
|
|
84
86
|
|
|
87
|
+
desc 'Answer all yes/no menus with yes'
|
|
88
|
+
switch [:yes], negatable: false
|
|
89
|
+
|
|
90
|
+
desc 'Answer all yes/no menus with no'
|
|
91
|
+
switch [:no], negatable: false
|
|
92
|
+
|
|
85
93
|
desc 'Exclude auto tags and default tags'
|
|
86
94
|
switch %i[x noauto], default_value: false, negatable: false
|
|
87
95
|
|
|
@@ -121,9 +129,9 @@ command %i[now next] do |c|
|
|
|
121
129
|
c.desc "Edit entry with #{Doing::Util.default_editor}"
|
|
122
130
|
c.switch %i[e editor], negatable: false, default_value: false
|
|
123
131
|
|
|
124
|
-
c.desc 'Backdate start time [4pm|20m|2h|yesterday noon]'
|
|
132
|
+
c.desc 'Backdate start time [4pm|20m|2h|"yesterday noon"]'
|
|
125
133
|
c.arg_name 'DATE_STRING'
|
|
126
|
-
c.flag %i[b back]
|
|
134
|
+
c.flag %i[b back started]
|
|
127
135
|
|
|
128
136
|
c.desc 'Timed entry, marks last entry in section as @done'
|
|
129
137
|
c.switch %i[f finish_last], negatable: false, default_value: false
|
|
@@ -138,9 +146,9 @@ command %i[now next] do |c|
|
|
|
138
146
|
|
|
139
147
|
c.action do |_global_options, options, args|
|
|
140
148
|
if options[:back]
|
|
141
|
-
date =
|
|
149
|
+
date = options[:back].chronify(guess: :begin)
|
|
142
150
|
|
|
143
|
-
raise InvalidTimeExpression.new('unable to parse date string', topic: '
|
|
151
|
+
raise InvalidTimeExpression.new('unable to parse date string', topic: 'Parser:') if date.nil?
|
|
144
152
|
else
|
|
145
153
|
date = Time.now
|
|
146
154
|
end
|
|
@@ -151,29 +159,34 @@ command %i[now next] do |c|
|
|
|
151
159
|
options[:section] = settings['current_section']
|
|
152
160
|
end
|
|
153
161
|
|
|
154
|
-
if options[:
|
|
162
|
+
if options[:editor] || (args.empty? && $stdin.stat.size.zero?)
|
|
155
163
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
156
164
|
|
|
157
|
-
input = ''
|
|
165
|
+
input = date.strftime('%F %R | ')
|
|
158
166
|
input += args.join(' ') unless args.empty?
|
|
159
167
|
input = wwid.fork_editor(input).strip
|
|
160
168
|
|
|
161
169
|
raise EmptyInput, 'No content' if input.empty?
|
|
162
170
|
|
|
163
|
-
title, note = wwid.format_input(input)
|
|
164
|
-
note.
|
|
165
|
-
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:
|
|
171
|
+
date, title, note = wwid.format_input(input)
|
|
172
|
+
note.add(options[:note]) if options[:note]
|
|
173
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
166
174
|
wwid.write(wwid.doing_file)
|
|
167
175
|
elsif args.length.positive?
|
|
168
|
-
title, note = wwid.format_input(args.join(' '))
|
|
169
|
-
|
|
170
|
-
|
|
176
|
+
d, title, note = wwid.format_input(args.join(' '))
|
|
177
|
+
date = d.nil? ? date : d
|
|
178
|
+
note.add(options[:note]) if options[:note]
|
|
179
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
171
180
|
wwid.write(wwid.doing_file)
|
|
172
181
|
elsif $stdin.stat.size.positive?
|
|
173
|
-
input = $stdin.read
|
|
174
|
-
title, note = wwid.format_input(input)
|
|
175
|
-
|
|
176
|
-
|
|
182
|
+
input = $stdin.read.strip
|
|
183
|
+
d, title, note = wwid.format_input(input)
|
|
184
|
+
unless d.nil?
|
|
185
|
+
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
|
186
|
+
date = d
|
|
187
|
+
end
|
|
188
|
+
note.add(options[:note]) if options[:note]
|
|
189
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
177
190
|
wwid.write(wwid.doing_file)
|
|
178
191
|
else
|
|
179
192
|
raise EmptyInput, 'You must provide content when creating a new entry'
|
|
@@ -238,14 +251,13 @@ command %i[reset begin] do |c|
|
|
|
238
251
|
items = wwid.filter_items([], opt: options)
|
|
239
252
|
|
|
240
253
|
if options[:interactive]
|
|
241
|
-
last_entry =
|
|
254
|
+
last_entry = Doing::Prompt.choose_from_items(items, include_section: options[:section].nil?,
|
|
242
255
|
menu: true,
|
|
243
256
|
header: '',
|
|
244
257
|
prompt: 'Select an entry to start/reset > ',
|
|
245
258
|
multiple: false,
|
|
246
259
|
sort: false,
|
|
247
|
-
show_if_single: true
|
|
248
|
-
}, include_section: options[:section].nil? )
|
|
260
|
+
show_if_single: true)
|
|
249
261
|
else
|
|
250
262
|
last_entry = items.last
|
|
251
263
|
end
|
|
@@ -344,7 +356,7 @@ command :note do |c|
|
|
|
344
356
|
last_note = last_entry.note || Doing::Note.new
|
|
345
357
|
new_note = Doing::Note.new
|
|
346
358
|
|
|
347
|
-
if options[:
|
|
359
|
+
if options[:editor] || (args.empty? && $stdin.stat.size.zero? && !options[:remove])
|
|
348
360
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
349
361
|
|
|
350
362
|
input = !args.empty? ? args.join(' ') : ''
|
|
@@ -357,14 +369,14 @@ command :note do |c|
|
|
|
357
369
|
|
|
358
370
|
input = prev_input.add(input)
|
|
359
371
|
|
|
360
|
-
input = wwid.fork_editor(
|
|
361
|
-
|
|
372
|
+
input = wwid.fork_editor(prev_input.strip_lines.join("\n"), message: nil).strip
|
|
373
|
+
note = input
|
|
362
374
|
options[:remove] = true
|
|
363
375
|
new_note.add(note)
|
|
364
376
|
elsif !args.empty?
|
|
365
377
|
new_note.add(args.join(' '))
|
|
366
378
|
elsif $stdin.stat.size.positive?
|
|
367
|
-
new_note.add($stdin.read)
|
|
379
|
+
new_note.add($stdin.read.strip)
|
|
368
380
|
else
|
|
369
381
|
raise EmptyInput, 'You must provide content when adding a note' unless options[:remove]
|
|
370
382
|
end
|
|
@@ -409,7 +421,7 @@ command :meanwhile do |c|
|
|
|
409
421
|
|
|
410
422
|
c.action do |_global_options, options, args|
|
|
411
423
|
if options[:back]
|
|
412
|
-
date =
|
|
424
|
+
date = options[:back].chronify(guess: :begin)
|
|
413
425
|
|
|
414
426
|
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
|
415
427
|
else
|
|
@@ -423,31 +435,35 @@ command :meanwhile do |c|
|
|
|
423
435
|
end
|
|
424
436
|
input = ''
|
|
425
437
|
|
|
426
|
-
if options[:
|
|
438
|
+
if options[:editor]
|
|
427
439
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
428
|
-
|
|
440
|
+
input += date.strftime('%F %R | ')
|
|
429
441
|
input += args.join(' ') unless args.empty?
|
|
430
442
|
input = wwid.fork_editor(input).strip
|
|
431
443
|
elsif !args.empty?
|
|
432
444
|
input = args.join(' ')
|
|
433
445
|
elsif $stdin.stat.size.positive?
|
|
434
|
-
input = $stdin.read
|
|
446
|
+
input = $stdin.read.strip
|
|
435
447
|
end
|
|
436
448
|
|
|
437
449
|
if input && !input.empty?
|
|
438
|
-
input, note = wwid.format_input(input)
|
|
450
|
+
d, input, note = wwid.format_input(input)
|
|
451
|
+
unless d.nil?
|
|
452
|
+
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
|
453
|
+
date = d
|
|
454
|
+
end
|
|
439
455
|
else
|
|
440
456
|
input = nil
|
|
441
457
|
note = []
|
|
442
458
|
end
|
|
443
459
|
|
|
444
|
-
if options[:
|
|
445
|
-
note.push(options[:
|
|
460
|
+
if options[:note]
|
|
461
|
+
note.push(options[:note])
|
|
446
462
|
elsif note.empty?
|
|
447
463
|
note = nil
|
|
448
464
|
end
|
|
449
465
|
|
|
450
|
-
wwid.stop_start('meanwhile', { new_item: input, back: date, section: section, archive: options[:
|
|
466
|
+
wwid.stop_start('meanwhile', { new_item: input, back: date, section: section, archive: options[:archive], note: note })
|
|
451
467
|
wwid.write(wwid.doing_file)
|
|
452
468
|
end
|
|
453
469
|
end
|
|
@@ -465,11 +481,11 @@ command :template do |c|
|
|
|
465
481
|
c.switch %i[l list], negatable: false
|
|
466
482
|
|
|
467
483
|
c.desc 'List in single column for completion'
|
|
468
|
-
c.switch %i[c]
|
|
484
|
+
c.switch %i[c column]
|
|
469
485
|
|
|
470
486
|
c.action do |_global_options, options, args|
|
|
471
|
-
if options[:list] || options[:
|
|
472
|
-
if options[:
|
|
487
|
+
if options[:list] || options[:column]
|
|
488
|
+
if options[:column]
|
|
473
489
|
$stdout.print Doing::Plugins.plugin_templates.join("\n")
|
|
474
490
|
else
|
|
475
491
|
$stdout.puts "Available templates: #{Doing::Plugins.plugin_templates.join(', ')}"
|
|
@@ -478,7 +494,7 @@ command :template do |c|
|
|
|
478
494
|
end
|
|
479
495
|
|
|
480
496
|
if args.empty?
|
|
481
|
-
type =
|
|
497
|
+
type = Doing::Prompt.choose_from(Doing::Plugins.plugin_templates, sorted: false, prompt: 'Select template type > ')
|
|
482
498
|
else
|
|
483
499
|
type = args[0]
|
|
484
500
|
end
|
|
@@ -532,6 +548,25 @@ command :select do |c|
|
|
|
532
548
|
c.arg_name 'QUERY'
|
|
533
549
|
c.flag %i[q query search]
|
|
534
550
|
|
|
551
|
+
c.desc 'Select from entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.'
|
|
552
|
+
c.arg_name 'DATE_STRING'
|
|
553
|
+
c.flag [:before]
|
|
554
|
+
|
|
555
|
+
c.desc 'Select from entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.'
|
|
556
|
+
c.arg_name 'DATE_STRING'
|
|
557
|
+
c.flag [:after]
|
|
558
|
+
|
|
559
|
+
c.desc %(
|
|
560
|
+
Date range to show, or a single day to filter date on.
|
|
561
|
+
Date range argument should be quoted. Date specifications can be natural language.
|
|
562
|
+
To specify a range, use "to" or "through": `doing select --from "monday 8am to friday 5pm"`.
|
|
563
|
+
|
|
564
|
+
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
|
565
|
+
by time of day.
|
|
566
|
+
)
|
|
567
|
+
c.arg_name 'DATE_OR_RANGE'
|
|
568
|
+
c.flag [:from]
|
|
569
|
+
|
|
535
570
|
c.desc 'Force exact search string matching (case sensitive)'
|
|
536
571
|
c.switch %i[x exact], default_value: false, negatable: false
|
|
537
572
|
|
|
@@ -604,7 +639,7 @@ command :later do |c|
|
|
|
604
639
|
|
|
605
640
|
c.action do |_global_options, options, args|
|
|
606
641
|
if options[:back]
|
|
607
|
-
date =
|
|
642
|
+
date = options[:back].chronify(guess: :begin)
|
|
608
643
|
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
|
609
644
|
else
|
|
610
645
|
date = Time.now
|
|
@@ -613,22 +648,29 @@ command :later do |c|
|
|
|
613
648
|
if options[:editor] || (args.empty? && $stdin.stat.size.zero?)
|
|
614
649
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
615
650
|
|
|
616
|
-
input
|
|
651
|
+
input += date.strftime('%F %R | ')
|
|
652
|
+
input += args.empty? ? '' : args.join(' ')
|
|
617
653
|
input = wwid.fork_editor(input).strip
|
|
618
654
|
raise EmptyInput, 'No content' unless input && !input.empty?
|
|
619
655
|
|
|
620
|
-
title, note = wwid.format_input(input)
|
|
621
|
-
|
|
656
|
+
d, title, note = wwid.format_input(input)
|
|
657
|
+
date = d.nil? ? date : d
|
|
658
|
+
note.add(options[:note]) if options[:note]
|
|
622
659
|
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
|
623
660
|
wwid.write(wwid.doing_file)
|
|
624
661
|
elsif !args.empty?
|
|
625
|
-
title, note = wwid.format_input(args.join(' '))
|
|
626
|
-
|
|
662
|
+
d, title, note = wwid.format_input(args.join(' '))
|
|
663
|
+
date = d.nil? ? date : d
|
|
664
|
+
note.add(options[:note]) if options[:note]
|
|
627
665
|
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
|
628
666
|
wwid.write(wwid.doing_file)
|
|
629
667
|
elsif $stdin.stat.size.positive?
|
|
630
|
-
title, note = wwid.format_input($stdin.read)
|
|
631
|
-
|
|
668
|
+
d, title, note = wwid.format_input($stdin.read)
|
|
669
|
+
unless d.nil?
|
|
670
|
+
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
|
671
|
+
date = d
|
|
672
|
+
end
|
|
673
|
+
note.add(options[:note]) if options[:note]
|
|
632
674
|
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
|
633
675
|
wwid.write(wwid.doing_file)
|
|
634
676
|
else
|
|
@@ -659,9 +701,9 @@ command %i[done did] do |c|
|
|
|
659
701
|
c.arg_name 'DATE_STRING'
|
|
660
702
|
c.flag [:at]
|
|
661
703
|
|
|
662
|
-
c.desc 'Backdate start date by interval [4pm|20m|2h|yesterday noon]'
|
|
704
|
+
c.desc 'Backdate start date by interval or set to time [4pm|20m|2h|"yesterday noon"]'
|
|
663
705
|
c.arg_name 'DATE_STRING'
|
|
664
|
-
c.flag %i[b back]
|
|
706
|
+
c.flag %i[b back started]
|
|
665
707
|
|
|
666
708
|
c.desc %(Set completion date to start date plus interval (XX[mhd] or HH:MM).
|
|
667
709
|
If used without the --back option, the start date will be moved back to allow
|
|
@@ -692,26 +734,24 @@ command %i[done did] do |c|
|
|
|
692
734
|
donedate = nil
|
|
693
735
|
|
|
694
736
|
if options[:took]
|
|
695
|
-
took =
|
|
737
|
+
took = options[:took].chronify_qty
|
|
696
738
|
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
|
697
739
|
end
|
|
698
740
|
|
|
699
741
|
if options[:back]
|
|
700
|
-
date =
|
|
742
|
+
date = options[:back].chronify(guess: :begin)
|
|
701
743
|
raise InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
|
|
702
744
|
else
|
|
703
745
|
date = options[:took] ? Time.now - took : Time.now
|
|
704
746
|
end
|
|
705
747
|
|
|
706
748
|
if options[:at]
|
|
707
|
-
finish_date =
|
|
749
|
+
finish_date = options[:at].chronify(guess: :begin)
|
|
708
750
|
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
|
709
751
|
|
|
710
752
|
date = options[:took] ? finish_date - took : finish_date
|
|
711
753
|
elsif options[:took]
|
|
712
754
|
finish_date = date + took
|
|
713
|
-
elsif options[:back]
|
|
714
|
-
finish_date = date
|
|
715
755
|
else
|
|
716
756
|
finish_date = Time.now
|
|
717
757
|
end
|
|
@@ -743,16 +783,17 @@ command %i[done did] do |c|
|
|
|
743
783
|
|
|
744
784
|
old_entry = last_entry.dup
|
|
745
785
|
last_entry.note.add(note)
|
|
746
|
-
input = [last_entry.title, last_entry.note.
|
|
786
|
+
input = ["#{last_entry.date.strftime('%F %R | ')}#{last_entry.title}", last_entry.note.strip_lines.join("\n")].join("\n")
|
|
747
787
|
else
|
|
748
788
|
is_new = true
|
|
749
|
-
input = [args.join(' '), note.
|
|
789
|
+
input = ["#{date.strftime('%F %R | ')}#{args.join(' ')}", note.strip_lines.join("\n")].join("\n")
|
|
750
790
|
end
|
|
751
791
|
|
|
752
792
|
input = wwid.fork_editor(input).strip
|
|
753
793
|
raise EmptyInput, 'No content' unless input && !input.empty?
|
|
754
794
|
|
|
755
|
-
title, note = wwid.format_input(input)
|
|
795
|
+
d, title, note = wwid.format_input(input)
|
|
796
|
+
date = d.nil? ? date : d
|
|
756
797
|
new_entry = Doing::Item.new(date, title, section, note)
|
|
757
798
|
if new_entry.should_finish?
|
|
758
799
|
if new_entry.should_time?
|
|
@@ -763,23 +804,23 @@ command %i[done did] do |c|
|
|
|
763
804
|
end
|
|
764
805
|
|
|
765
806
|
if (is_new)
|
|
766
|
-
wwid.content
|
|
807
|
+
wwid.content.push(new_entry)
|
|
767
808
|
else
|
|
768
|
-
wwid.update_item(old_entry, new_entry)
|
|
809
|
+
wwid.content.update_item(old_entry, new_entry)
|
|
769
810
|
end
|
|
770
811
|
|
|
771
|
-
if options[:
|
|
812
|
+
if options[:archive]
|
|
772
813
|
wwid.move_item(new_entry, 'Archive', label: true)
|
|
773
814
|
end
|
|
774
815
|
|
|
775
816
|
wwid.write(wwid.doing_file)
|
|
776
817
|
elsif args.empty? && $stdin.stat.size.zero?
|
|
777
|
-
if options[:
|
|
818
|
+
if options[:remove]
|
|
778
819
|
wwid.tag_last({ tags: ['done'], count: 1, section: section, remove: true })
|
|
779
820
|
else
|
|
780
821
|
note = options[:note] ? Doing::Note.new(options[:note]) : nil
|
|
781
822
|
opt = {
|
|
782
|
-
archive: options[:
|
|
823
|
+
archive: options[:archive],
|
|
783
824
|
back: finish_date,
|
|
784
825
|
count: 1,
|
|
785
826
|
date: options[:date],
|
|
@@ -793,9 +834,10 @@ command %i[done did] do |c|
|
|
|
793
834
|
end
|
|
794
835
|
elsif !args.empty?
|
|
795
836
|
note = Doing::Note.new(options[:note])
|
|
796
|
-
title, new_note = wwid.format_input([args.join(' '), note.
|
|
837
|
+
d, title, new_note = wwid.format_input([args.join(' '), note.strip_lines.join("\n")].join("\n"))
|
|
838
|
+
date = d.nil? ? date : d
|
|
797
839
|
title.chomp!
|
|
798
|
-
section = 'Archive' if options[:
|
|
840
|
+
section = 'Archive' if options[:archive]
|
|
799
841
|
new_entry = Doing::Item.new(date, title, section, new_note)
|
|
800
842
|
if new_entry.should_finish?
|
|
801
843
|
if new_entry.should_time?
|
|
@@ -804,13 +846,17 @@ command %i[done did] do |c|
|
|
|
804
846
|
new_entry.tag('done')
|
|
805
847
|
end
|
|
806
848
|
end
|
|
807
|
-
wwid.content
|
|
849
|
+
wwid.content.push(new_entry)
|
|
808
850
|
wwid.write(wwid.doing_file)
|
|
809
851
|
Doing.logger.info('Entry Added:', new_entry.title)
|
|
810
852
|
elsif $stdin.stat.size.positive?
|
|
811
|
-
title, note = wwid.format_input($stdin.read)
|
|
853
|
+
d, title, note = wwid.format_input($stdin.read.strip)
|
|
854
|
+
unless d.nil?
|
|
855
|
+
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
|
856
|
+
date = d
|
|
857
|
+
end
|
|
812
858
|
note.add(options[:note]) if options[:note]
|
|
813
|
-
section = options[:
|
|
859
|
+
section = options[:archive] ? 'Archive' : section
|
|
814
860
|
new_entry = Doing::Item.new(date, title, section, note)
|
|
815
861
|
|
|
816
862
|
if new_entry.should_finish?
|
|
@@ -821,7 +867,7 @@ command %i[done did] do |c|
|
|
|
821
867
|
end
|
|
822
868
|
end
|
|
823
869
|
|
|
824
|
-
wwid.content
|
|
870
|
+
wwid.content.push(new_entry)
|
|
825
871
|
wwid.write(wwid.doing_file)
|
|
826
872
|
Doing.logger.info('Entry Added:', new_entry.title)
|
|
827
873
|
else
|
|
@@ -907,7 +953,7 @@ command :cancel do |c|
|
|
|
907
953
|
end
|
|
908
954
|
|
|
909
955
|
opts = {
|
|
910
|
-
archive: options[:
|
|
956
|
+
archive: options[:archive],
|
|
911
957
|
case: options[:case].normalize_case,
|
|
912
958
|
count: count,
|
|
913
959
|
date: false,
|
|
@@ -1001,7 +1047,7 @@ command :finish do |c|
|
|
|
1001
1047
|
options[:fuzzy] = false
|
|
1002
1048
|
unless options[:auto]
|
|
1003
1049
|
if options[:took]
|
|
1004
|
-
took =
|
|
1050
|
+
took = options[:took].chronify_qty
|
|
1005
1051
|
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
|
1006
1052
|
end
|
|
1007
1053
|
|
|
@@ -1010,21 +1056,21 @@ command :finish do |c|
|
|
|
1010
1056
|
raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
|
|
1011
1057
|
|
|
1012
1058
|
if options[:at]
|
|
1013
|
-
finish_date =
|
|
1059
|
+
finish_date = options[:at].chronify(guess: :begin)
|
|
1014
1060
|
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
|
1015
1061
|
|
|
1016
1062
|
date = options[:took] ? finish_date - took : finish_date
|
|
1017
1063
|
elsif options[:back]
|
|
1018
|
-
date =
|
|
1064
|
+
date = options[:back].chronify()
|
|
1019
1065
|
|
|
1020
1066
|
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
|
1021
|
-
elsif options[:took]
|
|
1022
|
-
date = wwid.chronify_qty(options[:took])
|
|
1023
1067
|
else
|
|
1024
1068
|
date = Time.now
|
|
1025
1069
|
end
|
|
1026
1070
|
end
|
|
1027
1071
|
|
|
1072
|
+
options[:took] = options[:took].chronify_qty if options[:took]
|
|
1073
|
+
|
|
1028
1074
|
if options[:tag].nil?
|
|
1029
1075
|
tags = []
|
|
1030
1076
|
else
|
|
@@ -1064,6 +1110,7 @@ command :finish do |c|
|
|
|
1064
1110
|
tag: tags,
|
|
1065
1111
|
tag_bool: options[:bool].normalize_bool,
|
|
1066
1112
|
tags: ['done'],
|
|
1113
|
+
took: options[:took],
|
|
1067
1114
|
unfinished: options[:unfinished]
|
|
1068
1115
|
}
|
|
1069
1116
|
|
|
@@ -1188,7 +1235,7 @@ command :tag do |c|
|
|
|
1188
1235
|
c.desc 'Tag last entry (or entries) not marked @done'
|
|
1189
1236
|
c.switch %i[u unfinished], negatable: false, default_value: false
|
|
1190
1237
|
|
|
1191
|
-
c.desc 'Autotag entries based on autotag configuration in ~/.
|
|
1238
|
+
c.desc 'Autotag entries based on autotag configuration in ~/.config/doing/config.yml'
|
|
1192
1239
|
c.switch %i[a autotag], negatable: false, default_value: false
|
|
1193
1240
|
|
|
1194
1241
|
c.desc 'Tag the last X entries containing TAG.
|
|
@@ -1279,15 +1326,15 @@ command :tag do |c|
|
|
|
1279
1326
|
end
|
|
1280
1327
|
|
|
1281
1328
|
|
|
1282
|
-
question = if options[:
|
|
1329
|
+
question = if options[:aarchive]
|
|
1283
1330
|
"Are you sure you want to autotag all records#{section_q}"
|
|
1284
|
-
elsif options[:
|
|
1331
|
+
elsif options[:remove]
|
|
1285
1332
|
"Are you sure you want to remove #{tags.join(' and ')} from all records#{section_q}"
|
|
1286
1333
|
else
|
|
1287
1334
|
"Are you sure you want to add #{tags.join(' and ')} to all records#{section_q}"
|
|
1288
1335
|
end
|
|
1289
1336
|
|
|
1290
|
-
res =
|
|
1337
|
+
res = Doing::Prompt.yn(question, default_response: false)
|
|
1291
1338
|
|
|
1292
1339
|
raise UserCancelled unless res
|
|
1293
1340
|
end
|
|
@@ -1408,7 +1455,7 @@ command [:mark, :flag] do |c|
|
|
|
1408
1455
|
"Are you sure you want to flag all records#{section_q}"
|
|
1409
1456
|
end
|
|
1410
1457
|
|
|
1411
|
-
res =
|
|
1458
|
+
res = Doing::Prompt.yn(question, default_response: false)
|
|
1412
1459
|
|
|
1413
1460
|
exit_now! 'Cancelled' unless res
|
|
1414
1461
|
end
|
|
@@ -1454,14 +1501,26 @@ command :show do |c|
|
|
|
1454
1501
|
c.arg_name 'AGE'
|
|
1455
1502
|
c.flag %i[a age], default_value: 'newest'
|
|
1456
1503
|
|
|
1457
|
-
c.desc '
|
|
1504
|
+
c.desc 'Show entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.'
|
|
1458
1505
|
c.arg_name 'DATE_STRING'
|
|
1459
1506
|
c.flag [:before]
|
|
1460
1507
|
|
|
1461
|
-
c.desc '
|
|
1508
|
+
c.desc 'Show entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.'
|
|
1462
1509
|
c.arg_name 'DATE_STRING'
|
|
1463
1510
|
c.flag [:after]
|
|
1464
1511
|
|
|
1512
|
+
c.desc %(
|
|
1513
|
+
Date range to show, or a single day to filter date on.
|
|
1514
|
+
Date range argument should be quoted. Date specifications can be natural language.
|
|
1515
|
+
To specify a range, use "to" or "through": `doing show --from "monday 8am to friday 5pm"`.
|
|
1516
|
+
|
|
1517
|
+
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
|
1518
|
+
by time of day.
|
|
1519
|
+
)
|
|
1520
|
+
|
|
1521
|
+
c.arg_name 'DATE_OR_RANGE'
|
|
1522
|
+
c.flag [:from]
|
|
1523
|
+
|
|
1465
1524
|
c.desc 'Search filter, surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
|
|
1466
1525
|
c.arg_name 'QUERY'
|
|
1467
1526
|
c.flag [:search]
|
|
@@ -1483,14 +1542,6 @@ command :show do |c|
|
|
|
1483
1542
|
c.arg_name 'ORDER'
|
|
1484
1543
|
c.flag %i[s sort], must_match: REGEX_SORT_ORDER, default_value: 'asc'
|
|
1485
1544
|
|
|
1486
|
-
c.desc %(
|
|
1487
|
-
Date range to show, or a single day to filter date on.
|
|
1488
|
-
Date range argument should be quoted. Date specifications can be natural language.
|
|
1489
|
-
To specify a range, use "to" or "through": `doing show --from "monday to friday"`
|
|
1490
|
-
)
|
|
1491
|
-
c.arg_name 'DATE_OR_RANGE'
|
|
1492
|
-
c.flag %i[f from]
|
|
1493
|
-
|
|
1494
1545
|
c.desc 'Show time intervals on @done tasks'
|
|
1495
1546
|
c.switch %i[t times], default_value: true, negatable: true
|
|
1496
1547
|
|
|
@@ -1565,21 +1616,6 @@ command :show do |c|
|
|
|
1565
1616
|
}
|
|
1566
1617
|
end
|
|
1567
1618
|
|
|
1568
|
-
if options[:from]
|
|
1569
|
-
|
|
1570
|
-
date_string = options[:from]
|
|
1571
|
-
if date_string =~ / (to|through|thru|(un)?til|-+) /
|
|
1572
|
-
dates = date_string.split(/ (to|through|thru|(un)?til|-+) /)
|
|
1573
|
-
start = wwid.chronify(dates[0], guess: :begin)
|
|
1574
|
-
finish = wwid.chronify(dates[2], guess: :end)
|
|
1575
|
-
else
|
|
1576
|
-
start = wwid.chronify(date_string, guess: :begin)
|
|
1577
|
-
finish = false
|
|
1578
|
-
end
|
|
1579
|
-
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
|
1580
|
-
dates = [start, finish]
|
|
1581
|
-
end
|
|
1582
|
-
|
|
1583
1619
|
options[:times] = true if options[:totals]
|
|
1584
1620
|
|
|
1585
1621
|
template = settings['templates']['default'].deep_merge({
|
|
@@ -1598,10 +1634,8 @@ command :show do |c|
|
|
|
1598
1634
|
end
|
|
1599
1635
|
|
|
1600
1636
|
opt = options.dup
|
|
1601
|
-
|
|
1602
1637
|
opt[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
1603
1638
|
opt[:count] = options[:count].to_i
|
|
1604
|
-
opt[:date_filter] = dates
|
|
1605
1639
|
opt[:highlight] = true
|
|
1606
1640
|
opt[:order] = options[:sort].normalize_order
|
|
1607
1641
|
opt[:section] = section
|
|
@@ -1632,14 +1666,25 @@ command %i[grep search] do |c|
|
|
|
1632
1666
|
c.arg_name 'NAME'
|
|
1633
1667
|
c.flag %i[s section], default_value: 'All'
|
|
1634
1668
|
|
|
1635
|
-
c.desc '
|
|
1669
|
+
c.desc 'Search entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.'
|
|
1636
1670
|
c.arg_name 'DATE_STRING'
|
|
1637
1671
|
c.flag [:before]
|
|
1638
1672
|
|
|
1639
|
-
c.desc '
|
|
1673
|
+
c.desc 'Search entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.'
|
|
1640
1674
|
c.arg_name 'DATE_STRING'
|
|
1641
1675
|
c.flag [:after]
|
|
1642
1676
|
|
|
1677
|
+
c.desc %(
|
|
1678
|
+
Date range to show, or a single day to filter date on.
|
|
1679
|
+
Date range argument should be quoted. Date specifications can be natural language.
|
|
1680
|
+
To specify a range, use "to" or "through": `doing search --from "monday 8am to friday 5pm"`.
|
|
1681
|
+
|
|
1682
|
+
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
|
1683
|
+
by time of day.
|
|
1684
|
+
)
|
|
1685
|
+
c.arg_name 'DATE_OR_RANGE'
|
|
1686
|
+
c.flag [:from]
|
|
1687
|
+
|
|
1643
1688
|
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
|
1644
1689
|
c.arg_name 'FORMAT'
|
|
1645
1690
|
c.flag %i[o output]
|
|
@@ -1729,7 +1774,7 @@ command :recent do |c|
|
|
|
1729
1774
|
c.switch %i[i interactive], negatable: false, default_value: false
|
|
1730
1775
|
|
|
1731
1776
|
c.action do |global_options, options, args|
|
|
1732
|
-
section = wwid.guess_section(options[:
|
|
1777
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
1733
1778
|
|
|
1734
1779
|
unless global_options[:version]
|
|
1735
1780
|
if settings['templates']['recent'].key?('count')
|
|
@@ -1744,7 +1789,7 @@ command :recent do |c|
|
|
|
1744
1789
|
count = args.empty? ? config_count : args[0].to_i
|
|
1745
1790
|
end
|
|
1746
1791
|
|
|
1747
|
-
options[:
|
|
1792
|
+
options[:times] = true if options[:totals]
|
|
1748
1793
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
1749
1794
|
|
|
1750
1795
|
template = settings['templates']['recent'].deep_merge(settings['templates']['default'])
|
|
@@ -1753,7 +1798,7 @@ command :recent do |c|
|
|
|
1753
1798
|
opts = {
|
|
1754
1799
|
sort_tags: options[:sort_tags],
|
|
1755
1800
|
tags_color: tags_color,
|
|
1756
|
-
times: options[:
|
|
1801
|
+
times: options[:times],
|
|
1757
1802
|
totals: options[:totals],
|
|
1758
1803
|
interactive: options[:interactive]
|
|
1759
1804
|
}
|
|
@@ -1799,19 +1844,20 @@ command :today do |c|
|
|
|
1799
1844
|
c.arg_name 'TIME_STRING'
|
|
1800
1845
|
c.flag [:after]
|
|
1801
1846
|
|
|
1847
|
+
c.desc %(
|
|
1848
|
+
Time range to show `doing today --from "12pm to 4pm"`
|
|
1849
|
+
)
|
|
1850
|
+
c.arg_name 'DATE_OR_RANGE'
|
|
1851
|
+
c.flag [:from]
|
|
1852
|
+
|
|
1802
1853
|
c.action do |_global_options, options, _args|
|
|
1803
1854
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
1804
1855
|
|
|
1805
|
-
options[:
|
|
1856
|
+
options[:times] = true if options[:totals]
|
|
1806
1857
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
section: options[:section],
|
|
1811
|
-
sort_tags: options[:sort_tags],
|
|
1812
|
-
totals: options[:totals]
|
|
1813
|
-
}
|
|
1814
|
-
Doing::Pager.page wwid.today(options[:times], options[:output], opt).chomp
|
|
1858
|
+
filter_options = %i[after before from section sort_tags totals].each_with_object({}) { |k, hsh| hsh[k] = options[k] }
|
|
1859
|
+
|
|
1860
|
+
Doing::Pager.page wwid.today(options[:times], options[:output], filter_options).chomp
|
|
1815
1861
|
end
|
|
1816
1862
|
end
|
|
1817
1863
|
|
|
@@ -1854,23 +1900,23 @@ command :on do |c|
|
|
|
1854
1900
|
|
|
1855
1901
|
if date_string =~ / (to|through|thru) /
|
|
1856
1902
|
dates = date_string.split(/ (to|through|thru) /)
|
|
1857
|
-
start =
|
|
1858
|
-
finish =
|
|
1903
|
+
start = dates[0].chronify(guess: :begin)
|
|
1904
|
+
finish = dates[2].chronify(guess: :end)
|
|
1859
1905
|
else
|
|
1860
|
-
start =
|
|
1906
|
+
start = date_string.chronify(guess: :begin)
|
|
1861
1907
|
finish = false
|
|
1862
1908
|
end
|
|
1863
1909
|
|
|
1864
1910
|
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
|
1865
1911
|
|
|
1866
|
-
message = "
|
|
1912
|
+
message = "date interpreted as #{start}"
|
|
1867
1913
|
message += " to #{finish}" if finish
|
|
1868
|
-
Doing.logger.debug(message)
|
|
1914
|
+
Doing.logger.debug('Interpreter:', message)
|
|
1869
1915
|
|
|
1870
|
-
options[:
|
|
1916
|
+
options[:times] = true if options[:totals]
|
|
1871
1917
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
1872
1918
|
|
|
1873
|
-
Doing::Pager.page wwid.list_date([start, finish], options[:
|
|
1919
|
+
Doing::Pager.page wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
|
1874
1920
|
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
1875
1921
|
end
|
|
1876
1922
|
end
|
|
@@ -1913,17 +1959,17 @@ command :since do |c|
|
|
|
1913
1959
|
date_string.sub!(/(day) (\d)/, '\1 at \2')
|
|
1914
1960
|
date_string.sub!(/(\d+)d( ago)?/, '\1 days ago')
|
|
1915
1961
|
|
|
1916
|
-
start =
|
|
1962
|
+
start = date_string.chronify(guess: :begin)
|
|
1917
1963
|
finish = Time.now
|
|
1918
1964
|
|
|
1919
1965
|
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
|
1920
1966
|
|
|
1921
|
-
Doing.logger.debug("
|
|
1967
|
+
Doing.logger.debug('Interpreter:', "date interpreted as #{start} through the current time")
|
|
1922
1968
|
|
|
1923
|
-
options[:
|
|
1969
|
+
options[:times] = true if options[:totals]
|
|
1924
1970
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
1925
1971
|
|
|
1926
|
-
Doing::Pager.page wwid.list_date([start, finish], options[:
|
|
1972
|
+
Doing::Pager.page wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
|
1927
1973
|
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
1928
1974
|
end
|
|
1929
1975
|
end
|
|
@@ -1961,6 +2007,12 @@ command :yesterday do |c|
|
|
|
1961
2007
|
c.arg_name 'TIME_STRING'
|
|
1962
2008
|
c.flag [:after]
|
|
1963
2009
|
|
|
2010
|
+
c.desc %(
|
|
2011
|
+
Time range to show, e.g. `doing yesterday --from "1am to 8am"`
|
|
2012
|
+
)
|
|
2013
|
+
c.arg_name 'TIME_RANGE'
|
|
2014
|
+
c.flag [:from]
|
|
2015
|
+
|
|
1964
2016
|
c.desc 'Tag sort direction (asc|desc)'
|
|
1965
2017
|
c.arg_name 'DIRECTION'
|
|
1966
2018
|
c.flag [:tag_order], must_match: REGEX_SORT_ORDER, default_value: 'asc'
|
|
@@ -1970,9 +2022,16 @@ command :yesterday do |c|
|
|
|
1970
2022
|
|
|
1971
2023
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
1972
2024
|
|
|
2025
|
+
if options[:from]
|
|
2026
|
+
options[:from] = options[:from].split(/ (?:to|through|thru|(?:un)?til|-+) /).map do |time|
|
|
2027
|
+
"yesterday #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}"
|
|
2028
|
+
end.join(' to ')
|
|
2029
|
+
end
|
|
2030
|
+
|
|
1973
2031
|
opt = {
|
|
1974
2032
|
after: options[:after],
|
|
1975
2033
|
before: options[:before],
|
|
2034
|
+
from: options[:from],
|
|
1976
2035
|
sort_tags: options[:sort_tags],
|
|
1977
2036
|
tag_order: options[:tag_order].normalize_order,
|
|
1978
2037
|
totals: options[:totals],
|
|
@@ -2053,9 +2112,9 @@ command :last do |c|
|
|
|
2053
2112
|
end
|
|
2054
2113
|
|
|
2055
2114
|
if options[:editor]
|
|
2056
|
-
wwid.edit_last(section: options[:
|
|
2115
|
+
wwid.edit_last(section: options[:section], options: { search: search, fuzzy: options[:fuzzy], case: options[:case], tag: tags, tag_bool: options[:bool], not: options[:not] })
|
|
2057
2116
|
else
|
|
2058
|
-
Doing::Pager::page wwid.last(times: true, section: options[:
|
|
2117
|
+
Doing::Pager::page wwid.last(times: true, section: options[:section],
|
|
2059
2118
|
options: { search: search, fuzzy: options[:fuzzy], case: options[:case], negate: options[:not], tag: tags, tag_bool: options[:bool] }).strip
|
|
2060
2119
|
end
|
|
2061
2120
|
end
|
|
@@ -2067,8 +2126,8 @@ command :sections do |c|
|
|
|
2067
2126
|
c.switch %i[c column], negatable: false, default_value: false
|
|
2068
2127
|
|
|
2069
2128
|
c.action do |_global_options, options, _args|
|
|
2070
|
-
joiner = options[:
|
|
2071
|
-
print wwid.
|
|
2129
|
+
joiner = options[:column] ? "\n" : "\t"
|
|
2130
|
+
print wwid.content.section_titles.join(joiner)
|
|
2072
2131
|
end
|
|
2073
2132
|
end
|
|
2074
2133
|
|
|
@@ -2089,7 +2148,7 @@ command :add_section do |c|
|
|
|
2089
2148
|
c.action do |_global_options, _options, args|
|
|
2090
2149
|
raise InvalidArgument, "Section #{args[0]} already exists" if wwid.sections.include?(args[0])
|
|
2091
2150
|
|
|
2092
|
-
wwid.add_section(args.join(' ').cap_first)
|
|
2151
|
+
wwid.content.add_section(args.join(' ').cap_first, log: true)
|
|
2093
2152
|
wwid.write(wwid.doing_file)
|
|
2094
2153
|
end
|
|
2095
2154
|
end
|
|
@@ -2219,14 +2278,25 @@ command :view do |c|
|
|
|
2219
2278
|
c.arg_name 'DIRECTION'
|
|
2220
2279
|
c.flag [:tag_order], must_match: REGEX_SORT_ORDER
|
|
2221
2280
|
|
|
2222
|
-
c.desc 'View entries older than date'
|
|
2281
|
+
c.desc 'View entries older than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.'
|
|
2223
2282
|
c.arg_name 'DATE_STRING'
|
|
2224
2283
|
c.flag [:before]
|
|
2225
2284
|
|
|
2226
|
-
c.desc 'View entries newer than date'
|
|
2285
|
+
c.desc 'View entries newer than date. If this is only a time (8am, 1:30pm, 15:00), all dates will be included, but entries will be filtered by time of day.'
|
|
2227
2286
|
c.arg_name 'DATE_STRING'
|
|
2228
2287
|
c.flag [:after]
|
|
2229
2288
|
|
|
2289
|
+
c.desc %(
|
|
2290
|
+
Date range to show, or a single day to filter date on.
|
|
2291
|
+
Date range argument should be quoted. Date specifications can be natural language.
|
|
2292
|
+
To specify a range, use "to" or "through": `doing view --from "monday 8am to friday 5pm" view_name`.
|
|
2293
|
+
|
|
2294
|
+
If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered
|
|
2295
|
+
by time of day.
|
|
2296
|
+
)
|
|
2297
|
+
c.arg_name 'DATE_OR_RANGE'
|
|
2298
|
+
c.flag [:from]
|
|
2299
|
+
|
|
2230
2300
|
c.desc 'Only show items with recorded time intervals (override view settings)'
|
|
2231
2301
|
c.switch [:only_timed], default_value: false, negatable: false
|
|
2232
2302
|
|
|
@@ -2247,6 +2317,7 @@ command :view do |c|
|
|
|
2247
2317
|
rescue WrongCommand => exception
|
|
2248
2318
|
cmd = commands[:show]
|
|
2249
2319
|
options[:sort] = 'asc'
|
|
2320
|
+
options[:tag_order] = 'asc'
|
|
2250
2321
|
action = cmd.send(:get_action, nil)
|
|
2251
2322
|
return action.call(global_options, options, args)
|
|
2252
2323
|
end
|
|
@@ -2289,12 +2360,9 @@ command :view do |c|
|
|
|
2289
2360
|
# If the -o/--output flag was specified, override any default in the view template
|
|
2290
2361
|
options[:output] ||= view.key?('output_format') ? view['output_format'] : 'template'
|
|
2291
2362
|
|
|
2292
|
-
count =
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
view.key?('count') ? view['count'] : 10
|
|
2296
|
-
end
|
|
2297
|
-
section = if options[:s]
|
|
2363
|
+
count = options[:count] ? options[:count] : view.key?('count') ? view['count'] : 10
|
|
2364
|
+
|
|
2365
|
+
section = if options[:section]
|
|
2298
2366
|
section
|
|
2299
2367
|
else
|
|
2300
2368
|
view.key?('section') ? view['section'] : settings['current_section']
|
|
@@ -2312,7 +2380,7 @@ command :view do |c|
|
|
|
2312
2380
|
view.key?('tag_order') ? view['tag_order'].normalize_order : 'asc'
|
|
2313
2381
|
end
|
|
2314
2382
|
|
|
2315
|
-
options[:
|
|
2383
|
+
options[:times] = true if totals
|
|
2316
2384
|
output_format = options[:output]&.downcase || 'template'
|
|
2317
2385
|
|
|
2318
2386
|
options[:sort_tags] = if options[:tag_sort]
|
|
@@ -2322,27 +2390,8 @@ command :view do |c|
|
|
|
2322
2390
|
else
|
|
2323
2391
|
false
|
|
2324
2392
|
end
|
|
2325
|
-
if view.key?('after') && !options[:after]
|
|
2326
|
-
options[:after] = view['after']
|
|
2327
|
-
end
|
|
2328
2393
|
|
|
2329
|
-
if view.key?(
|
|
2330
|
-
options[:before] = view['before']
|
|
2331
|
-
end
|
|
2332
|
-
|
|
2333
|
-
if view.key?('from')
|
|
2334
|
-
date_string = view['from']
|
|
2335
|
-
if date_string =~ / (to|through|thru|(un)?til|-+) /
|
|
2336
|
-
dates = date_string.split(/ (to|through|thru|(un)?til|-+) /)
|
|
2337
|
-
start = wwid.chronify(dates[0], guess: :begin)
|
|
2338
|
-
finish = wwid.chronify(dates[2], guess: :end)
|
|
2339
|
-
else
|
|
2340
|
-
start = wwid.chronify(date_string, guess: :begin)
|
|
2341
|
-
finish = false
|
|
2342
|
-
end
|
|
2343
|
-
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
|
2344
|
-
dates = [start, finish]
|
|
2345
|
-
end
|
|
2394
|
+
%w[before after from].each { |k| options[k.to_sym] = view[k] if view.key?(k) && !options[k.to_sym] }
|
|
2346
2395
|
|
|
2347
2396
|
options[:case] = options[:case].normalize_case
|
|
2348
2397
|
|
|
@@ -2354,22 +2403,21 @@ command :view do |c|
|
|
|
2354
2403
|
end
|
|
2355
2404
|
|
|
2356
2405
|
opts = options.dup
|
|
2357
|
-
opts[:search] = search
|
|
2358
|
-
opts[:output] = output_format
|
|
2359
2406
|
opts[:count] = count
|
|
2360
2407
|
opts[:format] = date_format
|
|
2361
2408
|
opts[:highlight] = options[:color]
|
|
2362
2409
|
opts[:only_timed] = only_timed
|
|
2363
2410
|
opts[:order] = order
|
|
2411
|
+
opts[:output] = options[:interactive] ? nil : options[:output]
|
|
2412
|
+
opts[:output] = output_format
|
|
2413
|
+
opts[:page_title] = page_title
|
|
2414
|
+
opts[:search] = search
|
|
2364
2415
|
opts[:section] = section
|
|
2365
2416
|
opts[:tag_filter] = tag_filter
|
|
2366
2417
|
opts[:tag_order] = tag_order
|
|
2367
2418
|
opts[:tags_color] = tags_color
|
|
2368
2419
|
opts[:template] = template
|
|
2369
2420
|
opts[:totals] = totals
|
|
2370
|
-
opts[:page_title] = page_title
|
|
2371
|
-
opts[:date_filter] = dates
|
|
2372
|
-
opts[:output] = options[:interactive] ? nil : options[:output]
|
|
2373
2421
|
|
|
2374
2422
|
Doing::Pager.page wwid.list_section(opts)
|
|
2375
2423
|
elsif title.instance_of?(FalseClass)
|
|
@@ -2386,7 +2434,7 @@ command :views do |c|
|
|
|
2386
2434
|
c.switch %i[c column], default_value: false
|
|
2387
2435
|
|
|
2388
2436
|
c.action do |_global_options, options, _args|
|
|
2389
|
-
joiner = options[:
|
|
2437
|
+
joiner = options[:column] ? "\n" : "\t"
|
|
2390
2438
|
print wwid.views.join(joiner)
|
|
2391
2439
|
end
|
|
2392
2440
|
end
|
|
@@ -2552,6 +2600,10 @@ end
|
|
|
2552
2600
|
desc 'Open the "doing" file in an editor'
|
|
2553
2601
|
long_desc "`doing open` defaults to using the editor_app setting in #{config.config_file} (#{settings.key?('editor_app') ? settings['editor_app'] : 'not set'})."
|
|
2554
2602
|
command :open do |c|
|
|
2603
|
+
c.desc 'Open with editor command (e.g. vim, mate)'
|
|
2604
|
+
c.arg_name 'COMMAND'
|
|
2605
|
+
c.flag %i[e editor]
|
|
2606
|
+
|
|
2555
2607
|
if `uname` =~ /Darwin/
|
|
2556
2608
|
c.desc 'Open with app name'
|
|
2557
2609
|
c.arg_name 'APP_NAME'
|
|
@@ -2567,14 +2619,20 @@ command :open do |c|
|
|
|
2567
2619
|
params.delete_if do |k, v|
|
|
2568
2620
|
k.instance_of?(String) || v.nil? || v == false
|
|
2569
2621
|
end
|
|
2570
|
-
|
|
2622
|
+
|
|
2623
|
+
if options[:editor]
|
|
2624
|
+
raise MissingEditor, "Editor #{options[:editor]} not found" unless Doing::Util.exec_available(options[:editor].split(/ /).first)
|
|
2625
|
+
|
|
2626
|
+
editor = TTY::Which.which(options[:editor])
|
|
2627
|
+
system %(#{editor} "#{File.expand_path(wwid.doing_file)}")
|
|
2628
|
+
elsif `uname` =~ /Darwin/
|
|
2571
2629
|
if options[:app]
|
|
2572
|
-
system %(open -a "#{options[:
|
|
2630
|
+
system %(open -a "#{options[:app]}" "#{File.expand_path(wwid.doing_file)}")
|
|
2573
2631
|
elsif options[:bundle_id]
|
|
2574
|
-
system %(open -b "#{options[:
|
|
2632
|
+
system %(open -b "#{options[:bundle_id]}" "#{File.expand_path(wwid.doing_file)}")
|
|
2575
2633
|
elsif Doing::Util.find_default_editor('doing_file')
|
|
2576
2634
|
editor = Doing::Util.find_default_editor('doing_file')
|
|
2577
|
-
if Doing::Util.exec_available(editor)
|
|
2635
|
+
if Doing::Util.exec_available(editor.split(/ /).first)
|
|
2578
2636
|
system %(#{editor} "#{File.expand_path(wwid.doing_file)}")
|
|
2579
2637
|
else
|
|
2580
2638
|
system %(open -a "#{editor}" "#{File.expand_path(wwid.doing_file)}")
|
|
@@ -2591,132 +2649,240 @@ command :open do |c|
|
|
|
2591
2649
|
end
|
|
2592
2650
|
|
|
2593
2651
|
desc 'Edit the configuration file or output a value from it'
|
|
2594
|
-
long_desc %(Run without arguments, `doing config` opens your
|
|
2652
|
+
long_desc %(Run without arguments, `doing config` opens your `config.yml` in an editor.
|
|
2595
2653
|
If local configurations are found in the path between the current directory
|
|
2596
|
-
and
|
|
2654
|
+
and the root (/), a menu will allow you to select which to open in the editor.
|
|
2597
2655
|
|
|
2598
2656
|
It will use the editor defined in `config_editor_app`, or one specified with `--editor`.
|
|
2599
2657
|
|
|
2600
|
-
Use `doing config
|
|
2658
|
+
Use `doing config get` to output the configuration to the terminal, and
|
|
2601
2659
|
provide a dot-separated key path to get a specific value. Shows the current value
|
|
2602
2660
|
including keys/overrides set by local configs.)
|
|
2603
|
-
arg_name 'KEY_PATH'
|
|
2604
2661
|
command :config do |c|
|
|
2605
2662
|
c.example 'doing config', desc: "Open an active configuration in #{Doing::Util.find_default_editor('config')}"
|
|
2606
|
-
c.example 'doing config
|
|
2607
|
-
c.example 'doing config
|
|
2663
|
+
c.example 'doing config get doing_file', desc: 'Output the value of a config key as YAML'
|
|
2664
|
+
c.example 'doing config get plugins.plugin_path -o json', desc: 'Output the value of a key path as JSON'
|
|
2665
|
+
c.example 'doing config set plugins.say.say_voice Alex', desc: 'Set the value of a key path and update config file'
|
|
2666
|
+
c.example 'doing config set plug.say.voice Zarvox', desc: 'Key paths for get and set are fuzzy matched'
|
|
2608
2667
|
|
|
2609
|
-
c.
|
|
2610
|
-
c.arg_name 'EDITOR'
|
|
2611
|
-
c.flag %i[e editor], default_value: nil
|
|
2668
|
+
c.default_command :edit
|
|
2612
2669
|
|
|
2613
|
-
c.desc '
|
|
2614
|
-
c.switch %i[d dump]
|
|
2670
|
+
c.desc 'DEPRECATED'
|
|
2671
|
+
c.switch %i[d dump]
|
|
2615
2672
|
|
|
2616
|
-
c.desc '
|
|
2617
|
-
c.
|
|
2618
|
-
c.flag %i[o output], default_value: 'yaml', must_match: /^(?:y(?:aml)?|j(?:son)?|r(?:aw)?)$/
|
|
2673
|
+
c.desc 'DEPRECATED'
|
|
2674
|
+
c.switch %i[u update]
|
|
2619
2675
|
|
|
2620
|
-
c.desc '
|
|
2621
|
-
c.
|
|
2676
|
+
c.desc 'List configuration paths, including .doingrc files in the current and parent directories'
|
|
2677
|
+
c.long_desc 'Config files are listed in order of precedence (if there are multiple configs detected).
|
|
2678
|
+
Values defined in the top item in the list will override values in configutations below it.'
|
|
2679
|
+
c.command :list do |list|
|
|
2680
|
+
list.action do |global, options, args|
|
|
2681
|
+
puts config.additional_configs.join("\n")
|
|
2682
|
+
puts config.config_file
|
|
2683
|
+
end
|
|
2684
|
+
end
|
|
2622
2685
|
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2686
|
+
c.desc 'Open config file in editor'
|
|
2687
|
+
c.command :edit do |edit|
|
|
2688
|
+
edit.example 'doing config edit', desc: 'Open a config file in the default editor'
|
|
2689
|
+
edit.example 'doing config edit --editor vim', desc: 'Open config in specific editor'
|
|
2627
2690
|
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2691
|
+
edit.desc 'Editor to use'
|
|
2692
|
+
edit.arg_name 'EDITOR'
|
|
2693
|
+
edit.flag %i[e editor], default_value: nil
|
|
2631
2694
|
|
|
2632
|
-
|
|
2633
|
-
|
|
2695
|
+
if `uname` =~ /Darwin/
|
|
2696
|
+
edit.desc 'Application to use'
|
|
2697
|
+
edit.arg_name 'APP_NAME'
|
|
2698
|
+
edit.flag %i[a app]
|
|
2699
|
+
|
|
2700
|
+
edit.desc 'Application bundle id to use'
|
|
2701
|
+
edit.arg_name 'BUNDLE_ID'
|
|
2702
|
+
edit.flag %i[b bundle_id]
|
|
2703
|
+
|
|
2704
|
+
edit.desc "Use the config_editor_app defined in ~/.config/doing/config.yml (#{settings.key?('config_editor_app') ? settings['config_editor_app'] : 'config_editor_app not set'})"
|
|
2705
|
+
edit.switch %i[x default]
|
|
2706
|
+
end
|
|
2707
|
+
|
|
2708
|
+
edit.action do |global, options, args|
|
|
2709
|
+
if options[:update] || options[:dump]
|
|
2710
|
+
cmd = commands[:config]
|
|
2711
|
+
if options[:update]
|
|
2712
|
+
cmd = cmd.commands[:update]
|
|
2713
|
+
elsif options[:dump]
|
|
2714
|
+
cmd = cmd.commands[:get]
|
|
2715
|
+
end
|
|
2716
|
+
action = cmd.send(:get_action, nil)
|
|
2717
|
+
action.call(global, options, args)
|
|
2718
|
+
Doing.logger.warn('Deprecated:', '--dump and --update are deprecated,
|
|
2719
|
+
use `doing config get` and `doing config update`')
|
|
2720
|
+
Doing.logger.output_results
|
|
2721
|
+
return
|
|
2722
|
+
end
|
|
2723
|
+
|
|
2724
|
+
config_file = config.choose_config
|
|
2725
|
+
|
|
2726
|
+
if `uname` =~ /Darwin/
|
|
2727
|
+
if options[:default]
|
|
2728
|
+
editor = Doing::Util.find_default_editor('config')
|
|
2729
|
+
if editor
|
|
2730
|
+
if Doing::Util.exec_available(editor.split(/ /).first)
|
|
2731
|
+
system %(#{editor} "#{config_file}")
|
|
2732
|
+
else
|
|
2733
|
+
`open -a "#{editor}" "#{config_file}"`
|
|
2734
|
+
end
|
|
2735
|
+
else
|
|
2736
|
+
raise InvalidArgument, 'No viable editor found in config or environment.'
|
|
2737
|
+
end
|
|
2738
|
+
elsif options[:app] || options[:bundle_id]
|
|
2739
|
+
if options[:app]
|
|
2740
|
+
`open -a "#{options[:app]}" "#{config_file}"`
|
|
2741
|
+
elsif options[:bundle_id]
|
|
2742
|
+
`open -b #{options[:bundle_id]} "#{config_file}"`
|
|
2743
|
+
end
|
|
2744
|
+
else
|
|
2745
|
+
editor = options[:editor] || Doing::Util.find_default_editor('config')
|
|
2746
|
+
|
|
2747
|
+
raise MissingEditor, 'No viable editor defined in config or environment' unless editor
|
|
2748
|
+
|
|
2749
|
+
if Doing::Util.exec_available(editor.split(/ /).first)
|
|
2750
|
+
system %(#{editor} "#{config_file}")
|
|
2751
|
+
else
|
|
2752
|
+
`open -a "#{editor}" "#{config_file}"`
|
|
2753
|
+
end
|
|
2754
|
+
end
|
|
2755
|
+
else
|
|
2756
|
+
editor = options[:editor] || Doing::Util.default_editor
|
|
2757
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' unless editor && Doing::Util.exec_available(editor.split(/ /).first)
|
|
2758
|
+
|
|
2759
|
+
system %(#{editor} "#{config_file}")
|
|
2760
|
+
end
|
|
2761
|
+
end
|
|
2634
2762
|
end
|
|
2635
2763
|
|
|
2636
|
-
c.
|
|
2637
|
-
|
|
2764
|
+
c.desc 'Update default config file, adding any missing keys'
|
|
2765
|
+
c.command %i[update refresh] do |update|
|
|
2766
|
+
update.action do |_global, options, args|
|
|
2638
2767
|
config.configure({rewrite: true, ignore_local: true})
|
|
2639
|
-
|
|
2768
|
+
Doing.logger.warn('Config:', 'config refreshed')
|
|
2640
2769
|
end
|
|
2770
|
+
end
|
|
2771
|
+
|
|
2772
|
+
c.desc 'Undo the last change to a config file'
|
|
2773
|
+
c.command :undo do |undo|
|
|
2774
|
+
undo.action do |_global, options, args|
|
|
2775
|
+
config_file = config.choose_config
|
|
2776
|
+
wwid.restore_backup(config_file)
|
|
2777
|
+
end
|
|
2778
|
+
end
|
|
2779
|
+
|
|
2780
|
+
c.desc 'Output a key\'s value'
|
|
2781
|
+
c.arg 'KEY_PATH'
|
|
2782
|
+
c.command %i[get dump] do |dump|
|
|
2783
|
+
dump.example 'doing config get', desc: 'Output the entire configuration'
|
|
2784
|
+
dump.example 'doing config get timer_format --output raw', desc: 'Output the value of timer_format as a plain string'
|
|
2785
|
+
dump.example 'doing config get doing_file', desc: 'Output the value of the doing_file setting, respecting local configurations'
|
|
2786
|
+
dump.example 'doing config get -o json plug.plugpath', desc: 'Key path is fuzzy matched: output the value of plugins->plugin_path as JSON'
|
|
2787
|
+
|
|
2788
|
+
dump.desc 'Format for output (json|yaml|raw)'
|
|
2789
|
+
dump.arg_name 'FORMAT'
|
|
2790
|
+
dump.flag %i[o output], default_value: 'yaml', must_match: /^(?:y(?:aml)?|j(?:son)?|r(?:aw)?)$/
|
|
2791
|
+
|
|
2792
|
+
dump.action do |_global, options, args|
|
|
2641
2793
|
|
|
2642
|
-
if options[:dump]
|
|
2643
2794
|
keypath = args.join('.')
|
|
2644
2795
|
cfg = config.value_for_key(keypath)
|
|
2796
|
+
real_path = config.resolve_key_path(keypath)
|
|
2645
2797
|
|
|
2646
2798
|
if cfg
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2799
|
+
val = cfg.map {|k, v| v }[0]
|
|
2800
|
+
if real_path.count.positive?
|
|
2801
|
+
nested_cfg = {}
|
|
2802
|
+
nested_cfg.deep_set(real_path, val)
|
|
2803
|
+
else
|
|
2804
|
+
nested_cfg = val
|
|
2805
|
+
end
|
|
2806
|
+
|
|
2807
|
+
if options[:output] =~ /^r/
|
|
2808
|
+
if val.is_a?(Hash)
|
|
2809
|
+
$stdout.puts YAML.dump(val)
|
|
2810
|
+
elsif val.is_a?(Array)
|
|
2811
|
+
$stdout.puts val.join(', ')
|
|
2812
|
+
else
|
|
2813
|
+
$stdout.puts val.to_s
|
|
2814
|
+
end
|
|
2815
|
+
else
|
|
2816
|
+
$stdout.puts case options[:output]
|
|
2817
|
+
when /^j/
|
|
2818
|
+
JSON.pretty_generate(val)
|
|
2819
|
+
else
|
|
2820
|
+
YAML.dump(nested_cfg)
|
|
2821
|
+
end
|
|
2822
|
+
end
|
|
2655
2823
|
else
|
|
2656
2824
|
Doing.logger.log_now(:error, 'Config:', "Key #{keypath} not found")
|
|
2657
2825
|
end
|
|
2658
2826
|
Doing.logger.output_results
|
|
2659
2827
|
return
|
|
2660
2828
|
end
|
|
2829
|
+
end
|
|
2661
2830
|
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2831
|
+
c.desc 'Set a key\'s value in the config file'
|
|
2832
|
+
c.arg 'KEY VALUE'
|
|
2833
|
+
c.command :set do |set|
|
|
2834
|
+
set.example 'doing config set timer_format human', desc: 'Set the value of timer_format to "human"'
|
|
2835
|
+
set.example 'doing config set plug.plugpath ~/my_plugins', desc: 'Key path is fuzzy matched: set the value of plugins->plugin_path'
|
|
2666
2836
|
|
|
2667
|
-
|
|
2837
|
+
set.desc 'Delete specified key'
|
|
2838
|
+
set.switch %i[r remove], default_value: false, negatable: false
|
|
2668
2839
|
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
end
|
|
2840
|
+
set.action do |_global, options, args|
|
|
2841
|
+
if args.count < 2 && !options[:remove]
|
|
2842
|
+
raise InvalidArgument, 'config set requires at least two arguments, key path and value'
|
|
2673
2843
|
|
|
2674
|
-
|
|
2675
|
-
if options[:x]
|
|
2676
|
-
editor = Doing::Util.find_default_editor('config')
|
|
2677
|
-
if editor
|
|
2678
|
-
if Doing::Util.exec_available(editor)
|
|
2679
|
-
system %(#{editor} "#{config_file}")
|
|
2680
|
-
else
|
|
2681
|
-
`open -a "#{editor}" "#{config_file}"`
|
|
2682
|
-
end
|
|
2683
|
-
else
|
|
2684
|
-
raise InvalidArgument, 'No viable editor found in config or environment.'
|
|
2685
|
-
end
|
|
2686
|
-
elsif options[:a] || options[:b]
|
|
2687
|
-
if options[:a]
|
|
2688
|
-
`open -a "#{options[:a]}" "#{config_file}"`
|
|
2689
|
-
elsif options[:b]
|
|
2690
|
-
`open -b #{options[:b]} "#{config_file}"`
|
|
2691
|
-
end
|
|
2692
|
-
else
|
|
2693
|
-
editor = options[:e] || Doing::Util.find_default_editor('config')
|
|
2844
|
+
end
|
|
2694
2845
|
|
|
2695
|
-
|
|
2846
|
+
value = options[:remove] ? nil : args.pop
|
|
2847
|
+
keypath = args.join('.')
|
|
2848
|
+
old_value = config.value_for_key(keypath).map { |k, v| v.to_s }
|
|
2849
|
+
real_path = config.resolve_key_path(keypath)
|
|
2850
|
+
raise InvalidArgument, 'Invalid key path' if real_path.empty?
|
|
2696
2851
|
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2852
|
+
config_file = config.choose_config
|
|
2853
|
+
cfg = YAML.safe_load_file(config_file) || {}
|
|
2854
|
+
|
|
2855
|
+
$stderr.puts "Updating #{config_file}".yellow
|
|
2856
|
+
|
|
2857
|
+
if options[:remove]
|
|
2858
|
+
cfg.deep_set(real_path, nil)
|
|
2859
|
+
$stderr.puts "#{'Deleting key:'.yellow} #{real_path.join('->').boldwhite}"
|
|
2860
|
+
else
|
|
2861
|
+
old_value = cfg.dig(*real_path) || 'empty'
|
|
2862
|
+
cfg.deep_set(real_path, value.set_type)
|
|
2863
|
+
$stderr.puts "#{'Key path:'.yellow} #{real_path.join('->').boldwhite}"
|
|
2864
|
+
$stderr.puts "#{'Previous:'.yellow} #{old_value.to_s.boldwhite}"
|
|
2865
|
+
$stderr.puts "#{' New:'.yellow} #{value.set_type.to_s.boldwhite}"
|
|
2702
2866
|
end
|
|
2703
|
-
else
|
|
2704
|
-
editor = options[:e] || Doing::Util.default_editor
|
|
2705
|
-
raise MissingEditor, 'No EDITOR variable defined in environment' unless editor && Doing::Util.exec_available(editor)
|
|
2706
2867
|
|
|
2707
|
-
|
|
2868
|
+
res = Doing::Prompt.yn('Update selected config', default_response: true)
|
|
2869
|
+
|
|
2870
|
+
raise UserCancelled, 'Cancelled' unless res
|
|
2871
|
+
|
|
2872
|
+
Doing::Util.write_to_file(config_file, YAML.dump(cfg), backup: true)
|
|
2873
|
+
Doing.logger.warn('Config:', "#{config_file} updated")
|
|
2708
2874
|
end
|
|
2709
2875
|
end
|
|
2710
2876
|
end
|
|
2711
2877
|
|
|
2712
|
-
desc 'Undo the last change to the
|
|
2878
|
+
desc 'Undo the last change to the Doing file'
|
|
2713
2879
|
command :undo do |c|
|
|
2714
2880
|
c.desc 'Specify alternate doing file'
|
|
2715
2881
|
c.arg_name 'PATH'
|
|
2716
2882
|
c.flag %i[f file], default_value: wwid.doing_file
|
|
2717
2883
|
|
|
2718
2884
|
c.action do |_global_options, options, _args|
|
|
2719
|
-
file = options[:
|
|
2885
|
+
file = options[:file] || wwid.doing_file
|
|
2720
2886
|
wwid.restore_backup(file)
|
|
2721
2887
|
end
|
|
2722
2888
|
end
|
|
@@ -2764,6 +2930,7 @@ command :import do |c|
|
|
|
2764
2930
|
c.arg_name 'PREFIX'
|
|
2765
2931
|
c.flag :prefix
|
|
2766
2932
|
|
|
2933
|
+
# TODO: Allow time range filtering
|
|
2767
2934
|
c.desc 'Import entries older than date'
|
|
2768
2935
|
c.arg_name 'DATE_STRING'
|
|
2769
2936
|
c.flag [:before]
|
|
@@ -2793,10 +2960,10 @@ command :import do |c|
|
|
|
2793
2960
|
date_string = options[:from]
|
|
2794
2961
|
if date_string =~ / (to|through|thru|(un)?til|-+) /
|
|
2795
2962
|
dates = date_string.split(/ (to|through|thru|(un)?til|-+) /)
|
|
2796
|
-
start =
|
|
2797
|
-
finish =
|
|
2963
|
+
start = dates[0].chronify(guess: :begin)
|
|
2964
|
+
finish = dates[2].chronify(guess: :end)
|
|
2798
2965
|
else
|
|
2799
|
-
start =
|
|
2966
|
+
start = date_string.chronify(guess: :begin)
|
|
2800
2967
|
finish = false
|
|
2801
2968
|
end
|
|
2802
2969
|
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
|
@@ -2823,9 +2990,9 @@ pre do |global, _command, _options, _args|
|
|
|
2823
2990
|
|
|
2824
2991
|
$stdout.puts "doing v#{Doing::VERSION}" if global[:version]
|
|
2825
2992
|
unless STDOUT.isatty
|
|
2826
|
-
Doing::Color
|
|
2993
|
+
Doing::Color.coloring = global[:pager] ? global[:color] : false
|
|
2827
2994
|
else
|
|
2828
|
-
Doing::Color
|
|
2995
|
+
Doing::Color.coloring = global[:color]
|
|
2829
2996
|
end
|
|
2830
2997
|
|
|
2831
2998
|
# Return true to proceed; false to abort and not call the
|
|
@@ -2853,13 +3020,21 @@ end
|
|
|
2853
3020
|
|
|
2854
3021
|
around do |global, command, options, arguments, code|
|
|
2855
3022
|
# Doing.logger.debug('Pager:', "Global: #{global[:pager]}, Config: #{settings['paginate']}, Pager: #{Doing::Pager.paginate}")
|
|
2856
|
-
|
|
3023
|
+
if env_log_level.nil?
|
|
3024
|
+
Doing.logger.adjust_verbosity(global)
|
|
3025
|
+
end
|
|
2857
3026
|
|
|
2858
3027
|
if global[:stdout]
|
|
2859
3028
|
Doing.logger.logdev = $stdout
|
|
2860
3029
|
end
|
|
2861
3030
|
|
|
2862
|
-
|
|
3031
|
+
if global[:yes]
|
|
3032
|
+
Doing::Prompt.force_answer = true
|
|
3033
|
+
elsif global[:no]
|
|
3034
|
+
Doing::Prompt.force_answer = false
|
|
3035
|
+
else
|
|
3036
|
+
Doing::Prompt.default_answer = global[:default]
|
|
3037
|
+
end
|
|
2863
3038
|
|
|
2864
3039
|
if global[:config_file] && global[:config_file] != config.config_file
|
|
2865
3040
|
Doing.logger.warn(format('%sWARNING:%s %sThe use of --config_file is deprecated, please set the environment variable DOING_CONFIG instead.', colors.flamingo, colors.default, colors.boldred))
|