doing 2.1.18 → 2.1.23
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 -16
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +121 -53
- data/Gemfile.lock +11 -11
- data/README.md +1 -1
- data/Rakefile +12 -4
- data/bin/doing +297 -234
- data/docs/doc/Array.html +7 -30
- 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 +6 -5
- 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/Types.html +201 -0
- data/docs/doc/Doing/Util/Backup.html +3 -3
- data/docs/doc/Doing/Util.html +4 -7
- data/docs/doc/Doing/WWID.html +66 -8
- data/docs/doc/Doing.html +6 -6
- data/docs/doc/GLI/Commands/Help.html +185 -0
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +3 -3
- data/docs/doc/GLI/Commands.html +7 -5
- data/docs/doc/GLI.html +6 -4
- data/docs/doc/Hash.html +80 -16
- 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 +195 -26
- data/docs/doc/Symbol.html +3 -3
- data/docs/doc/Time.html +3 -3
- data/docs/doc/_index.html +22 -8
- 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 +334 -270
- data/docs/doc/top-level-namespace.html +3 -3
- data/docs/index.md +1 -1
- data/doing.gemspec +1 -1
- data/doing.rdoc +173 -15
- data/lib/completion/_doing.zsh +20 -20
- data/lib/completion/doing.bash +37 -26
- data/lib/completion/doing.fish +114 -16
- data/lib/doing/array.rb +5 -4
- data/lib/doing/array_chronify.rb +4 -3
- data/lib/doing/changelog/change.rb +115 -0
- data/lib/doing/changelog/changes.rb +73 -0
- data/lib/doing/changelog/entry.rb +21 -0
- data/lib/doing/changelog/version.rb +97 -0
- data/lib/doing/changelog.rb +6 -0
- data/lib/doing/completion/fish_completion.rb +80 -11
- data/lib/doing/configuration.rb +17 -8
- data/lib/doing/hash.rb +25 -6
- data/lib/doing/help_monkey_patch.rb +31 -0
- data/lib/doing/hooks.rb +5 -1
- data/lib/doing/item.rb +10 -25
- 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 +9 -3
- data/lib/doing/prompt.rb +4 -2
- data/lib/doing/string.rb +44 -12
- data/lib/doing/string_chronify.rb +56 -18
- data/lib/doing/template_string.rb +7 -0
- data/lib/doing/types.rb +25 -0
- data/lib/doing/util.rb +2 -1
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +93 -69
- data/lib/doing.rb +2 -0
- data/lib/examples/commands/later.rb +32 -0
- data/lib/helpers/threaded_tests.rb +286 -0
- metadata +17 -6
data/bin/doing
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
|
5
5
|
require 'gli'
|
|
6
|
+
require 'doing/help_monkey_patch'
|
|
6
7
|
require 'doing'
|
|
7
8
|
require 'tempfile'
|
|
8
9
|
require 'pp'
|
|
@@ -21,16 +22,12 @@ end
|
|
|
21
22
|
|
|
22
23
|
include GLI::App
|
|
23
24
|
include Doing::Errors
|
|
25
|
+
|
|
24
26
|
version Doing::VERSION
|
|
25
27
|
hide_commands_without_desc true
|
|
26
28
|
autocomplete_commands true
|
|
27
29
|
|
|
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)
|
|
30
|
+
include Doing::Types
|
|
34
31
|
|
|
35
32
|
colors = Doing::Color
|
|
36
33
|
wwid = Doing::WWID.new
|
|
@@ -70,7 +67,49 @@ if settings.dig('plugins', 'command_path')
|
|
|
70
67
|
commands_from File.expand_path(settings.dig('plugins', 'command_path'))
|
|
71
68
|
end
|
|
72
69
|
|
|
73
|
-
|
|
70
|
+
accept TemplateName do |value|
|
|
71
|
+
res = settings['templates'].keys.select { |k| k =~ value.to_rx(distance: 2) }
|
|
72
|
+
raise InvalidArgument, "Unknown template: #{value}" if res.empty?
|
|
73
|
+
|
|
74
|
+
res.group_by(&:length).min.last[0]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
accept DateBeginString do |value|
|
|
78
|
+
if value =~ REGEX_TIME
|
|
79
|
+
res = value
|
|
80
|
+
else
|
|
81
|
+
res = value.chronify(guess: :begin, future: false)
|
|
82
|
+
end
|
|
83
|
+
raise InvalidTimeExpression, 'Invalid start date' unless res
|
|
84
|
+
|
|
85
|
+
res
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
accept DateEndString do |value|
|
|
89
|
+
if value =~ REGEX_TIME
|
|
90
|
+
res = value
|
|
91
|
+
else
|
|
92
|
+
res = value.chronify(guess: :end, future: false)
|
|
93
|
+
end
|
|
94
|
+
raise InvalidTimeExpression, 'Invalid end date' unless res
|
|
95
|
+
|
|
96
|
+
res
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
accept DateRangeString do |value|
|
|
100
|
+
start, finish = value.split_date_range
|
|
101
|
+
raise InvalidTimeExpression, 'Invalid range' unless start
|
|
102
|
+
|
|
103
|
+
finish ||= Time.now
|
|
104
|
+
[start, finish]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
accept DateIntervalString do |value|
|
|
108
|
+
res = value.chronify_qty
|
|
109
|
+
raise InvalidTimeExpression, 'Invalid time quantity' unless res
|
|
110
|
+
|
|
111
|
+
res
|
|
112
|
+
end
|
|
74
113
|
|
|
75
114
|
accept TagArray do |value|
|
|
76
115
|
value.gsub(/[, ]+/, ' ').split(' ').map { |tag| tag.sub(/^@/, '')}.map(&:strip)
|
|
@@ -97,7 +136,7 @@ desc 'Use a pager when output is longer than screen'
|
|
|
97
136
|
switch %i[p pager], default_value: settings['paginate']
|
|
98
137
|
|
|
99
138
|
desc 'Answer yes/no menus with default option'
|
|
100
|
-
switch [:default], default_value: false
|
|
139
|
+
switch [:default], default_value: false, negatable: false
|
|
101
140
|
|
|
102
141
|
desc 'Answer all yes/no menus with yes'
|
|
103
142
|
switch [:yes], negatable: false
|
|
@@ -143,6 +182,10 @@ command %i[again resume] do |c|
|
|
|
143
182
|
c.arg_name 'SECTION_NAME'
|
|
144
183
|
c.flag [:in]
|
|
145
184
|
|
|
185
|
+
c.desc 'Backdate start date by interval or set to time [4pm|20m|2h|"yesterday noon"]'
|
|
186
|
+
c.arg_name 'DATE_STRING'
|
|
187
|
+
c.flag %i[b back started], type: DateBeginString
|
|
188
|
+
|
|
146
189
|
c.desc 'Repeat last entry matching tags. Combine multiple tags with a comma. Wildcards allowed (*, ?)'
|
|
147
190
|
c.arg_name 'TAG'
|
|
148
191
|
c.flag [:tag], type: TagArray
|
|
@@ -198,6 +241,13 @@ command %i[again resume] do |c|
|
|
|
198
241
|
options[:search] = search
|
|
199
242
|
end
|
|
200
243
|
|
|
244
|
+
if options[:back]
|
|
245
|
+
date = options[:back]
|
|
246
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
|
|
247
|
+
else
|
|
248
|
+
date = Time.now
|
|
249
|
+
end
|
|
250
|
+
|
|
201
251
|
note = Doing::Note.new(options[:note])
|
|
202
252
|
note.add(Doing::Prompt.read_lines(prompt: 'Add a note')) if options[:ask]
|
|
203
253
|
|
|
@@ -208,6 +258,7 @@ command %i[again resume] do |c|
|
|
|
208
258
|
opts[:tag] = tags
|
|
209
259
|
opts[:tag_bool] = options[:bool].normalize_bool
|
|
210
260
|
opts[:interactive] = options[:interactive]
|
|
261
|
+
opts[:date] = date
|
|
211
262
|
|
|
212
263
|
wwid.repeat_last(opts)
|
|
213
264
|
end
|
|
@@ -321,7 +372,7 @@ desc 'Add a completed item with @done(date). No argument finishes last entry'
|
|
|
321
372
|
long_desc 'Use this command to add an entry after you\'ve already finished it. It will be immediately marked as @done.
|
|
322
373
|
You can modify the start and end times of the entry using the --back, --took, and --at flags, making it an easy
|
|
323
374
|
way to add entries in post and maintain accurate (albeit manual) time tracking.'
|
|
324
|
-
arg_name 'ENTRY'
|
|
375
|
+
arg_name 'ENTRY', optional: true
|
|
325
376
|
command %i[done did] do |c|
|
|
326
377
|
c.example 'doing done', desc: 'Tag the last entry @done'
|
|
327
378
|
c.example 'doing done I already finished this', desc: 'Add a new entry and immediately mark it @done'
|
|
@@ -340,24 +391,24 @@ command %i[done did] do |c|
|
|
|
340
391
|
c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm).
|
|
341
392
|
Used with --took, backdates start date)
|
|
342
393
|
c.arg_name 'DATE_STRING'
|
|
343
|
-
c.flag %i[at finished]
|
|
394
|
+
c.flag %i[at finished], type: DateEndString
|
|
344
395
|
|
|
345
396
|
c.desc 'Backdate start date by interval or set to time [4pm|20m|2h|"yesterday noon"]'
|
|
346
397
|
c.arg_name 'DATE_STRING'
|
|
347
|
-
c.flag %i[b back started]
|
|
398
|
+
c.flag %i[b back started], type: DateBeginString
|
|
348
399
|
|
|
349
400
|
c.desc %(
|
|
350
401
|
Start and end times as a date/time range `doing done --from "1am to 8am"`.
|
|
351
402
|
Overrides other date flags.
|
|
352
403
|
)
|
|
353
404
|
c.arg_name 'TIME_RANGE'
|
|
354
|
-
c.flag [:from]
|
|
405
|
+
c.flag [:from], must_match: REGEX_RANGE
|
|
355
406
|
|
|
356
407
|
c.desc %(Set completion date to start date plus interval (XX[mhd] or HH:MM).
|
|
357
408
|
If used without the --back option, the start date will be moved back to allow
|
|
358
409
|
the completion date to be the current time.)
|
|
359
410
|
c.arg_name 'INTERVAL'
|
|
360
|
-
c.flag %i[t took for]
|
|
411
|
+
c.flag %i[t took for], type: DateIntervalString
|
|
361
412
|
|
|
362
413
|
c.desc 'Section'
|
|
363
414
|
c.arg_name 'NAME'
|
|
@@ -385,23 +436,27 @@ command %i[done did] do |c|
|
|
|
385
436
|
donedate = nil
|
|
386
437
|
|
|
387
438
|
if options[:from]
|
|
388
|
-
|
|
439
|
+
options[:from] = options[:from].split(/#{REGEX_RANGE_INDICATOR}/).map do |time|
|
|
440
|
+
time =~ REGEX_TIME ? "today #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}" : time
|
|
441
|
+
end.join(' to ').split_date_range
|
|
442
|
+
date, finish_date = options[:from]
|
|
389
443
|
finish_date ||= Time.now
|
|
390
444
|
else
|
|
391
445
|
if options[:took]
|
|
392
|
-
took = options[:took]
|
|
446
|
+
took = options[:took]
|
|
393
447
|
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
|
394
448
|
end
|
|
395
449
|
|
|
396
450
|
if options[:back]
|
|
397
|
-
date = options[:back]
|
|
451
|
+
date = options[:back]
|
|
398
452
|
raise InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
|
|
399
453
|
else
|
|
400
454
|
date = options[:took] ? Time.now - took : Time.now
|
|
401
455
|
end
|
|
402
456
|
|
|
403
457
|
if options[:at]
|
|
404
|
-
finish_date = options[:at]
|
|
458
|
+
finish_date = options[:at]
|
|
459
|
+
finish_date = finish_date.chronify(guess: :begin) if finish_date.is_a? String
|
|
405
460
|
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
|
406
461
|
|
|
407
462
|
if options[:took]
|
|
@@ -417,6 +472,7 @@ command %i[done did] do |c|
|
|
|
417
472
|
end
|
|
418
473
|
|
|
419
474
|
if options[:date]
|
|
475
|
+
date = date.chronify(guess: :begin, context: :today) if date =~ REGEX_TIME
|
|
420
476
|
finish_date = wwid.verify_duration(date, finish_date) unless options[:took] || options[:from]
|
|
421
477
|
|
|
422
478
|
donedate = finish_date.strftime('%F %R')
|
|
@@ -528,7 +584,7 @@ command %i[done did] do |c|
|
|
|
528
584
|
wwid.content.push(new_entry)
|
|
529
585
|
Doing::Hooks.trigger :post_entry_added, wwid, new_entry.dup
|
|
530
586
|
wwid.write(wwid.doing_file)
|
|
531
|
-
Doing.logger.info('
|
|
587
|
+
Doing.logger.info('New entry:', %(added "#{new_entry.date.relative_date}: #{new_entry.title}" to #{section}))
|
|
532
588
|
elsif $stdin.stat.size.positive?
|
|
533
589
|
note = Doing::Note.new(options[:note])
|
|
534
590
|
d, title, note = wwid.format_input($stdin.read.strip)
|
|
@@ -553,7 +609,7 @@ command %i[done did] do |c|
|
|
|
553
609
|
Doing::Hooks.trigger :post_entry_added, wwid, new_entry.dup
|
|
554
610
|
|
|
555
611
|
wwid.write(wwid.doing_file)
|
|
556
|
-
Doing.logger.info('
|
|
612
|
+
Doing.logger.info('New entry:', %(added "#{new_entry.date.relative_date}: #{new_entry.title}" to #{section}))
|
|
557
613
|
else
|
|
558
614
|
raise EmptyInput, 'You must provide content when creating a new entry'
|
|
559
615
|
end
|
|
@@ -563,7 +619,7 @@ end
|
|
|
563
619
|
# @@finish
|
|
564
620
|
desc 'Mark last X entries as @done'
|
|
565
621
|
long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
|
|
566
|
-
arg_name 'COUNT'
|
|
622
|
+
arg_name 'COUNT', optional: true
|
|
567
623
|
command :finish do |c|
|
|
568
624
|
c.example 'doing finish', desc: 'Mark the last entry @done'
|
|
569
625
|
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 +630,15 @@ command :finish do |c|
|
|
|
574
630
|
|
|
575
631
|
c.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
|
|
576
632
|
c.arg_name 'DATE_STRING'
|
|
577
|
-
c.flag %i[b back]
|
|
633
|
+
c.flag %i[b back started], type: DateBeginString
|
|
578
634
|
|
|
579
635
|
c.desc 'Set the completed date to the start date plus XX[hmd]'
|
|
580
636
|
c.arg_name 'INTERVAL'
|
|
581
|
-
c.flag %i[t took for]
|
|
637
|
+
c.flag %i[t took for], type: DateIntervalString
|
|
582
638
|
|
|
583
639
|
c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm). If used, ignores --back.)
|
|
584
640
|
c.arg_name 'DATE_STRING'
|
|
585
|
-
c.flag [
|
|
641
|
+
c.flag %i[at finished], type: DateEndString
|
|
586
642
|
|
|
587
643
|
c.desc 'Finish the last X entries containing TAG.
|
|
588
644
|
Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool. Wildcards allowed (*, ?).'
|
|
@@ -639,7 +695,7 @@ command :finish do |c|
|
|
|
639
695
|
options[:fuzzy] = false
|
|
640
696
|
unless options[:auto]
|
|
641
697
|
if options[:took]
|
|
642
|
-
took = options[:took]
|
|
698
|
+
took = options[:took]
|
|
643
699
|
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
|
644
700
|
end
|
|
645
701
|
|
|
@@ -648,12 +704,13 @@ command :finish do |c|
|
|
|
648
704
|
raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
|
|
649
705
|
|
|
650
706
|
if options[:at]
|
|
651
|
-
finish_date = options[:at]
|
|
707
|
+
finish_date = options[:at]
|
|
708
|
+
finish_date = finish_date.chronify(guess: :begin) if finish_date.is_a? String
|
|
652
709
|
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
|
653
710
|
|
|
654
711
|
date = options[:took] ? finish_date - took : finish_date
|
|
655
712
|
elsif options[:back]
|
|
656
|
-
date = options[:back]
|
|
713
|
+
date = options[:back]
|
|
657
714
|
|
|
658
715
|
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
|
659
716
|
else
|
|
@@ -661,8 +718,6 @@ command :finish do |c|
|
|
|
661
718
|
end
|
|
662
719
|
end
|
|
663
720
|
|
|
664
|
-
options[:took] = options[:took].chronify_qty if options[:took]
|
|
665
|
-
|
|
666
721
|
if options[:tag].nil?
|
|
667
722
|
tags = []
|
|
668
723
|
else
|
|
@@ -711,92 +766,6 @@ command :finish do |c|
|
|
|
711
766
|
end
|
|
712
767
|
end
|
|
713
768
|
|
|
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
769
|
# @@mark @@flag
|
|
801
770
|
desc 'Mark last entry as flagged'
|
|
802
771
|
command %i[mark flag] do |c|
|
|
@@ -930,7 +899,7 @@ long_desc 'The @meanwhile tag allows you to have long-running entries that encom
|
|
|
930
899
|
This command makes it easy to start and stop these overarching entries. Just run `doing meanwhile Starting work on this
|
|
931
900
|
big project` to start a @meanwhile entry, add other entries as you work on the project, then use `doing meanwhile` by
|
|
932
901
|
itself to mark the entry as @done.'
|
|
933
|
-
arg_name 'ENTRY'
|
|
902
|
+
arg_name 'ENTRY', optional: true
|
|
934
903
|
command :meanwhile do |c|
|
|
935
904
|
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
905
|
c.example 'doing meanwhile', desc: 'Finish any open @meanwhile entry'
|
|
@@ -949,7 +918,7 @@ command :meanwhile do |c|
|
|
|
949
918
|
|
|
950
919
|
c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
|
|
951
920
|
c.arg_name 'DATE_STRING'
|
|
952
|
-
c.flag %i[b back]
|
|
921
|
+
c.flag %i[b back started], type: DateBeginString
|
|
953
922
|
|
|
954
923
|
c.desc 'Note'
|
|
955
924
|
c.arg_name 'TEXT'
|
|
@@ -960,7 +929,7 @@ command :meanwhile do |c|
|
|
|
960
929
|
|
|
961
930
|
c.action do |_global_options, options, args|
|
|
962
931
|
if options[:back]
|
|
963
|
-
date = options[:back]
|
|
932
|
+
date = options[:back]
|
|
964
933
|
|
|
965
934
|
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
|
966
935
|
else
|
|
@@ -1020,7 +989,7 @@ long_desc %(
|
|
|
1020
989
|
|
|
1021
990
|
Use -e to load the last entry in a text editor where you can append a note.
|
|
1022
991
|
)
|
|
1023
|
-
arg_name 'NOTE_TEXT'
|
|
992
|
+
arg_name 'NOTE_TEXT', optional: true
|
|
1024
993
|
command :note do |c|
|
|
1025
994
|
c.example 'doing note', desc: 'Open the last entry in $EDITOR to append a note'
|
|
1026
995
|
c.example 'doing note "Just a quick annotation"', desc: 'Add a quick note to the last entry'
|
|
@@ -1088,7 +1057,6 @@ command :note do |c|
|
|
|
1088
1057
|
options[:search] = search
|
|
1089
1058
|
end
|
|
1090
1059
|
|
|
1091
|
-
|
|
1092
1060
|
last_entry = wwid.last_entry(options)
|
|
1093
1061
|
|
|
1094
1062
|
unless last_entry
|
|
@@ -1098,37 +1066,37 @@ command :note do |c|
|
|
|
1098
1066
|
|
|
1099
1067
|
last_note = last_entry.note || Doing::Note.new
|
|
1100
1068
|
new_note = Doing::Note.new
|
|
1101
|
-
ask_note = options[:ask] ? Doing::Prompt.read_lines(prompt: 'Add a note') : ''
|
|
1102
1069
|
|
|
1103
|
-
if
|
|
1104
|
-
|
|
1070
|
+
if $stdin.stat.size.positive?
|
|
1071
|
+
new_note.add($stdin.read.strip)
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
unless args.empty?
|
|
1075
|
+
new_note.add(args.join(' '))
|
|
1076
|
+
end
|
|
1105
1077
|
|
|
1106
|
-
|
|
1078
|
+
if options[:editor]
|
|
1079
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
1107
1080
|
|
|
1108
1081
|
if options[:remove]
|
|
1109
|
-
|
|
1082
|
+
input = Doing::Note.new
|
|
1110
1083
|
else
|
|
1111
|
-
|
|
1084
|
+
input = last_entry.note || Doing::Note.new
|
|
1112
1085
|
end
|
|
1113
1086
|
|
|
1087
|
+
input.add(new_note)
|
|
1114
1088
|
|
|
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
|
|
1089
|
+
new_note = Doing::Note.new(wwid.fork_editor(input.strip_lines.join("\n"), message: nil).strip)
|
|
1120
1090
|
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?
|
|
1091
|
+
end
|
|
1128
1092
|
|
|
1093
|
+
if (new_note.empty? && !options[:remove]) || options[:ask]
|
|
1094
|
+
$stderr.puts last_note unless last_note.empty?
|
|
1095
|
+
$stderr.puts new_note unless new_note.empty?
|
|
1096
|
+
new_note.add(Doing::Prompt.read_lines(prompt: 'Add a note'))
|
|
1129
1097
|
end
|
|
1130
1098
|
|
|
1131
|
-
|
|
1099
|
+
raise EmptyInput, 'You must provide content when adding a note' unless options[:remove] || !new_note.empty?
|
|
1132
1100
|
|
|
1133
1101
|
if last_note.equal?(new_note)
|
|
1134
1102
|
Doing.logger.debug('Skipped:', 'No note change')
|
|
@@ -1169,7 +1137,7 @@ command %i[now next] do |c|
|
|
|
1169
1137
|
|
|
1170
1138
|
c.desc 'Backdate start time [4pm|20m|2h|"yesterday noon"]'
|
|
1171
1139
|
c.arg_name 'DATE_STRING'
|
|
1172
|
-
c.flag %i[b back started]
|
|
1140
|
+
c.flag %i[b back started], type: DateBeginString
|
|
1173
1141
|
|
|
1174
1142
|
c.desc 'Timed entry, marks last entry in section as @done'
|
|
1175
1143
|
c.switch %i[f finish_last], negatable: false, default_value: false
|
|
@@ -1187,7 +1155,7 @@ command %i[now next] do |c|
|
|
|
1187
1155
|
|
|
1188
1156
|
c.action do |_global_options, options, args|
|
|
1189
1157
|
if options[:back]
|
|
1190
|
-
date = options[:back]
|
|
1158
|
+
date = options[:back]
|
|
1191
1159
|
|
|
1192
1160
|
raise InvalidTimeExpression.new('unable to parse date string', topic: 'Parser:') if date.nil?
|
|
1193
1161
|
else
|
|
@@ -1211,7 +1179,7 @@ command %i[now next] do |c|
|
|
|
1211
1179
|
input += "\n#{ask_note}" unless ask_note.empty?
|
|
1212
1180
|
input = wwid.fork_editor(input).strip
|
|
1213
1181
|
|
|
1214
|
-
|
|
1182
|
+
d, title, note = wwid.format_input(input)
|
|
1215
1183
|
raise EmptyInput, 'No content' if title.strip.empty?
|
|
1216
1184
|
|
|
1217
1185
|
if ask_note.empty? && options[:ask]
|
|
@@ -1219,6 +1187,7 @@ command %i[now next] do |c|
|
|
|
1219
1187
|
note.add(ask_note) unless ask_note.empty?
|
|
1220
1188
|
end
|
|
1221
1189
|
|
|
1190
|
+
date = d.nil? ? date : d
|
|
1222
1191
|
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
1223
1192
|
wwid.write(wwid.doing_file)
|
|
1224
1193
|
elsif args.length.positive?
|
|
@@ -1265,7 +1234,7 @@ desc 'Reset the start time of an entry'
|
|
|
1265
1234
|
long_desc 'Update the start time of the last entry or the last entry matching a tag/search filter.
|
|
1266
1235
|
If no argument is provided, the start time will be reset to the current time.
|
|
1267
1236
|
If a date string is provided as an argument, the start time will be set to the parsed result.'
|
|
1268
|
-
arg_name 'DATE_STRING'
|
|
1237
|
+
arg_name 'DATE_STRING', optional: true
|
|
1269
1238
|
command %i[reset begin] do |c|
|
|
1270
1239
|
c.example 'doing reset', desc: 'Reset the start time of the last entry to the current time'
|
|
1271
1240
|
c.example 'doing reset --tag project1', desc: 'Reset the start time of the most recent entry tagged @project1 to the current time'
|
|
@@ -1279,6 +1248,9 @@ command %i[reset begin] do |c|
|
|
|
1279
1248
|
c.desc 'Resume entry (remove @done)'
|
|
1280
1249
|
c.switch %i[r resume], default_value: true
|
|
1281
1250
|
|
|
1251
|
+
c.desc 'Change start date but do not remove @done (shortcut for --no-resume)'
|
|
1252
|
+
c.switch [:n]
|
|
1253
|
+
|
|
1282
1254
|
c.desc 'Reset last entry matching tag. Wildcards allowed (*, ?)'
|
|
1283
1255
|
c.arg_name 'TAG'
|
|
1284
1256
|
c.flag [:tag]
|
|
@@ -1416,11 +1388,11 @@ command :select do |c|
|
|
|
1416
1388
|
|
|
1417
1389
|
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
1390
|
c.arg_name 'DATE_STRING'
|
|
1419
|
-
c.flag [:before]
|
|
1391
|
+
c.flag [:before], type: DateBeginString
|
|
1420
1392
|
|
|
1421
1393
|
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
1394
|
c.arg_name 'DATE_STRING'
|
|
1423
|
-
c.flag [:after]
|
|
1395
|
+
c.flag [:after], type: DateEndString
|
|
1424
1396
|
|
|
1425
1397
|
c.desc %(
|
|
1426
1398
|
Date range to show, or a single day to filter date on.
|
|
@@ -1431,7 +1403,7 @@ command :select do |c|
|
|
|
1431
1403
|
by time of day.
|
|
1432
1404
|
)
|
|
1433
1405
|
c.arg_name 'DATE_OR_RANGE'
|
|
1434
|
-
c.flag [:from]
|
|
1406
|
+
c.flag [:from], type: DateRangeString
|
|
1435
1407
|
|
|
1436
1408
|
c.desc 'Force exact search string matching (case sensitive)'
|
|
1437
1409
|
c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
|
|
@@ -1698,11 +1670,11 @@ command %i[grep search] do |c|
|
|
|
1698
1670
|
|
|
1699
1671
|
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
1672
|
c.arg_name 'DATE_STRING'
|
|
1701
|
-
c.flag [:before]
|
|
1673
|
+
c.flag [:before], type: DateBeginString
|
|
1702
1674
|
|
|
1703
1675
|
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
1676
|
c.arg_name 'DATE_STRING'
|
|
1705
|
-
c.flag [:after]
|
|
1677
|
+
c.flag [:after], type: DateEndString
|
|
1706
1678
|
|
|
1707
1679
|
c.desc %(
|
|
1708
1680
|
Date range to show, or a single day to filter date on.
|
|
@@ -1713,12 +1685,20 @@ command %i[grep search] do |c|
|
|
|
1713
1685
|
by time of day.
|
|
1714
1686
|
)
|
|
1715
1687
|
c.arg_name 'DATE_OR_RANGE'
|
|
1716
|
-
c.flag [:from]
|
|
1688
|
+
c.flag [:from], type: DateRangeString
|
|
1717
1689
|
|
|
1718
1690
|
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
|
1719
1691
|
c.arg_name 'FORMAT'
|
|
1720
1692
|
c.flag %i[o output]
|
|
1721
1693
|
|
|
1694
|
+
c.desc "Output using a template from configuration"
|
|
1695
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
1696
|
+
c.flag [:config_template], type: TemplateName, default_value: 'default'
|
|
1697
|
+
|
|
1698
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
1699
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
1700
|
+
c.flag [:template]
|
|
1701
|
+
|
|
1722
1702
|
c.desc 'Show time intervals on @done tasks'
|
|
1723
1703
|
c.switch %i[t times], default_value: true, negatable: true
|
|
1724
1704
|
|
|
@@ -1773,7 +1753,7 @@ command %i[grep search] do |c|
|
|
|
1773
1753
|
options[:fuzzy] = false
|
|
1774
1754
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
1775
1755
|
|
|
1776
|
-
template = settings['templates'][
|
|
1756
|
+
template = settings['templates'][options[:config_template]].deep_merge(settings)
|
|
1777
1757
|
tags_color = template.key?('tags_color') ? template['tags_color'] : nil
|
|
1778
1758
|
|
|
1779
1759
|
section = wwid.guess_section(options[:section]) if options[:section]
|
|
@@ -1830,6 +1810,14 @@ command :last do |c|
|
|
|
1830
1810
|
c.arg_name 'QUERY'
|
|
1831
1811
|
c.flag [:search]
|
|
1832
1812
|
|
|
1813
|
+
c.desc "Output using a template from configuration"
|
|
1814
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
1815
|
+
c.flag [:config_template], type: TemplateName, default_value: 'last'
|
|
1816
|
+
|
|
1817
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
1818
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
1819
|
+
c.flag [:template]
|
|
1820
|
+
|
|
1833
1821
|
c.desc "Highlight search matches in output. Only affects command line output"
|
|
1834
1822
|
c.switch %i[h hilite], default_value: settings.dig('search', 'highlight')
|
|
1835
1823
|
|
|
@@ -1883,6 +1871,8 @@ command :last do |c|
|
|
|
1883
1871
|
else
|
|
1884
1872
|
last = wwid.last(times: true, section: options[:section],
|
|
1885
1873
|
options: {
|
|
1874
|
+
config_template: options[:config_template],
|
|
1875
|
+
template: options[:template],
|
|
1886
1876
|
duration: options[:duration],
|
|
1887
1877
|
search: options[:search],
|
|
1888
1878
|
fuzzy: options[:fuzzy],
|
|
@@ -1917,6 +1907,14 @@ command :recent do |c|
|
|
|
1917
1907
|
c.desc 'Show time intervals on @done tasks'
|
|
1918
1908
|
c.switch %i[t times], default_value: true, negatable: true
|
|
1919
1909
|
|
|
1910
|
+
c.desc "Output using a template from configuration"
|
|
1911
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
1912
|
+
c.flag [:config_template], type: TemplateName, default_value: 'recent'
|
|
1913
|
+
|
|
1914
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
1915
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
1916
|
+
c.flag [:template]
|
|
1917
|
+
|
|
1920
1918
|
c.desc 'Show elapsed time on entries without @done tag'
|
|
1921
1919
|
c.switch [:duration]
|
|
1922
1920
|
|
|
@@ -1960,7 +1958,9 @@ command :recent do |c|
|
|
|
1960
1958
|
times: options[:times],
|
|
1961
1959
|
totals: options[:totals],
|
|
1962
1960
|
interactive: options[:interactive],
|
|
1963
|
-
duration: options[:duration]
|
|
1961
|
+
duration: options[:duration],
|
|
1962
|
+
config_template: options[:config_template],
|
|
1963
|
+
template: options[:template]
|
|
1964
1964
|
}
|
|
1965
1965
|
|
|
1966
1966
|
Doing::Pager::page wwid.recent(count, section.cap_first, opts)
|
|
@@ -2011,11 +2011,11 @@ command :show do |c|
|
|
|
2011
2011
|
|
|
2012
2012
|
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
2013
|
c.arg_name 'DATE_STRING'
|
|
2014
|
-
c.flag [:before]
|
|
2014
|
+
c.flag [:before], type: DateBeginString
|
|
2015
2015
|
|
|
2016
2016
|
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
2017
|
c.arg_name 'DATE_STRING'
|
|
2018
|
-
c.flag [:after]
|
|
2018
|
+
c.flag [:after], type: DateEndString
|
|
2019
2019
|
|
|
2020
2020
|
c.desc %(
|
|
2021
2021
|
Date range to show, or a single day to filter date on.
|
|
@@ -2027,7 +2027,7 @@ command :show do |c|
|
|
|
2027
2027
|
)
|
|
2028
2028
|
|
|
2029
2029
|
c.arg_name 'DATE_OR_RANGE'
|
|
2030
|
-
c.flag [:from]
|
|
2030
|
+
c.flag [:from], type: DateRangeString
|
|
2031
2031
|
|
|
2032
2032
|
c.desc 'Search filter, surround with slashes for regex (/query/), start with single quote for exact match ("\'query")'
|
|
2033
2033
|
c.arg_name 'QUERY'
|
|
@@ -2075,6 +2075,14 @@ command :show do |c|
|
|
|
2075
2075
|
c.desc 'Only show items with recorded time intervals'
|
|
2076
2076
|
c.switch [:only_timed], default_value: false, negatable: false
|
|
2077
2077
|
|
|
2078
|
+
c.desc "Output using a template from configuration"
|
|
2079
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2080
|
+
c.flag [:config_template], type: TemplateName, default_value: 'default'
|
|
2081
|
+
|
|
2082
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2083
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2084
|
+
c.flag [:template]
|
|
2085
|
+
|
|
2078
2086
|
c.desc 'Select section or tag to display from a menu'
|
|
2079
2087
|
c.switch %i[m menu], negatable: false, default_value: false
|
|
2080
2088
|
|
|
@@ -2131,7 +2139,7 @@ command :show do |c|
|
|
|
2131
2139
|
|
|
2132
2140
|
options[:times] = true if options[:totals]
|
|
2133
2141
|
|
|
2134
|
-
template = settings['templates'][
|
|
2142
|
+
template = settings['templates'][options[:config_template]].deep_merge({
|
|
2135
2143
|
'wrap_width' => settings['wrap_width'] || 0,
|
|
2136
2144
|
'date_format' => settings['default_date_format'],
|
|
2137
2145
|
'order' => settings['order'] || 'asc',
|
|
@@ -2195,6 +2203,7 @@ end
|
|
|
2195
2203
|
|
|
2196
2204
|
# @@tags
|
|
2197
2205
|
desc 'List all tags in the current Doing file'
|
|
2206
|
+
arg_name 'MAX_COUNT', optional: true, type: Integer
|
|
2198
2207
|
command :tags do |c|
|
|
2199
2208
|
c.desc 'Section'
|
|
2200
2209
|
c.arg_name 'SECTION_NAME'
|
|
@@ -2203,6 +2212,9 @@ command :tags do |c|
|
|
|
2203
2212
|
c.desc 'Show count of occurrences'
|
|
2204
2213
|
c.switch %i[c counts]
|
|
2205
2214
|
|
|
2215
|
+
c.desc 'Output in a single line with @ symbols. Ignored if --counts is specified.'
|
|
2216
|
+
c.switch %i[l line]
|
|
2217
|
+
|
|
2206
2218
|
c.desc 'Sort by name or count'
|
|
2207
2219
|
c.arg_name 'SORT_ORDER'
|
|
2208
2220
|
c.flag %i[sort], default_value: 'name', must_match: /^(?:n(?:ame)?|c(?:ount)?)$/
|
|
@@ -2246,6 +2258,7 @@ command :tags do |c|
|
|
|
2246
2258
|
|
|
2247
2259
|
c.action do |_global, options, args|
|
|
2248
2260
|
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
2261
|
+
options[:count] = args.count.positive? ? args[0].to_i : 0
|
|
2249
2262
|
|
|
2250
2263
|
items = wwid.filter_items([], opt: options)
|
|
2251
2264
|
|
|
@@ -2273,7 +2286,11 @@ command :tags do |c|
|
|
|
2273
2286
|
if options[:counts]
|
|
2274
2287
|
tags.each { |t, c| puts "#{t} (#{c})" }
|
|
2275
2288
|
else
|
|
2276
|
-
|
|
2289
|
+
if options[:line]
|
|
2290
|
+
puts tags.map { |t, c| t }.to_tags.join(' ')
|
|
2291
|
+
else
|
|
2292
|
+
tags.each { |t, c| puts "#{t}" }
|
|
2293
|
+
end
|
|
2277
2294
|
end
|
|
2278
2295
|
end
|
|
2279
2296
|
end
|
|
@@ -2311,6 +2328,14 @@ command :today do |c|
|
|
|
2311
2328
|
c.arg_name 'FORMAT'
|
|
2312
2329
|
c.flag %i[o output]
|
|
2313
2330
|
|
|
2331
|
+
c.desc "Output using a template from configuration"
|
|
2332
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2333
|
+
c.flag [:config_template], type: TemplateName, default_value: 'today'
|
|
2334
|
+
|
|
2335
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2336
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2337
|
+
c.flag [:template]
|
|
2338
|
+
|
|
2314
2339
|
c.desc 'View entries before specified time (e.g. 8am, 12:30pm, 15:00)'
|
|
2315
2340
|
c.arg_name 'TIME_STRING'
|
|
2316
2341
|
c.flag [:before]
|
|
@@ -2322,21 +2347,21 @@ command :today do |c|
|
|
|
2322
2347
|
c.desc %(
|
|
2323
2348
|
Time range to show `doing today --from "12pm to 4pm"`
|
|
2324
2349
|
)
|
|
2325
|
-
c.arg_name '
|
|
2326
|
-
c.flag [:from]
|
|
2350
|
+
c.arg_name 'TIME_RANGE'
|
|
2351
|
+
c.flag [:from], type: DateRangeString
|
|
2327
2352
|
|
|
2328
2353
|
c.action do |_global_options, options, _args|
|
|
2329
2354
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
2330
2355
|
|
|
2331
2356
|
options[:times] = true if options[:totals]
|
|
2332
2357
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2333
|
-
filter_options = %i[after before duration from section sort_tags totals].each_with_object({}) { |k, hsh| hsh[k] = options[k] }
|
|
2358
|
+
filter_options = %i[after before duration from section sort_tags totals template config_template].each_with_object({}) { |k, hsh| hsh[k] = options[k] }
|
|
2334
2359
|
|
|
2335
2360
|
Doing::Pager.page wwid.today(options[:times], options[:output], filter_options).chomp
|
|
2336
2361
|
end
|
|
2337
2362
|
end
|
|
2338
2363
|
|
|
2339
|
-
#
|
|
2364
|
+
# @@on
|
|
2340
2365
|
desc 'List entries for a date'
|
|
2341
2366
|
long_desc %(Date argument can be natural language. "thursday" would be interpreted as "last thursday,"
|
|
2342
2367
|
and "2d" would be interpreted as "two days ago." If you use "to" or "through" between two dates,
|
|
@@ -2370,21 +2395,24 @@ command :on do |c|
|
|
|
2370
2395
|
c.arg_name 'FORMAT'
|
|
2371
2396
|
c.flag %i[o output]
|
|
2372
2397
|
|
|
2398
|
+
c.desc "Output using a template from configuration"
|
|
2399
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2400
|
+
c.flag [:config_template], type: TemplateName, default_value: 'default'
|
|
2401
|
+
|
|
2402
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2403
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2404
|
+
c.flag [:template]
|
|
2405
|
+
|
|
2373
2406
|
c.action do |_global_options, options, args|
|
|
2374
2407
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
2375
2408
|
|
|
2376
2409
|
raise MissingArgument, 'Missing date argument' if args.empty?
|
|
2377
2410
|
|
|
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
|
|
2411
|
+
date_string = args.join(' ').strip
|
|
2412
|
+
if date_string =~ /^tod(?:ay)?/i
|
|
2413
|
+
date_string = 'today to tomorrow 12am'
|
|
2387
2414
|
end
|
|
2415
|
+
start, finish = date_string.split_date_range
|
|
2388
2416
|
|
|
2389
2417
|
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
|
2390
2418
|
|
|
@@ -2396,7 +2424,7 @@ command :on do |c|
|
|
|
2396
2424
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2397
2425
|
|
|
2398
2426
|
Doing::Pager.page wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
|
2399
|
-
{ duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
2427
|
+
{ template: options[:template], config_template: options[:config_template], duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
2400
2428
|
end
|
|
2401
2429
|
end
|
|
2402
2430
|
|
|
@@ -2432,6 +2460,14 @@ command :since do |c|
|
|
|
2432
2460
|
c.arg_name 'FORMAT'
|
|
2433
2461
|
c.flag %i[o output]
|
|
2434
2462
|
|
|
2463
|
+
c.desc "Output using a template from configuration"
|
|
2464
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2465
|
+
c.flag [:config_template], type: TemplateName, default_value: 'default'
|
|
2466
|
+
|
|
2467
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2468
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2469
|
+
c.flag [:template]
|
|
2470
|
+
|
|
2435
2471
|
c.action do |_global_options, options, args|
|
|
2436
2472
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
2437
2473
|
|
|
@@ -2453,7 +2489,7 @@ command :since do |c|
|
|
|
2453
2489
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2454
2490
|
|
|
2455
2491
|
Doing::Pager.page wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
|
2456
|
-
{ duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
2492
|
+
{ template: options[:template], config_template: options[:config_template], duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
2457
2493
|
end
|
|
2458
2494
|
end
|
|
2459
2495
|
|
|
@@ -2536,11 +2572,11 @@ command :view do |c|
|
|
|
2536
2572
|
|
|
2537
2573
|
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
2574
|
c.arg_name 'DATE_STRING'
|
|
2539
|
-
c.flag [:before]
|
|
2575
|
+
c.flag [:before], type: DateBeginString
|
|
2540
2576
|
|
|
2541
2577
|
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
2578
|
c.arg_name 'DATE_STRING'
|
|
2543
|
-
c.flag [:after]
|
|
2579
|
+
c.flag [:after], type: DateEndString
|
|
2544
2580
|
|
|
2545
2581
|
c.desc %(
|
|
2546
2582
|
Date range to show, or a single day to filter date on.
|
|
@@ -2551,7 +2587,7 @@ command :view do |c|
|
|
|
2551
2587
|
by time of day.
|
|
2552
2588
|
)
|
|
2553
2589
|
c.arg_name 'DATE_OR_RANGE'
|
|
2554
|
-
c.flag [:from]
|
|
2590
|
+
c.flag [:from], type: DateRangeString
|
|
2555
2591
|
|
|
2556
2592
|
c.desc 'Only show items with recorded time intervals (override view settings)'
|
|
2557
2593
|
c.switch [:only_timed], default_value: false, negatable: false
|
|
@@ -2588,17 +2624,17 @@ command :view do |c|
|
|
|
2588
2624
|
view = wwid.get_view(title)
|
|
2589
2625
|
|
|
2590
2626
|
if view
|
|
2591
|
-
page_title = view
|
|
2627
|
+
page_title = view['title'] || title.cap_first
|
|
2592
2628
|
only_timed = if (view.key?('only_timed') && view['only_timed']) || options[:only_timed]
|
|
2593
2629
|
true
|
|
2594
2630
|
else
|
|
2595
2631
|
false
|
|
2596
2632
|
end
|
|
2597
2633
|
|
|
2598
|
-
template = view
|
|
2599
|
-
date_format = view
|
|
2634
|
+
template = view['template'] || nil
|
|
2635
|
+
date_format = view['date_format'] || nil
|
|
2600
2636
|
|
|
2601
|
-
tags_color = view
|
|
2637
|
+
tags_color = view['tags_color'] || nil
|
|
2602
2638
|
tag_filter = false
|
|
2603
2639
|
if options[:tag]
|
|
2604
2640
|
tag_filter = { 'tags' => [], 'bool' => 'OR' }
|
|
@@ -2628,19 +2664,19 @@ command :view do |c|
|
|
|
2628
2664
|
section = if options[:section]
|
|
2629
2665
|
section
|
|
2630
2666
|
else
|
|
2631
|
-
view
|
|
2667
|
+
view['section'] || settings['current_section']
|
|
2632
2668
|
end
|
|
2633
|
-
order = view
|
|
2669
|
+
order = view['order']&.normalize_order || 'asc'
|
|
2634
2670
|
|
|
2635
2671
|
totals = if options[:totals]
|
|
2636
2672
|
true
|
|
2637
2673
|
else
|
|
2638
|
-
view
|
|
2674
|
+
view['totals'] || false
|
|
2639
2675
|
end
|
|
2640
2676
|
tag_order = if options[:tag_order]
|
|
2641
2677
|
options[:tag_order].normalize_order
|
|
2642
2678
|
else
|
|
2643
|
-
view
|
|
2679
|
+
view['tag_order']&.normalize_order || 'asc'
|
|
2644
2680
|
end
|
|
2645
2681
|
|
|
2646
2682
|
options[:times] = true if totals
|
|
@@ -2669,7 +2705,6 @@ command :view do |c|
|
|
|
2669
2705
|
|
|
2670
2706
|
opts = options.dup
|
|
2671
2707
|
opts[:age] = options[:age].normalize_age(:newest)
|
|
2672
|
-
opts[:view_template] = title
|
|
2673
2708
|
opts[:count] = count
|
|
2674
2709
|
opts[:format] = date_format
|
|
2675
2710
|
opts[:highlight] = options[:color]
|
|
@@ -2686,6 +2721,7 @@ command :view do |c|
|
|
|
2686
2721
|
opts[:tags_color] = tags_color
|
|
2687
2722
|
opts[:template] = template
|
|
2688
2723
|
opts[:totals] = totals
|
|
2724
|
+
opts[:view_template] = title
|
|
2689
2725
|
|
|
2690
2726
|
Doing::Pager.page wwid.list_section(opts)
|
|
2691
2727
|
elsif title.instance_of?(FalseClass)
|
|
@@ -2713,6 +2749,14 @@ command :yesterday do |c|
|
|
|
2713
2749
|
c.arg_name 'FORMAT'
|
|
2714
2750
|
c.flag %i[o output]
|
|
2715
2751
|
|
|
2752
|
+
c.desc "Output using a template from configuration"
|
|
2753
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2754
|
+
c.flag [:config_template], type: TemplateName, default_value: 'today'
|
|
2755
|
+
|
|
2756
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2757
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2758
|
+
c.flag [:template]
|
|
2759
|
+
|
|
2716
2760
|
c.desc 'Show time intervals on @done tasks'
|
|
2717
2761
|
c.switch %i[t times], default_value: true, negatable: true
|
|
2718
2762
|
|
|
@@ -2735,11 +2779,9 @@ command :yesterday do |c|
|
|
|
2735
2779
|
c.arg_name 'TIME_STRING'
|
|
2736
2780
|
c.flag [:after]
|
|
2737
2781
|
|
|
2738
|
-
c.desc
|
|
2739
|
-
Time range to show, e.g. `doing yesterday --from "1am to 8am"`
|
|
2740
|
-
)
|
|
2782
|
+
c.desc 'Time range to show, e.g. `doing yesterday --from "1am to 8am"`'
|
|
2741
2783
|
c.arg_name 'TIME_RANGE'
|
|
2742
|
-
c.flag [:from]
|
|
2784
|
+
c.flag [:from], must_match: REGEX_TIME_RANGE
|
|
2743
2785
|
|
|
2744
2786
|
c.desc 'Tag sort direction (asc|desc)'
|
|
2745
2787
|
c.arg_name 'DIRECTION'
|
|
@@ -2751,21 +2793,15 @@ command :yesterday do |c|
|
|
|
2751
2793
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2752
2794
|
|
|
2753
2795
|
if options[:from]
|
|
2754
|
-
options[:from] = options[:from].split(/
|
|
2796
|
+
options[:from] = options[:from].split(/#{REGEX_RANGE_INDICATOR}/).map do |time|
|
|
2755
2797
|
"yesterday #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}"
|
|
2756
|
-
end.join(' to ')
|
|
2757
|
-
end
|
|
2758
|
-
|
|
2759
|
-
opt =
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
from: options[:from],
|
|
2764
|
-
sort_tags: options[:sort_tags],
|
|
2765
|
-
tag_order: options[:tag_order].normalize_order,
|
|
2766
|
-
totals: options[:totals],
|
|
2767
|
-
order: settings.dig('templates', 'today', 'order')
|
|
2768
|
-
}
|
|
2798
|
+
end.join(' to ').split_date_range
|
|
2799
|
+
end
|
|
2800
|
+
|
|
2801
|
+
opt = options.dup
|
|
2802
|
+
opt[:tag_order] = options[:tag_order].normalize_order
|
|
2803
|
+
opt[:order] = settings.dig('templates', options[:config_template], 'order')
|
|
2804
|
+
|
|
2769
2805
|
Doing::Pager.page wwid.yesterday(options[:section], options[:times], options[:output], opt).chomp
|
|
2770
2806
|
end
|
|
2771
2807
|
end
|
|
@@ -2991,8 +3027,7 @@ command :config do |c|
|
|
|
2991
3027
|
value = options[:remove] ? nil : args.pop
|
|
2992
3028
|
keypath = args.join('.')
|
|
2993
3029
|
real_path = config.resolve_key_path(keypath, create: true)
|
|
2994
|
-
|
|
2995
|
-
old_value = settings.dig(*real_path) || nil
|
|
3030
|
+
old_value = settings.dig(*real_path)
|
|
2996
3031
|
old_type = old_value&.class.to_s || nil
|
|
2997
3032
|
|
|
2998
3033
|
if old_value.is_a?(Hash) && !options[:remove]
|
|
@@ -3018,7 +3053,6 @@ command :config do |c|
|
|
|
3018
3053
|
else
|
|
3019
3054
|
current_value = cfg.dig(*real_path)
|
|
3020
3055
|
cfg.deep_set(real_path, value.set_type(old_type))
|
|
3021
|
-
|
|
3022
3056
|
$stderr.puts "#{' Key path:'.yellow} #{real_path.join('->').boldwhite}"
|
|
3023
3057
|
$stderr.puts "#{'Inherited:'.yellow} #{(old_value ? old_value.to_s : 'empty').boldwhite}"
|
|
3024
3058
|
$stderr.puts "#{' Current:'.yellow} #{ (current_value ? current_value.to_s : 'empty').boldwhite }"
|
|
@@ -3178,7 +3212,7 @@ command %i[archive move] do |c|
|
|
|
3178
3212
|
c.desc 'Archive entries older than date
|
|
3179
3213
|
(Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
|
|
3180
3214
|
c.arg_name 'DATE_STRING'
|
|
3181
|
-
c.flag [:before]
|
|
3215
|
+
c.flag [:before], type: DateEndString
|
|
3182
3216
|
|
|
3183
3217
|
c.action do |_global_options, options, args|
|
|
3184
3218
|
options[:fuzzy] = false
|
|
@@ -3269,11 +3303,11 @@ command :import do |c|
|
|
|
3269
3303
|
# TODO: Allow time range filtering
|
|
3270
3304
|
c.desc 'Import entries older than date'
|
|
3271
3305
|
c.arg_name 'DATE_STRING'
|
|
3272
|
-
c.flag [:before]
|
|
3306
|
+
c.flag [:before], type: DateBeginString
|
|
3273
3307
|
|
|
3274
3308
|
c.desc 'Import entries newer than date'
|
|
3275
3309
|
c.arg_name 'DATE_STRING'
|
|
3276
|
-
c.flag [:after]
|
|
3310
|
+
c.flag [:after], type: DateEndString
|
|
3277
3311
|
|
|
3278
3312
|
c.desc %(
|
|
3279
3313
|
Date range to import. Date range argument should be quoted. Date specifications can be natural language.
|
|
@@ -3281,7 +3315,7 @@ command :import do |c|
|
|
|
3281
3315
|
Has no effect unless the import plugin has implemented date range filtering.
|
|
3282
3316
|
)
|
|
3283
3317
|
c.arg_name 'DATE_OR_RANGE'
|
|
3284
|
-
c.flag %i[f from]
|
|
3318
|
+
c.flag %i[f from], type: DateRangeString
|
|
3285
3319
|
|
|
3286
3320
|
c.desc 'Allow entries that overlap existing times'
|
|
3287
3321
|
c.switch [:overlap], negatable: true
|
|
@@ -3299,24 +3333,19 @@ command :import do |c|
|
|
|
3299
3333
|
end
|
|
3300
3334
|
|
|
3301
3335
|
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]
|
|
3336
|
+
options[:date_filter] = options[:from]
|
|
3337
|
+
|
|
3338
|
+
raise InvalidTimeExpression, 'Unrecognized date string' unless options[:date_filter][0]
|
|
3339
|
+
elsif options[:before] || options[:after]
|
|
3340
|
+
options[:date_filter] = [nil, nil]
|
|
3341
|
+
options[:date_filter][1] = options[:before] || Time.now + (1 << 64)
|
|
3342
|
+
options[:date_filter][0] = options[:after] || Time.now - (1 << 64)
|
|
3313
3343
|
end
|
|
3314
3344
|
|
|
3315
3345
|
options[:case] = options[:case].normalize_case
|
|
3316
3346
|
|
|
3317
3347
|
if options[:type] =~ Doing::Plugins.plugin_regex(type: :import)
|
|
3318
3348
|
options[:no_overlap] = !options[:overlap]
|
|
3319
|
-
options[:date_filter] = dates
|
|
3320
3349
|
wwid.import(args, options)
|
|
3321
3350
|
wwid.write(wwid.doing_file)
|
|
3322
3351
|
else
|
|
@@ -3633,17 +3662,44 @@ end
|
|
|
3633
3662
|
|
|
3634
3663
|
# @@changelog @@changes
|
|
3635
3664
|
desc 'List recent changes in Doing'
|
|
3636
|
-
long_desc
|
|
3637
|
-
|
|
3665
|
+
long_desc %(Display a formatted list of changes in recent versions.
|
|
3666
|
+
|
|
3667
|
+
Without flags, displays only the most recent version.
|
|
3668
|
+
Use --lookup or --all for history.)
|
|
3669
|
+
command %i[changes changelog] do |c|
|
|
3670
|
+
c.desc 'Display all versions'
|
|
3671
|
+
c.switch %i[a all], default_value: false, negatable: false
|
|
3672
|
+
|
|
3673
|
+
c.desc %(Look up a specific version. Specify versions as "MAJ.MIN.PATCH", MIN
|
|
3674
|
+
and PATCH are optional. Use > or < to see all changes since or prior
|
|
3675
|
+
to a version.)
|
|
3676
|
+
c.arg_name 'VERSION'
|
|
3677
|
+
c.flag %i[l lookup], must_match: /^(?:(?:(?:[<>=]|p(?:rior)|b(?:efore)|o(?:lder)|s(?:ince)|a(?:fter)|n(?:ewer))? *[\d.*?]+ *)+|(?:[\d.]+ *-+ *[\d.]+))$/
|
|
3678
|
+
|
|
3679
|
+
c.desc %(Show changelogs matching search terms (uses pattern-based searching).
|
|
3680
|
+
Add slashes to search with regular expressions, e.g. `--search "/output.*flag/"`)
|
|
3681
|
+
c.flag %i[s search]
|
|
3682
|
+
|
|
3683
|
+
c.example 'doing changes', desc: 'View changes in the current version'
|
|
3684
|
+
c.example 'doing changes --all', desc: 'See the entire changelog'
|
|
3685
|
+
c.example 'doing changes --lookup 2.0.21', desc: 'See changes from version 2.0.21'
|
|
3686
|
+
c.example 'doing changes --lookup "> 2.1"', desc: 'See all changes since 2.1.0'
|
|
3687
|
+
c.example 'doing changes --search "tags +bool"', desc: 'See all changes containing "tags" and "bool"'
|
|
3688
|
+
c.example 'doing changes -l "> 2.1" -s "pattern"', desc: 'Lookup and search can be combined'
|
|
3689
|
+
|
|
3690
|
+
|
|
3638
3691
|
c.action do |_global_options, options, args|
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3692
|
+
cl = Doing::Changes.new(lookup: options[:lookup], search: options[:search])
|
|
3693
|
+
|
|
3694
|
+
content = if options[:all] || options[:search] || options[:lookup]
|
|
3695
|
+
cl.to_s
|
|
3696
|
+
else
|
|
3697
|
+
cl.latest
|
|
3698
|
+
end
|
|
3699
|
+
|
|
3700
|
+
parsed = TTY::Markdown.parse(content, width: 80, symbols: {override: {bullet: "•"}})
|
|
3701
|
+
Doing::Pager.paginate = true
|
|
3702
|
+
Doing::Pager.page parsed
|
|
3647
3703
|
end
|
|
3648
3704
|
end
|
|
3649
3705
|
|
|
@@ -3667,9 +3723,9 @@ command :commands_accepting do |c|
|
|
|
3667
3723
|
end
|
|
3668
3724
|
|
|
3669
3725
|
if o[:column]
|
|
3670
|
-
puts cmds
|
|
3726
|
+
puts cmds.sort
|
|
3671
3727
|
else
|
|
3672
|
-
puts "Commands accepting --#{option}: #{cmds.join(', ')}"
|
|
3728
|
+
puts "Commands accepting --#{option}: #{cmds.sort.join(', ')}"
|
|
3673
3729
|
end
|
|
3674
3730
|
end
|
|
3675
3731
|
end
|
|
@@ -3700,7 +3756,7 @@ pre do |global, _command, _options, _args|
|
|
|
3700
3756
|
Doing::Pager.paginate = global[:pager]
|
|
3701
3757
|
|
|
3702
3758
|
$stdout.puts "doing v#{Doing::VERSION}" if global[:version]
|
|
3703
|
-
unless
|
|
3759
|
+
unless $stdout.isatty
|
|
3704
3760
|
Doing::Color.coloring = global[:pager] ? global[:color] : false
|
|
3705
3761
|
else
|
|
3706
3762
|
Doing::Color.coloring = global[:color]
|
|
@@ -3714,7 +3770,9 @@ pre do |global, _command, _options, _args|
|
|
|
3714
3770
|
end
|
|
3715
3771
|
|
|
3716
3772
|
on_error do |exception|
|
|
3717
|
-
if exception.kind_of?(
|
|
3773
|
+
if exception.kind_of?(GLI::UnknownCommand)
|
|
3774
|
+
exit run(['now'].concat(ARGV))
|
|
3775
|
+
elsif exception.kind_of?(SystemExit)
|
|
3718
3776
|
false
|
|
3719
3777
|
else
|
|
3720
3778
|
# Doing.logger.error('Fatal:', exception)
|
|
@@ -3748,7 +3806,12 @@ around do |global, command, options, arguments, code|
|
|
|
3748
3806
|
Doing::Prompt.force_answer = false
|
|
3749
3807
|
Doing.config.force_answer = false
|
|
3750
3808
|
else
|
|
3751
|
-
Doing::Prompt.default_answer =
|
|
3809
|
+
Doing::Prompt.default_answer = if $stdout.isatty
|
|
3810
|
+
global[:default]
|
|
3811
|
+
else
|
|
3812
|
+
true
|
|
3813
|
+
end
|
|
3814
|
+
|
|
3752
3815
|
Doing.config.force_answer = global[:default] ? true : false
|
|
3753
3816
|
end
|
|
3754
3817
|
|