doing 2.1.17 → 2.1.22
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 +15 -14
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +117 -53
- data/Gemfile.lock +11 -11
- data/README.md +1 -1
- data/Rakefile +12 -4
- data/bin/doing +161 -205
- data/docs/doc/Array.html +9 -37
- data/docs/doc/BooleanTermParser/Clause.html +3 -3
- data/docs/doc/BooleanTermParser/Operator.html +3 -3
- data/docs/doc/BooleanTermParser/Query.html +3 -3
- data/docs/doc/BooleanTermParser/QueryParser.html +3 -3
- data/docs/doc/BooleanTermParser/QueryTransformer.html +3 -3
- data/docs/doc/BooleanTermParser.html +3 -3
- data/docs/doc/Doing/Color.html +3 -3
- data/docs/doc/Doing/Completion.html +3 -3
- data/docs/doc/Doing/Configuration.html +4 -3
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +3 -3
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +3 -3
- data/docs/doc/Doing/Errors/DoingStandardError.html +3 -3
- data/docs/doc/Doing/Errors/EmptyInput.html +3 -3
- data/docs/doc/Doing/Errors/NoResults.html +3 -3
- data/docs/doc/Doing/Errors/PluginException.html +3 -3
- data/docs/doc/Doing/Errors/UserCancelled.html +3 -3
- data/docs/doc/Doing/Errors/WrongCommand.html +3 -3
- data/docs/doc/Doing/Errors.html +3 -3
- data/docs/doc/Doing/Hooks.html +3 -3
- data/docs/doc/Doing/Item.html +3 -3
- data/docs/doc/Doing/Items.html +3 -3
- data/docs/doc/Doing/LogAdapter.html +3 -3
- data/docs/doc/Doing/Note.html +3 -3
- data/docs/doc/Doing/Pager.html +3 -3
- data/docs/doc/Doing/Plugins.html +3 -3
- data/docs/doc/Doing/Prompt.html +7 -7
- data/docs/doc/Doing/Section.html +3 -3
- data/docs/doc/Doing/TemplateString.html +4 -4
- data/docs/doc/Doing/Util/Backup.html +3 -3
- data/docs/doc/Doing/Util.html +3 -3
- data/docs/doc/Doing/WWID.html +66 -8
- data/docs/doc/Doing.html +6 -6
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +3 -3
- data/docs/doc/GLI/Commands.html +3 -3
- data/docs/doc/GLI.html +3 -3
- data/docs/doc/Hash.html +78 -6
- data/docs/doc/Numeric.html +3 -3
- data/docs/doc/PhraseParser/Operator.html +3 -3
- data/docs/doc/PhraseParser/PhraseClause.html +3 -3
- data/docs/doc/PhraseParser/Query.html +3 -3
- data/docs/doc/PhraseParser/QueryParser.html +3 -3
- data/docs/doc/PhraseParser/QueryTransformer.html +3 -3
- data/docs/doc/PhraseParser/TermClause.html +3 -3
- data/docs/doc/PhraseParser.html +3 -3
- data/docs/doc/Status.html +3 -3
- data/docs/doc/String.html +156 -17
- data/docs/doc/Symbol.html +3 -3
- data/docs/doc/Time.html +3 -3
- data/docs/doc/_index.html +23 -16
- data/docs/doc/class_list.html +1 -1
- data/docs/doc/file.README.html +4 -4
- data/docs/doc/frames.html +1 -1
- data/docs/doc/index.html +4 -4
- data/docs/doc/method_list.html +331 -283
- data/docs/doc/top-level-namespace.html +3 -3
- data/doing.gemspec +1 -1
- data/doing.rdoc +26 -12
- data/lib/completion/_doing.zsh +5 -5
- data/lib/completion/doing.bash +8 -8
- data/lib/completion/doing.fish +93 -15
- data/lib/doing/array.rb +5 -4
- data/lib/doing/array_chronify.rb +4 -3
- data/lib/doing/completion/fish_completion.rb +80 -11
- data/lib/doing/configuration.rb +2 -1
- data/lib/doing/hash.rb +22 -4
- data/lib/doing/item.rb +2 -2
- data/lib/doing/items.rb +3 -1
- data/lib/doing/log_adapter.rb +1 -1
- data/lib/doing/pager.rb +2 -2
- data/lib/doing/plugins/export/dayone_export.rb +1 -1
- data/lib/doing/plugins/export/markdown_export.rb +1 -1
- data/lib/doing/plugins/export/template_export.rb +8 -2
- data/lib/doing/prompt.rb +4 -2
- data/lib/doing/string.rb +25 -2
- data/lib/doing/string_chronify.rb +55 -17
- data/lib/doing/template_string.rb +7 -0
- data/lib/doing/types.rb +23 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +71 -50
- data/lib/doing.rb +1 -0
- data/lib/examples/commands/later.rb +32 -0
- data/lib/helpers/threaded_tests.rb +273 -0
- metadata +9 -6
data/bin/doing
CHANGED
|
@@ -25,12 +25,7 @@ version Doing::VERSION
|
|
|
25
25
|
hide_commands_without_desc true
|
|
26
26
|
autocomplete_commands true
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
REGEX_SORT_ORDER = /^(?:a(?:sc)?|d(?:esc)?)$/i
|
|
30
|
-
REGEX_VALUE_QUERY = /^(?:!)?@?(?:\S+) +(?:!?[<>=][=*]?|[$*^]=) +(?:.*?)$/
|
|
31
|
-
|
|
32
|
-
InvalidExportType = Class.new(RuntimeError)
|
|
33
|
-
MissingConfigFile = Class.new(RuntimeError)
|
|
28
|
+
include Doing::Types
|
|
34
29
|
|
|
35
30
|
colors = Doing::Color
|
|
36
31
|
wwid = Doing::WWID.new
|
|
@@ -70,7 +65,42 @@ if settings.dig('plugins', 'command_path')
|
|
|
70
65
|
commands_from File.expand_path(settings.dig('plugins', 'command_path'))
|
|
71
66
|
end
|
|
72
67
|
|
|
73
|
-
|
|
68
|
+
accept DateBeginString do |value|
|
|
69
|
+
if value =~ REGEX_TIME
|
|
70
|
+
res = value
|
|
71
|
+
else
|
|
72
|
+
res = value.chronify(guess: :begin, future: false)
|
|
73
|
+
end
|
|
74
|
+
raise InvalidTimeExpression, 'Invalid start date' unless res
|
|
75
|
+
|
|
76
|
+
res
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
accept DateEndString do |value|
|
|
80
|
+
if value =~ REGEX_TIME
|
|
81
|
+
res = value
|
|
82
|
+
else
|
|
83
|
+
res = value.chronify(guess: :end, future: false)
|
|
84
|
+
end
|
|
85
|
+
raise InvalidTimeExpression, 'Invalid end date' unless res
|
|
86
|
+
|
|
87
|
+
res
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
accept DateRangeString do |value|
|
|
91
|
+
start, finish = value.split_date_range
|
|
92
|
+
raise InvalidTimeExpression, 'Invalid range' unless start
|
|
93
|
+
|
|
94
|
+
finish ||= Time.now
|
|
95
|
+
[start, finish]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
accept DateIntervalString do |value|
|
|
99
|
+
res = value.chronify_qty
|
|
100
|
+
raise InvalidTimeExpression, 'Invalid time quantity' unless res
|
|
101
|
+
|
|
102
|
+
res
|
|
103
|
+
end
|
|
74
104
|
|
|
75
105
|
accept TagArray do |value|
|
|
76
106
|
value.gsub(/[, ]+/, ' ').split(' ').map { |tag| tag.sub(/^@/, '')}.map(&:strip)
|
|
@@ -97,7 +127,7 @@ desc 'Use a pager when output is longer than screen'
|
|
|
97
127
|
switch %i[p pager], default_value: settings['paginate']
|
|
98
128
|
|
|
99
129
|
desc 'Answer yes/no menus with default option'
|
|
100
|
-
switch [:default], default_value: false
|
|
130
|
+
switch [:default], default_value: false, negatable: false
|
|
101
131
|
|
|
102
132
|
desc 'Answer all yes/no menus with yes'
|
|
103
133
|
switch [:yes], negatable: false
|
|
@@ -143,6 +173,10 @@ command %i[again resume] do |c|
|
|
|
143
173
|
c.arg_name 'SECTION_NAME'
|
|
144
174
|
c.flag [:in]
|
|
145
175
|
|
|
176
|
+
c.desc 'Backdate start date by interval or set to time [4pm|20m|2h|"yesterday noon"]'
|
|
177
|
+
c.arg_name 'DATE_STRING'
|
|
178
|
+
c.flag %i[b back started], type: DateBeginString
|
|
179
|
+
|
|
146
180
|
c.desc 'Repeat last entry matching tags. Combine multiple tags with a comma. Wildcards allowed (*, ?)'
|
|
147
181
|
c.arg_name 'TAG'
|
|
148
182
|
c.flag [:tag], type: TagArray
|
|
@@ -198,6 +232,13 @@ command %i[again resume] do |c|
|
|
|
198
232
|
options[:search] = search
|
|
199
233
|
end
|
|
200
234
|
|
|
235
|
+
if options[:back]
|
|
236
|
+
date = options[:back]
|
|
237
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
|
|
238
|
+
else
|
|
239
|
+
date = Time.now
|
|
240
|
+
end
|
|
241
|
+
|
|
201
242
|
note = Doing::Note.new(options[:note])
|
|
202
243
|
note.add(Doing::Prompt.read_lines(prompt: 'Add a note')) if options[:ask]
|
|
203
244
|
|
|
@@ -208,6 +249,7 @@ command %i[again resume] do |c|
|
|
|
208
249
|
opts[:tag] = tags
|
|
209
250
|
opts[:tag_bool] = options[:bool].normalize_bool
|
|
210
251
|
opts[:interactive] = options[:interactive]
|
|
252
|
+
opts[:date] = date
|
|
211
253
|
|
|
212
254
|
wwid.repeat_last(opts)
|
|
213
255
|
end
|
|
@@ -321,7 +363,7 @@ desc 'Add a completed item with @done(date). No argument finishes last entry'
|
|
|
321
363
|
long_desc 'Use this command to add an entry after you\'ve already finished it. It will be immediately marked as @done.
|
|
322
364
|
You can modify the start and end times of the entry using the --back, --took, and --at flags, making it an easy
|
|
323
365
|
way to add entries in post and maintain accurate (albeit manual) time tracking.'
|
|
324
|
-
arg_name 'ENTRY'
|
|
366
|
+
arg_name 'ENTRY', optional: true
|
|
325
367
|
command %i[done did] do |c|
|
|
326
368
|
c.example 'doing done', desc: 'Tag the last entry @done'
|
|
327
369
|
c.example 'doing done I already finished this', desc: 'Add a new entry and immediately mark it @done'
|
|
@@ -340,24 +382,24 @@ command %i[done did] do |c|
|
|
|
340
382
|
c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm).
|
|
341
383
|
Used with --took, backdates start date)
|
|
342
384
|
c.arg_name 'DATE_STRING'
|
|
343
|
-
c.flag %i[at finished]
|
|
385
|
+
c.flag %i[at finished], type: DateEndString
|
|
344
386
|
|
|
345
387
|
c.desc 'Backdate start date by interval or set to time [4pm|20m|2h|"yesterday noon"]'
|
|
346
388
|
c.arg_name 'DATE_STRING'
|
|
347
|
-
c.flag %i[b back started]
|
|
389
|
+
c.flag %i[b back started], type: DateBeginString
|
|
348
390
|
|
|
349
391
|
c.desc %(
|
|
350
392
|
Start and end times as a date/time range `doing done --from "1am to 8am"`.
|
|
351
393
|
Overrides other date flags.
|
|
352
394
|
)
|
|
353
395
|
c.arg_name 'TIME_RANGE'
|
|
354
|
-
c.flag [:from]
|
|
396
|
+
c.flag [:from], must_match: REGEX_RANGE
|
|
355
397
|
|
|
356
398
|
c.desc %(Set completion date to start date plus interval (XX[mhd] or HH:MM).
|
|
357
399
|
If used without the --back option, the start date will be moved back to allow
|
|
358
400
|
the completion date to be the current time.)
|
|
359
401
|
c.arg_name 'INTERVAL'
|
|
360
|
-
c.flag %i[t took for]
|
|
402
|
+
c.flag %i[t took for], type: DateIntervalString
|
|
361
403
|
|
|
362
404
|
c.desc 'Section'
|
|
363
405
|
c.arg_name 'NAME'
|
|
@@ -385,23 +427,27 @@ command %i[done did] do |c|
|
|
|
385
427
|
donedate = nil
|
|
386
428
|
|
|
387
429
|
if options[:from]
|
|
388
|
-
|
|
430
|
+
options[:from] = options[:from].split(/#{REGEX_RANGE_INDICATOR}/).map do |time|
|
|
431
|
+
time =~ REGEX_TIME ? "today #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}" : time
|
|
432
|
+
end.join(' to ').split_date_range
|
|
433
|
+
date, finish_date = options[:from]
|
|
389
434
|
finish_date ||= Time.now
|
|
390
435
|
else
|
|
391
436
|
if options[:took]
|
|
392
|
-
took = options[:took]
|
|
437
|
+
took = options[:took]
|
|
393
438
|
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
|
394
439
|
end
|
|
395
440
|
|
|
396
441
|
if options[:back]
|
|
397
|
-
date = options[:back]
|
|
442
|
+
date = options[:back]
|
|
398
443
|
raise InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
|
|
399
444
|
else
|
|
400
445
|
date = options[:took] ? Time.now - took : Time.now
|
|
401
446
|
end
|
|
402
447
|
|
|
403
448
|
if options[:at]
|
|
404
|
-
finish_date = options[:at]
|
|
449
|
+
finish_date = options[:at]
|
|
450
|
+
finish_date = finish_date.chronify(guess: :begin) if finish_date.is_a? String
|
|
405
451
|
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
|
406
452
|
|
|
407
453
|
if options[:took]
|
|
@@ -417,6 +463,7 @@ command %i[done did] do |c|
|
|
|
417
463
|
end
|
|
418
464
|
|
|
419
465
|
if options[:date]
|
|
466
|
+
date = date.chronify(guess: :begin, context: :today) if date =~ REGEX_TIME
|
|
420
467
|
finish_date = wwid.verify_duration(date, finish_date) unless options[:took] || options[:from]
|
|
421
468
|
|
|
422
469
|
donedate = finish_date.strftime('%F %R')
|
|
@@ -528,7 +575,7 @@ command %i[done did] do |c|
|
|
|
528
575
|
wwid.content.push(new_entry)
|
|
529
576
|
Doing::Hooks.trigger :post_entry_added, wwid, new_entry.dup
|
|
530
577
|
wwid.write(wwid.doing_file)
|
|
531
|
-
Doing.logger.info('
|
|
578
|
+
Doing.logger.info('New entry:', %(added "#{new_entry.date.relative_date}: #{new_entry.title}" to #{section}))
|
|
532
579
|
elsif $stdin.stat.size.positive?
|
|
533
580
|
note = Doing::Note.new(options[:note])
|
|
534
581
|
d, title, note = wwid.format_input($stdin.read.strip)
|
|
@@ -553,7 +600,7 @@ command %i[done did] do |c|
|
|
|
553
600
|
Doing::Hooks.trigger :post_entry_added, wwid, new_entry.dup
|
|
554
601
|
|
|
555
602
|
wwid.write(wwid.doing_file)
|
|
556
|
-
Doing.logger.info('
|
|
603
|
+
Doing.logger.info('New entry:', %(added "#{new_entry.date.relative_date}: #{new_entry.title}" to #{section}))
|
|
557
604
|
else
|
|
558
605
|
raise EmptyInput, 'You must provide content when creating a new entry'
|
|
559
606
|
end
|
|
@@ -563,7 +610,7 @@ end
|
|
|
563
610
|
# @@finish
|
|
564
611
|
desc 'Mark last X entries as @done'
|
|
565
612
|
long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
|
|
566
|
-
arg_name 'COUNT'
|
|
613
|
+
arg_name 'COUNT', optional: true
|
|
567
614
|
command :finish do |c|
|
|
568
615
|
c.example 'doing finish', desc: 'Mark the last entry @done'
|
|
569
616
|
c.example 'doing finish --auto --section Later 10', desc: 'Add @done to any unfinished entries in the last 10 in Later, setting the finish time based on the start time of the task after it'
|
|
@@ -574,15 +621,15 @@ command :finish do |c|
|
|
|
574
621
|
|
|
575
622
|
c.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
|
|
576
623
|
c.arg_name 'DATE_STRING'
|
|
577
|
-
c.flag %i[b back]
|
|
624
|
+
c.flag %i[b back started], type: DateBeginString
|
|
578
625
|
|
|
579
626
|
c.desc 'Set the completed date to the start date plus XX[hmd]'
|
|
580
627
|
c.arg_name 'INTERVAL'
|
|
581
|
-
c.flag %i[t took for]
|
|
628
|
+
c.flag %i[t took for], type: DateIntervalString
|
|
582
629
|
|
|
583
630
|
c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm). If used, ignores --back.)
|
|
584
631
|
c.arg_name 'DATE_STRING'
|
|
585
|
-
c.flag [
|
|
632
|
+
c.flag %i[at finished], type: DateEndString
|
|
586
633
|
|
|
587
634
|
c.desc 'Finish the last X entries containing TAG.
|
|
588
635
|
Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool. Wildcards allowed (*, ?).'
|
|
@@ -639,7 +686,7 @@ command :finish do |c|
|
|
|
639
686
|
options[:fuzzy] = false
|
|
640
687
|
unless options[:auto]
|
|
641
688
|
if options[:took]
|
|
642
|
-
took = options[:took]
|
|
689
|
+
took = options[:took]
|
|
643
690
|
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
|
644
691
|
end
|
|
645
692
|
|
|
@@ -648,12 +695,13 @@ command :finish do |c|
|
|
|
648
695
|
raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
|
|
649
696
|
|
|
650
697
|
if options[:at]
|
|
651
|
-
finish_date = options[:at]
|
|
698
|
+
finish_date = options[:at]
|
|
699
|
+
finish_date = finish_date.chronify(guess: :begin) if finish_date.is_a? String
|
|
652
700
|
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
|
653
701
|
|
|
654
702
|
date = options[:took] ? finish_date - took : finish_date
|
|
655
703
|
elsif options[:back]
|
|
656
|
-
date = options[:back]
|
|
704
|
+
date = options[:back]
|
|
657
705
|
|
|
658
706
|
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
|
659
707
|
else
|
|
@@ -661,8 +709,6 @@ command :finish do |c|
|
|
|
661
709
|
end
|
|
662
710
|
end
|
|
663
711
|
|
|
664
|
-
options[:took] = options[:took].chronify_qty if options[:took]
|
|
665
|
-
|
|
666
712
|
if options[:tag].nil?
|
|
667
713
|
tags = []
|
|
668
714
|
else
|
|
@@ -711,92 +757,6 @@ command :finish do |c|
|
|
|
711
757
|
end
|
|
712
758
|
end
|
|
713
759
|
|
|
714
|
-
# @@later
|
|
715
|
-
desc 'Add an item to the Later section'
|
|
716
|
-
arg_name 'ENTRY'
|
|
717
|
-
command :later do |c|
|
|
718
|
-
c.example 'doing later "Something I\'ll think about tomorrow"', desc: 'Add an entry to the Later section'
|
|
719
|
-
c.example 'doing later -e', desc: 'Open $EDITOR to create an entry and optional note'
|
|
720
|
-
|
|
721
|
-
c.desc "Edit entry with #{Doing::Util.default_editor}"
|
|
722
|
-
c.switch %i[e editor], negatable: false, default_value: false
|
|
723
|
-
|
|
724
|
-
c.desc 'Backdate start time to date string [4pm|20m|2h|yesterday noon]'
|
|
725
|
-
c.arg_name 'DATE_STRING'
|
|
726
|
-
c.flag %i[b back]
|
|
727
|
-
|
|
728
|
-
c.desc 'Note'
|
|
729
|
-
c.arg_name 'TEXT'
|
|
730
|
-
c.flag %i[n note]
|
|
731
|
-
|
|
732
|
-
c.desc 'Prompt for note via multi-line input'
|
|
733
|
-
c.switch %i[ask], negatable: false, default_value: false
|
|
734
|
-
|
|
735
|
-
c.action do |_global_options, options, args|
|
|
736
|
-
if options[:back]
|
|
737
|
-
date = options[:back].chronify(guess: :begin)
|
|
738
|
-
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
|
739
|
-
else
|
|
740
|
-
date = Time.now
|
|
741
|
-
end
|
|
742
|
-
|
|
743
|
-
ask_note = options[:ask] && !options[:editor] && args.count.positive? ? Doing::Prompt.read_lines(prompt: 'Add a note') : ''
|
|
744
|
-
|
|
745
|
-
if options[:editor]
|
|
746
|
-
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
747
|
-
|
|
748
|
-
input = ''
|
|
749
|
-
input += date.strftime('%F %R | ')
|
|
750
|
-
input += args.empty? ? '' : args.join(' ')
|
|
751
|
-
input += "\n#{options[:note]}" if options[:note]
|
|
752
|
-
input += "\n#{ask_note}" unless ask_note.empty?
|
|
753
|
-
|
|
754
|
-
input = wwid.fork_editor(input).strip
|
|
755
|
-
|
|
756
|
-
d, title, note = wwid.format_input(input)
|
|
757
|
-
raise EmptyInput, 'No content' if title.empty?
|
|
758
|
-
|
|
759
|
-
note.add(options[:note]) if options[:note]
|
|
760
|
-
if ask_note.empty? && options[:ask]
|
|
761
|
-
ask_note = Doing::Prompt.read_lines(prompt: 'Add a note')
|
|
762
|
-
note.add(ask_note) unless ask_note.empty?
|
|
763
|
-
end
|
|
764
|
-
|
|
765
|
-
date = d.nil? ? date : d
|
|
766
|
-
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
|
767
|
-
wwid.write(wwid.doing_file)
|
|
768
|
-
elsif !args.empty?
|
|
769
|
-
d, title, note = wwid.format_input(args.join(' '))
|
|
770
|
-
date = d.nil? ? date : d
|
|
771
|
-
note.add(options[:note]) if options[:note]
|
|
772
|
-
note.add(ask_note) unless ask_note.empty?
|
|
773
|
-
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
|
774
|
-
wwid.write(wwid.doing_file)
|
|
775
|
-
elsif $stdin.stat.size.positive?
|
|
776
|
-
d, title, note = wwid.format_input($stdin.read)
|
|
777
|
-
unless d.nil?
|
|
778
|
-
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
|
779
|
-
date = d
|
|
780
|
-
end
|
|
781
|
-
note.add(options[:note]) if options[:note]
|
|
782
|
-
note.add(ask_note) unless ask_note.empty?
|
|
783
|
-
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
|
784
|
-
wwid.write(wwid.doing_file)
|
|
785
|
-
else
|
|
786
|
-
title = Doing::Prompt.read_line(prompt: 'Entry content')
|
|
787
|
-
raise EmptyInput, 'You must provide content when creating a new entry' if title.strip.empty?
|
|
788
|
-
|
|
789
|
-
note = Doing::Note.new
|
|
790
|
-
res = Doing::Prompt.yn('Add a note', default_response: false)
|
|
791
|
-
ask_note = res ? Doing::Prompt.read_lines(prompt: 'Enter note') : []
|
|
792
|
-
note.add(ask_note)
|
|
793
|
-
|
|
794
|
-
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
|
795
|
-
wwid.write(wwid.doing_file)
|
|
796
|
-
end
|
|
797
|
-
end
|
|
798
|
-
end
|
|
799
|
-
|
|
800
760
|
# @@mark @@flag
|
|
801
761
|
desc 'Mark last entry as flagged'
|
|
802
762
|
command %i[mark flag] do |c|
|
|
@@ -930,7 +890,7 @@ long_desc 'The @meanwhile tag allows you to have long-running entries that encom
|
|
|
930
890
|
This command makes it easy to start and stop these overarching entries. Just run `doing meanwhile Starting work on this
|
|
931
891
|
big project` to start a @meanwhile entry, add other entries as you work on the project, then use `doing meanwhile` by
|
|
932
892
|
itself to mark the entry as @done.'
|
|
933
|
-
arg_name 'ENTRY'
|
|
893
|
+
arg_name 'ENTRY', optional: true
|
|
934
894
|
command :meanwhile do |c|
|
|
935
895
|
c.example 'doing meanwhile "Long task that will have others after it before it\'s done"', desc: 'Add a new long-running entry, completing any current @meanwhile entry'
|
|
936
896
|
c.example 'doing meanwhile', desc: 'Finish any open @meanwhile entry'
|
|
@@ -949,7 +909,7 @@ command :meanwhile do |c|
|
|
|
949
909
|
|
|
950
910
|
c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
|
|
951
911
|
c.arg_name 'DATE_STRING'
|
|
952
|
-
c.flag %i[b back]
|
|
912
|
+
c.flag %i[b back started], type: DateBeginString
|
|
953
913
|
|
|
954
914
|
c.desc 'Note'
|
|
955
915
|
c.arg_name 'TEXT'
|
|
@@ -960,7 +920,7 @@ command :meanwhile do |c|
|
|
|
960
920
|
|
|
961
921
|
c.action do |_global_options, options, args|
|
|
962
922
|
if options[:back]
|
|
963
|
-
date = options[:back]
|
|
923
|
+
date = options[:back]
|
|
964
924
|
|
|
965
925
|
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
|
966
926
|
else
|
|
@@ -1020,7 +980,7 @@ long_desc %(
|
|
|
1020
980
|
|
|
1021
981
|
Use -e to load the last entry in a text editor where you can append a note.
|
|
1022
982
|
)
|
|
1023
|
-
arg_name 'NOTE_TEXT'
|
|
983
|
+
arg_name 'NOTE_TEXT', optional: true
|
|
1024
984
|
command :note do |c|
|
|
1025
985
|
c.example 'doing note', desc: 'Open the last entry in $EDITOR to append a note'
|
|
1026
986
|
c.example 'doing note "Just a quick annotation"', desc: 'Add a quick note to the last entry'
|
|
@@ -1088,7 +1048,6 @@ command :note do |c|
|
|
|
1088
1048
|
options[:search] = search
|
|
1089
1049
|
end
|
|
1090
1050
|
|
|
1091
|
-
|
|
1092
1051
|
last_entry = wwid.last_entry(options)
|
|
1093
1052
|
|
|
1094
1053
|
unless last_entry
|
|
@@ -1098,37 +1057,37 @@ command :note do |c|
|
|
|
1098
1057
|
|
|
1099
1058
|
last_note = last_entry.note || Doing::Note.new
|
|
1100
1059
|
new_note = Doing::Note.new
|
|
1101
|
-
ask_note = options[:ask] ? Doing::Prompt.read_lines(prompt: 'Add a note') : ''
|
|
1102
1060
|
|
|
1103
|
-
if
|
|
1104
|
-
|
|
1061
|
+
if $stdin.stat.size.positive?
|
|
1062
|
+
new_note.add($stdin.read.strip)
|
|
1063
|
+
end
|
|
1105
1064
|
|
|
1106
|
-
|
|
1065
|
+
unless args.empty?
|
|
1066
|
+
new_note.add(args.join(' '))
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
if options[:editor]
|
|
1070
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
1107
1071
|
|
|
1108
1072
|
if options[:remove]
|
|
1109
|
-
|
|
1073
|
+
input = Doing::Note.new
|
|
1110
1074
|
else
|
|
1111
|
-
|
|
1075
|
+
input = last_entry.note || Doing::Note.new
|
|
1112
1076
|
end
|
|
1113
1077
|
|
|
1078
|
+
input.add(new_note)
|
|
1114
1079
|
|
|
1115
|
-
|
|
1116
|
-
input.add(ask_note) unless ask_note.empty?
|
|
1117
|
-
|
|
1118
|
-
input = wwid.fork_editor(prev_input.strip_lines.join("\n"), message: nil).strip
|
|
1119
|
-
note = input
|
|
1080
|
+
new_note = Doing::Note.new(wwid.fork_editor(input.strip_lines.join("\n"), message: nil).strip)
|
|
1120
1081
|
options[:remove] = true
|
|
1121
|
-
|
|
1122
|
-
elsif !args.empty?
|
|
1123
|
-
new_note.add(args.join(' '))
|
|
1124
|
-
elsif $stdin.stat.size.positive?
|
|
1125
|
-
new_note.add($stdin.read.strip)
|
|
1126
|
-
else
|
|
1127
|
-
raise EmptyInput, 'You must provide content when adding a note' unless options[:remove] || !ask_note.empty?
|
|
1082
|
+
end
|
|
1128
1083
|
|
|
1084
|
+
if (new_note.empty? && !options[:remove]) || options[:ask]
|
|
1085
|
+
$stderr.puts last_note unless last_note.empty?
|
|
1086
|
+
$stderr.puts new_note unless new_note.empty?
|
|
1087
|
+
new_note.add(Doing::Prompt.read_lines(prompt: 'Add a note'))
|
|
1129
1088
|
end
|
|
1130
1089
|
|
|
1131
|
-
|
|
1090
|
+
raise EmptyInput, 'You must provide content when adding a note' unless options[:remove] || !new_note.empty?
|
|
1132
1091
|
|
|
1133
1092
|
if last_note.equal?(new_note)
|
|
1134
1093
|
Doing.logger.debug('Skipped:', 'No note change')
|
|
@@ -1169,7 +1128,7 @@ command %i[now next] do |c|
|
|
|
1169
1128
|
|
|
1170
1129
|
c.desc 'Backdate start time [4pm|20m|2h|"yesterday noon"]'
|
|
1171
1130
|
c.arg_name 'DATE_STRING'
|
|
1172
|
-
c.flag %i[b back started]
|
|
1131
|
+
c.flag %i[b back started], type: DateBeginString
|
|
1173
1132
|
|
|
1174
1133
|
c.desc 'Timed entry, marks last entry in section as @done'
|
|
1175
1134
|
c.switch %i[f finish_last], negatable: false, default_value: false
|
|
@@ -1187,7 +1146,7 @@ command %i[now next] do |c|
|
|
|
1187
1146
|
|
|
1188
1147
|
c.action do |_global_options, options, args|
|
|
1189
1148
|
if options[:back]
|
|
1190
|
-
date = options[:back]
|
|
1149
|
+
date = options[:back]
|
|
1191
1150
|
|
|
1192
1151
|
raise InvalidTimeExpression.new('unable to parse date string', topic: 'Parser:') if date.nil?
|
|
1193
1152
|
else
|
|
@@ -1211,7 +1170,7 @@ command %i[now next] do |c|
|
|
|
1211
1170
|
input += "\n#{ask_note}" unless ask_note.empty?
|
|
1212
1171
|
input = wwid.fork_editor(input).strip
|
|
1213
1172
|
|
|
1214
|
-
|
|
1173
|
+
d, title, note = wwid.format_input(input)
|
|
1215
1174
|
raise EmptyInput, 'No content' if title.strip.empty?
|
|
1216
1175
|
|
|
1217
1176
|
if ask_note.empty? && options[:ask]
|
|
@@ -1219,6 +1178,7 @@ command %i[now next] do |c|
|
|
|
1219
1178
|
note.add(ask_note) unless ask_note.empty?
|
|
1220
1179
|
end
|
|
1221
1180
|
|
|
1181
|
+
date = d.nil? ? date : d
|
|
1222
1182
|
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
1223
1183
|
wwid.write(wwid.doing_file)
|
|
1224
1184
|
elsif args.length.positive?
|
|
@@ -1265,7 +1225,7 @@ desc 'Reset the start time of an entry'
|
|
|
1265
1225
|
long_desc 'Update the start time of the last entry or the last entry matching a tag/search filter.
|
|
1266
1226
|
If no argument is provided, the start time will be reset to the current time.
|
|
1267
1227
|
If a date string is provided as an argument, the start time will be set to the parsed result.'
|
|
1268
|
-
arg_name 'DATE_STRING'
|
|
1228
|
+
arg_name 'DATE_STRING', optional: true
|
|
1269
1229
|
command %i[reset begin] do |c|
|
|
1270
1230
|
c.example 'doing reset', desc: 'Reset the start time of the last entry to the current time'
|
|
1271
1231
|
c.example 'doing reset --tag project1', desc: 'Reset the start time of the most recent entry tagged @project1 to the current time'
|
|
@@ -1279,6 +1239,9 @@ command %i[reset begin] do |c|
|
|
|
1279
1239
|
c.desc 'Resume entry (remove @done)'
|
|
1280
1240
|
c.switch %i[r resume], default_value: true
|
|
1281
1241
|
|
|
1242
|
+
c.desc 'Change start date but do not remove @done (shortcut for --no-resume)'
|
|
1243
|
+
c.switch [:n]
|
|
1244
|
+
|
|
1282
1245
|
c.desc 'Reset last entry matching tag. Wildcards allowed (*, ?)'
|
|
1283
1246
|
c.arg_name 'TAG'
|
|
1284
1247
|
c.flag [:tag]
|
|
@@ -1416,11 +1379,11 @@ command :select do |c|
|
|
|
1416
1379
|
|
|
1417
1380
|
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'
|
|
1418
1381
|
c.arg_name 'DATE_STRING'
|
|
1419
|
-
c.flag [:before]
|
|
1382
|
+
c.flag [:before], type: DateBeginString
|
|
1420
1383
|
|
|
1421
1384
|
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'
|
|
1422
1385
|
c.arg_name 'DATE_STRING'
|
|
1423
|
-
c.flag [:after]
|
|
1386
|
+
c.flag [:after], type: DateEndString
|
|
1424
1387
|
|
|
1425
1388
|
c.desc %(
|
|
1426
1389
|
Date range to show, or a single day to filter date on.
|
|
@@ -1431,7 +1394,7 @@ command :select do |c|
|
|
|
1431
1394
|
by time of day.
|
|
1432
1395
|
)
|
|
1433
1396
|
c.arg_name 'DATE_OR_RANGE'
|
|
1434
|
-
c.flag [:from]
|
|
1397
|
+
c.flag [:from], type: DateRangeString
|
|
1435
1398
|
|
|
1436
1399
|
c.desc 'Force exact search string matching (case sensitive)'
|
|
1437
1400
|
c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
|
|
@@ -1698,11 +1661,11 @@ command %i[grep search] do |c|
|
|
|
1698
1661
|
|
|
1699
1662
|
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'
|
|
1700
1663
|
c.arg_name 'DATE_STRING'
|
|
1701
|
-
c.flag [:before]
|
|
1664
|
+
c.flag [:before], type: DateBeginString
|
|
1702
1665
|
|
|
1703
1666
|
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'
|
|
1704
1667
|
c.arg_name 'DATE_STRING'
|
|
1705
|
-
c.flag [:after]
|
|
1668
|
+
c.flag [:after], type: DateEndString
|
|
1706
1669
|
|
|
1707
1670
|
c.desc %(
|
|
1708
1671
|
Date range to show, or a single day to filter date on.
|
|
@@ -1713,7 +1676,7 @@ command %i[grep search] do |c|
|
|
|
1713
1676
|
by time of day.
|
|
1714
1677
|
)
|
|
1715
1678
|
c.arg_name 'DATE_OR_RANGE'
|
|
1716
|
-
c.flag [:from]
|
|
1679
|
+
c.flag [:from], type: DateRangeString
|
|
1717
1680
|
|
|
1718
1681
|
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
|
1719
1682
|
c.arg_name 'FORMAT'
|
|
@@ -2011,11 +1974,11 @@ command :show do |c|
|
|
|
2011
1974
|
|
|
2012
1975
|
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'
|
|
2013
1976
|
c.arg_name 'DATE_STRING'
|
|
2014
|
-
c.flag [:before]
|
|
1977
|
+
c.flag [:before], type: DateBeginString
|
|
2015
1978
|
|
|
2016
1979
|
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'
|
|
2017
1980
|
c.arg_name 'DATE_STRING'
|
|
2018
|
-
c.flag [:after]
|
|
1981
|
+
c.flag [:after], type: DateEndString
|
|
2019
1982
|
|
|
2020
1983
|
c.desc %(
|
|
2021
1984
|
Date range to show, or a single day to filter date on.
|
|
@@ -2027,7 +1990,7 @@ command :show do |c|
|
|
|
2027
1990
|
)
|
|
2028
1991
|
|
|
2029
1992
|
c.arg_name 'DATE_OR_RANGE'
|
|
2030
|
-
c.flag [:from]
|
|
1993
|
+
c.flag [:from], type: DateRangeString
|
|
2031
1994
|
|
|
2032
1995
|
c.desc 'Search filter, surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
|
|
2033
1996
|
c.arg_name 'QUERY'
|
|
@@ -2322,8 +2285,8 @@ command :today do |c|
|
|
|
2322
2285
|
c.desc %(
|
|
2323
2286
|
Time range to show `doing today --from "12pm to 4pm"`
|
|
2324
2287
|
)
|
|
2325
|
-
c.arg_name '
|
|
2326
|
-
c.flag [:from]
|
|
2288
|
+
c.arg_name 'TIME_RANGE'
|
|
2289
|
+
c.flag [:from], type: DateRangeString
|
|
2327
2290
|
|
|
2328
2291
|
c.action do |_global_options, options, _args|
|
|
2329
2292
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
@@ -2336,7 +2299,7 @@ command :today do |c|
|
|
|
2336
2299
|
end
|
|
2337
2300
|
end
|
|
2338
2301
|
|
|
2339
|
-
#
|
|
2302
|
+
# @@on
|
|
2340
2303
|
desc 'List entries for a date'
|
|
2341
2304
|
long_desc %(Date argument can be natural language. "thursday" would be interpreted as "last thursday,"
|
|
2342
2305
|
and "2d" would be interpreted as "two days ago." If you use "to" or "through" between two dates,
|
|
@@ -2375,16 +2338,11 @@ command :on do |c|
|
|
|
2375
2338
|
|
|
2376
2339
|
raise MissingArgument, 'Missing date argument' if args.empty?
|
|
2377
2340
|
|
|
2378
|
-
date_string = args.join(' ')
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
dates = date_string.split(/ (to|through|thru) /)
|
|
2382
|
-
start = dates[0].chronify(guess: :begin)
|
|
2383
|
-
finish = dates[2].chronify(guess: :end)
|
|
2384
|
-
else
|
|
2385
|
-
start = date_string.chronify(guess: :begin)
|
|
2386
|
-
finish = false
|
|
2341
|
+
date_string = args.join(' ').strip
|
|
2342
|
+
if date_string =~ /^tod(?:ay)?/i
|
|
2343
|
+
date_string = 'today to tomorrow 12am'
|
|
2387
2344
|
end
|
|
2345
|
+
start, finish = date_string.split_date_range
|
|
2388
2346
|
|
|
2389
2347
|
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
|
2390
2348
|
|
|
@@ -2536,11 +2494,11 @@ command :view do |c|
|
|
|
2536
2494
|
|
|
2537
2495
|
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'
|
|
2538
2496
|
c.arg_name 'DATE_STRING'
|
|
2539
|
-
c.flag [:before]
|
|
2497
|
+
c.flag [:before], type: DateBeginString
|
|
2540
2498
|
|
|
2541
2499
|
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'
|
|
2542
2500
|
c.arg_name 'DATE_STRING'
|
|
2543
|
-
c.flag [:after]
|
|
2501
|
+
c.flag [:after], type: DateEndString
|
|
2544
2502
|
|
|
2545
2503
|
c.desc %(
|
|
2546
2504
|
Date range to show, or a single day to filter date on.
|
|
@@ -2551,7 +2509,7 @@ command :view do |c|
|
|
|
2551
2509
|
by time of day.
|
|
2552
2510
|
)
|
|
2553
2511
|
c.arg_name 'DATE_OR_RANGE'
|
|
2554
|
-
c.flag [:from]
|
|
2512
|
+
c.flag [:from], type: DateRangeString
|
|
2555
2513
|
|
|
2556
2514
|
c.desc 'Only show items with recorded time intervals (override view settings)'
|
|
2557
2515
|
c.switch [:only_timed], default_value: false, negatable: false
|
|
@@ -2588,17 +2546,17 @@ command :view do |c|
|
|
|
2588
2546
|
view = wwid.get_view(title)
|
|
2589
2547
|
|
|
2590
2548
|
if view
|
|
2591
|
-
page_title = view
|
|
2549
|
+
page_title = view['title'] || title.cap_first
|
|
2592
2550
|
only_timed = if (view.key?('only_timed') && view['only_timed']) || options[:only_timed]
|
|
2593
2551
|
true
|
|
2594
2552
|
else
|
|
2595
2553
|
false
|
|
2596
2554
|
end
|
|
2597
2555
|
|
|
2598
|
-
template = view
|
|
2599
|
-
date_format = view
|
|
2556
|
+
template = view['template'] || nil
|
|
2557
|
+
date_format = view['date_format'] || nil
|
|
2600
2558
|
|
|
2601
|
-
tags_color = view
|
|
2559
|
+
tags_color = view['tags_color'] || nil
|
|
2602
2560
|
tag_filter = false
|
|
2603
2561
|
if options[:tag]
|
|
2604
2562
|
tag_filter = { 'tags' => [], 'bool' => 'OR' }
|
|
@@ -2628,19 +2586,19 @@ command :view do |c|
|
|
|
2628
2586
|
section = if options[:section]
|
|
2629
2587
|
section
|
|
2630
2588
|
else
|
|
2631
|
-
view
|
|
2589
|
+
view['section'] || settings['current_section']
|
|
2632
2590
|
end
|
|
2633
|
-
order = view
|
|
2591
|
+
order = view['order']&.normalize_order || 'asc'
|
|
2634
2592
|
|
|
2635
2593
|
totals = if options[:totals]
|
|
2636
2594
|
true
|
|
2637
2595
|
else
|
|
2638
|
-
view
|
|
2596
|
+
view['totals'] || false
|
|
2639
2597
|
end
|
|
2640
2598
|
tag_order = if options[:tag_order]
|
|
2641
2599
|
options[:tag_order].normalize_order
|
|
2642
2600
|
else
|
|
2643
|
-
view
|
|
2601
|
+
view['tag_order']&.normalize_order || 'asc'
|
|
2644
2602
|
end
|
|
2645
2603
|
|
|
2646
2604
|
options[:times] = true if totals
|
|
@@ -2669,7 +2627,6 @@ command :view do |c|
|
|
|
2669
2627
|
|
|
2670
2628
|
opts = options.dup
|
|
2671
2629
|
opts[:age] = options[:age].normalize_age(:newest)
|
|
2672
|
-
opts[:view_template] = title
|
|
2673
2630
|
opts[:count] = count
|
|
2674
2631
|
opts[:format] = date_format
|
|
2675
2632
|
opts[:highlight] = options[:color]
|
|
@@ -2686,6 +2643,7 @@ command :view do |c|
|
|
|
2686
2643
|
opts[:tags_color] = tags_color
|
|
2687
2644
|
opts[:template] = template
|
|
2688
2645
|
opts[:totals] = totals
|
|
2646
|
+
opts[:view_template] = title
|
|
2689
2647
|
|
|
2690
2648
|
Doing::Pager.page wwid.list_section(opts)
|
|
2691
2649
|
elsif title.instance_of?(FalseClass)
|
|
@@ -2735,11 +2693,9 @@ command :yesterday do |c|
|
|
|
2735
2693
|
c.arg_name 'TIME_STRING'
|
|
2736
2694
|
c.flag [:after]
|
|
2737
2695
|
|
|
2738
|
-
c.desc
|
|
2739
|
-
Time range to show, e.g. `doing yesterday --from "1am to 8am"`
|
|
2740
|
-
)
|
|
2696
|
+
c.desc 'Time range to show, e.g. `doing yesterday --from "1am to 8am"`'
|
|
2741
2697
|
c.arg_name 'TIME_RANGE'
|
|
2742
|
-
c.flag [:from]
|
|
2698
|
+
c.flag [:from], must_match: REGEX_TIME_RANGE
|
|
2743
2699
|
|
|
2744
2700
|
c.desc 'Tag sort direction (asc|desc)'
|
|
2745
2701
|
c.arg_name 'DIRECTION'
|
|
@@ -2751,9 +2707,9 @@ command :yesterday do |c|
|
|
|
2751
2707
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2752
2708
|
|
|
2753
2709
|
if options[:from]
|
|
2754
|
-
options[:from] = options[:from].split(/
|
|
2710
|
+
options[:from] = options[:from].split(/#{REGEX_RANGE_INDICATOR}/).map do |time|
|
|
2755
2711
|
"yesterday #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}"
|
|
2756
|
-
end.join(' to ')
|
|
2712
|
+
end.join(' to ').split_date_range
|
|
2757
2713
|
end
|
|
2758
2714
|
|
|
2759
2715
|
opt = {
|
|
@@ -2991,8 +2947,7 @@ command :config do |c|
|
|
|
2991
2947
|
value = options[:remove] ? nil : args.pop
|
|
2992
2948
|
keypath = args.join('.')
|
|
2993
2949
|
real_path = config.resolve_key_path(keypath, create: true)
|
|
2994
|
-
|
|
2995
|
-
old_value = settings.dig(*real_path) || nil
|
|
2950
|
+
old_value = settings.dig(*real_path)
|
|
2996
2951
|
old_type = old_value&.class.to_s || nil
|
|
2997
2952
|
|
|
2998
2953
|
if old_value.is_a?(Hash) && !options[:remove]
|
|
@@ -3018,7 +2973,6 @@ command :config do |c|
|
|
|
3018
2973
|
else
|
|
3019
2974
|
current_value = cfg.dig(*real_path)
|
|
3020
2975
|
cfg.deep_set(real_path, value.set_type(old_type))
|
|
3021
|
-
|
|
3022
2976
|
$stderr.puts "#{' Key path:'.yellow} #{real_path.join('->').boldwhite}"
|
|
3023
2977
|
$stderr.puts "#{'Inherited:'.yellow} #{(old_value ? old_value.to_s : 'empty').boldwhite}"
|
|
3024
2978
|
$stderr.puts "#{' Current:'.yellow} #{ (current_value ? current_value.to_s : 'empty').boldwhite }"
|
|
@@ -3178,7 +3132,7 @@ command %i[archive move] do |c|
|
|
|
3178
3132
|
c.desc 'Archive entries older than date
|
|
3179
3133
|
(Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
|
|
3180
3134
|
c.arg_name 'DATE_STRING'
|
|
3181
|
-
c.flag [:before]
|
|
3135
|
+
c.flag [:before], type: DateEndString
|
|
3182
3136
|
|
|
3183
3137
|
c.action do |_global_options, options, args|
|
|
3184
3138
|
options[:fuzzy] = false
|
|
@@ -3269,11 +3223,11 @@ command :import do |c|
|
|
|
3269
3223
|
# TODO: Allow time range filtering
|
|
3270
3224
|
c.desc 'Import entries older than date'
|
|
3271
3225
|
c.arg_name 'DATE_STRING'
|
|
3272
|
-
c.flag [:before]
|
|
3226
|
+
c.flag [:before], type: DateBeginString
|
|
3273
3227
|
|
|
3274
3228
|
c.desc 'Import entries newer than date'
|
|
3275
3229
|
c.arg_name 'DATE_STRING'
|
|
3276
|
-
c.flag [:after]
|
|
3230
|
+
c.flag [:after], type: DateEndString
|
|
3277
3231
|
|
|
3278
3232
|
c.desc %(
|
|
3279
3233
|
Date range to import. Date range argument should be quoted. Date specifications can be natural language.
|
|
@@ -3281,7 +3235,7 @@ command :import do |c|
|
|
|
3281
3235
|
Has no effect unless the import plugin has implemented date range filtering.
|
|
3282
3236
|
)
|
|
3283
3237
|
c.arg_name 'DATE_OR_RANGE'
|
|
3284
|
-
c.flag %i[f from]
|
|
3238
|
+
c.flag %i[f from], type: DateRangeString
|
|
3285
3239
|
|
|
3286
3240
|
c.desc 'Allow entries that overlap existing times'
|
|
3287
3241
|
c.switch [:overlap], negatable: true
|
|
@@ -3299,24 +3253,19 @@ command :import do |c|
|
|
|
3299
3253
|
end
|
|
3300
3254
|
|
|
3301
3255
|
if options[:from]
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
finish = date_string.chronify(guess: :end)
|
|
3310
|
-
end
|
|
3311
|
-
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
|
3312
|
-
dates = [start, finish]
|
|
3256
|
+
options[:date_filter] = options[:from]
|
|
3257
|
+
|
|
3258
|
+
raise InvalidTimeExpression, 'Unrecognized date string' unless options[:date_filter][0]
|
|
3259
|
+
elsif options[:before] || options[:after]
|
|
3260
|
+
options[:date_filter] = [nil, nil]
|
|
3261
|
+
options[:date_filter][1] = options[:before] || Time.now + (1 << 64)
|
|
3262
|
+
options[:date_filter][0] = options[:after] || Time.now - (1 << 64)
|
|
3313
3263
|
end
|
|
3314
3264
|
|
|
3315
3265
|
options[:case] = options[:case].normalize_case
|
|
3316
3266
|
|
|
3317
3267
|
if options[:type] =~ Doing::Plugins.plugin_regex(type: :import)
|
|
3318
3268
|
options[:no_overlap] = !options[:overlap]
|
|
3319
|
-
options[:date_filter] = dates
|
|
3320
3269
|
wwid.import(args, options)
|
|
3321
3270
|
wwid.write(wwid.doing_file)
|
|
3322
3271
|
else
|
|
@@ -3667,9 +3616,9 @@ command :commands_accepting do |c|
|
|
|
3667
3616
|
end
|
|
3668
3617
|
|
|
3669
3618
|
if o[:column]
|
|
3670
|
-
puts cmds
|
|
3619
|
+
puts cmds.sort
|
|
3671
3620
|
else
|
|
3672
|
-
puts "Commands accepting --#{option}: #{cmds.join(', ')}"
|
|
3621
|
+
puts "Commands accepting --#{option}: #{cmds.sort.join(', ')}"
|
|
3673
3622
|
end
|
|
3674
3623
|
end
|
|
3675
3624
|
end
|
|
@@ -3700,7 +3649,7 @@ pre do |global, _command, _options, _args|
|
|
|
3700
3649
|
Doing::Pager.paginate = global[:pager]
|
|
3701
3650
|
|
|
3702
3651
|
$stdout.puts "doing v#{Doing::VERSION}" if global[:version]
|
|
3703
|
-
unless
|
|
3652
|
+
unless $stdout.isatty
|
|
3704
3653
|
Doing::Color.coloring = global[:pager] ? global[:color] : false
|
|
3705
3654
|
else
|
|
3706
3655
|
Doing::Color.coloring = global[:color]
|
|
@@ -3714,7 +3663,9 @@ pre do |global, _command, _options, _args|
|
|
|
3714
3663
|
end
|
|
3715
3664
|
|
|
3716
3665
|
on_error do |exception|
|
|
3717
|
-
if exception.kind_of?(
|
|
3666
|
+
if exception.kind_of?(GLI::UnknownCommand)
|
|
3667
|
+
exit run(['now'].concat(ARGV))
|
|
3668
|
+
elsif exception.kind_of?(SystemExit)
|
|
3718
3669
|
false
|
|
3719
3670
|
else
|
|
3720
3671
|
# Doing.logger.error('Fatal:', exception)
|
|
@@ -3748,7 +3699,12 @@ around do |global, command, options, arguments, code|
|
|
|
3748
3699
|
Doing::Prompt.force_answer = false
|
|
3749
3700
|
Doing.config.force_answer = false
|
|
3750
3701
|
else
|
|
3751
|
-
Doing::Prompt.default_answer =
|
|
3702
|
+
Doing::Prompt.default_answer = if $stdout.isatty
|
|
3703
|
+
global[:default]
|
|
3704
|
+
else
|
|
3705
|
+
true
|
|
3706
|
+
end
|
|
3707
|
+
|
|
3752
3708
|
Doing.config.force_answer = global[:default] ? true : false
|
|
3753
3709
|
end
|
|
3754
3710
|
|