doing 2.1.21 → 2.1.25
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 +51 -13
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +6 -3
- data/bin/doing +254 -103
- data/docs/doc/Array.html +74 -34
- data/docs/doc/BooleanTermParser/Clause.html +5 -5
- data/docs/doc/BooleanTermParser/Operator.html +4 -4
- data/docs/doc/BooleanTermParser/Query.html +8 -8
- data/docs/doc/BooleanTermParser/QueryParser.html +2 -2
- data/docs/doc/BooleanTermParser/QueryTransformer.html +2 -2
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/Color.html +4 -4
- data/docs/doc/Doing/Completion.html +2 -2
- data/docs/doc/Doing/Configuration.html +16 -18
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +2 -2
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +2 -2
- data/docs/doc/Doing/Errors/DoingStandardError.html +2 -2
- data/docs/doc/Doing/Errors/EmptyInput.html +2 -2
- data/docs/doc/Doing/Errors/NoResults.html +2 -2
- data/docs/doc/Doing/Errors/PluginException.html +3 -3
- data/docs/doc/Doing/Errors/UserCancelled.html +2 -2
- data/docs/doc/Doing/Errors/WrongCommand.html +2 -2
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/Hooks.html +6 -6
- data/docs/doc/Doing/Item.html +50 -16
- data/docs/doc/Doing/Items.html +10 -10
- data/docs/doc/Doing/LogAdapter.html +24 -24
- data/docs/doc/Doing/Note.html +7 -7
- data/docs/doc/Doing/Pager.html +4 -4
- data/docs/doc/Doing/Plugins.html +7 -7
- data/docs/doc/Doing/Prompt.html +16 -16
- data/docs/doc/Doing/Section.html +6 -6
- data/docs/doc/Doing/TemplateString.html +8 -8
- data/docs/doc/Doing/Types.html +206 -0
- data/docs/doc/Doing/Util/Backup.html +10 -10
- data/docs/doc/Doing/Util.html +16 -19
- data/docs/doc/Doing/WWID.html +65 -53
- data/docs/doc/Doing.html +4 -4
- data/docs/doc/GLI/Commands/Help.html +185 -0
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +17 -17
- data/docs/doc/GLI/Commands.html +5 -3
- data/docs/doc/GLI.html +4 -2
- data/docs/doc/Hash.html +119 -21
- data/docs/doc/Numeric.html +5 -5
- data/docs/doc/PhraseParser/Operator.html +4 -4
- data/docs/doc/PhraseParser/PhraseClause.html +5 -5
- data/docs/doc/PhraseParser/Query.html +10 -10
- data/docs/doc/PhraseParser/QueryParser.html +2 -2
- data/docs/doc/PhraseParser/QueryTransformer.html +2 -2
- data/docs/doc/PhraseParser/TermClause.html +5 -5
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +7 -7
- data/docs/doc/String.html +206 -51
- data/docs/doc/Symbol.html +8 -8
- data/docs/doc/Time.html +6 -6
- data/docs/doc/_index.html +51 -14
- data/docs/doc/class_list.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +348 -252
- data/docs/doc/top-level-namespace.html +2 -93
- data/docs/index.md +1 -1
- data/doing.rdoc +177 -20
- data/example_plugin.rb +2 -2
- data/lib/completion/_doing.zsh +24 -24
- data/lib/completion/doing.bash +31 -20
- data/lib/completion/doing.fish +32 -10
- 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 +2 -1
- data/lib/doing/configuration.rb +20 -14
- data/lib/doing/good.rb +64 -0
- data/lib/doing/hash.rb +28 -5
- data/lib/doing/help_monkey_patch.rb +31 -0
- data/lib/doing/hooks.rb +8 -4
- data/lib/doing/item.rb +24 -35
- data/lib/doing/log_adapter.rb +1 -1
- data/lib/doing/pager.rb +1 -1
- data/lib/doing/plugins/export/template_export.rb +9 -3
- data/lib/doing/plugins/import/calendar_import.rb +1 -1
- data/lib/doing/plugins/import/doing_import.rb +1 -1
- data/lib/doing/plugins/import/timing_import.rb +1 -1
- data/lib/doing/prompt.rb +4 -2
- data/lib/doing/string.rb +30 -12
- data/lib/doing/string_chronify.rb +1 -1
- data/lib/doing/template_string.rb +9 -2
- data/lib/doing/types.rb +23 -16
- data/lib/doing/util.rb +12 -11
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +65 -46
- data/lib/doing.rb +3 -0
- data/lib/helpers/threaded_tests.rb +106 -99
- data/lib/helpers/threaded_tests_string.rb +50 -0
- metadata +12 -2
data/bin/doing
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
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
|
-
require 'doing/types'
|
|
8
8
|
require 'tempfile'
|
|
9
9
|
require 'pp'
|
|
10
10
|
|
|
@@ -22,17 +22,12 @@ end
|
|
|
22
22
|
|
|
23
23
|
include GLI::App
|
|
24
24
|
include Doing::Errors
|
|
25
|
+
|
|
25
26
|
version Doing::VERSION
|
|
26
27
|
hide_commands_without_desc true
|
|
27
28
|
autocomplete_commands true
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
MissingConfigFile = Class.new(RuntimeError)
|
|
31
|
-
TagArray = Class.new(Array)
|
|
32
|
-
DateBeginString = Class.new(DateTime)
|
|
33
|
-
DateEndString = Class.new(DateTime)
|
|
34
|
-
DateRangeString = Class.new(Array)
|
|
35
|
-
DateIntervalString = Class.new(DateTime)
|
|
30
|
+
include Doing::Types
|
|
36
31
|
|
|
37
32
|
colors = Doing::Color
|
|
38
33
|
wwid = Doing::WWID.new
|
|
@@ -72,6 +67,13 @@ if settings.dig('plugins', 'command_path')
|
|
|
72
67
|
commands_from File.expand_path(settings.dig('plugins', 'command_path'))
|
|
73
68
|
end
|
|
74
69
|
|
|
70
|
+
accept TemplateName do |value|
|
|
71
|
+
res = settings['templates'].keys.select { |k| k =~ value.to_rx(distance: 2) }
|
|
72
|
+
raise InvalidArgument, "Unknown template: #{value}" unless res.good?
|
|
73
|
+
|
|
74
|
+
res.group_by(&:length).min.last[0]
|
|
75
|
+
end
|
|
76
|
+
|
|
75
77
|
accept DateBeginString do |value|
|
|
76
78
|
if value =~ REGEX_TIME
|
|
77
79
|
res = value
|
|
@@ -102,6 +104,13 @@ accept DateRangeString do |value|
|
|
|
102
104
|
[start, finish]
|
|
103
105
|
end
|
|
104
106
|
|
|
107
|
+
accept DateRangeOptionalString do |value|
|
|
108
|
+
start, finish = value.split_date_range
|
|
109
|
+
raise InvalidTimeExpression, 'Invalid range' unless start
|
|
110
|
+
|
|
111
|
+
[start, finish]
|
|
112
|
+
end
|
|
113
|
+
|
|
105
114
|
accept DateIntervalString do |value|
|
|
106
115
|
res = value.chronify_qty
|
|
107
116
|
raise InvalidTimeExpression, 'Invalid time quantity' unless res
|
|
@@ -134,7 +143,7 @@ desc 'Use a pager when output is longer than screen'
|
|
|
134
143
|
switch %i[p pager], default_value: settings['paginate']
|
|
135
144
|
|
|
136
145
|
desc 'Answer yes/no menus with default option'
|
|
137
|
-
switch [:default], default_value: false
|
|
146
|
+
switch [:default], default_value: false, negatable: false
|
|
138
147
|
|
|
139
148
|
desc 'Answer all yes/no menus with yes'
|
|
140
149
|
switch [:yes], negatable: false
|
|
@@ -251,7 +260,7 @@ command %i[again resume] do |c|
|
|
|
251
260
|
|
|
252
261
|
options[:note] = note
|
|
253
262
|
|
|
254
|
-
opts = options.
|
|
263
|
+
opts = options.clone
|
|
255
264
|
|
|
256
265
|
opts[:tag] = tags
|
|
257
266
|
opts[:tag_bool] = options[:bool].normalize_bool
|
|
@@ -370,7 +379,7 @@ desc 'Add a completed item with @done(date). No argument finishes last entry'
|
|
|
370
379
|
long_desc 'Use this command to add an entry after you\'ve already finished it. It will be immediately marked as @done.
|
|
371
380
|
You can modify the start and end times of the entry using the --back, --took, and --at flags, making it an easy
|
|
372
381
|
way to add entries in post and maintain accurate (albeit manual) time tracking.'
|
|
373
|
-
arg_name 'ENTRY'
|
|
382
|
+
arg_name 'ENTRY', optional: true
|
|
374
383
|
command %i[done did] do |c|
|
|
375
384
|
c.example 'doing done', desc: 'Tag the last entry @done'
|
|
376
385
|
c.example 'doing done I already finished this', desc: 'Add a new entry and immediately mark it @done'
|
|
@@ -502,7 +511,7 @@ command %i[done did] do |c|
|
|
|
502
511
|
raise NoResults, 'No results'
|
|
503
512
|
end
|
|
504
513
|
|
|
505
|
-
old_entry = last_entry.
|
|
514
|
+
old_entry = last_entry.clone
|
|
506
515
|
last_entry.note.add(note)
|
|
507
516
|
input = ["#{last_entry.date.strftime('%F %R | ')}#{last_entry.title}", last_entry.note.strip_lines.join("\n")].join("\n")
|
|
508
517
|
else
|
|
@@ -511,13 +520,13 @@ command %i[done did] do |c|
|
|
|
511
520
|
end
|
|
512
521
|
|
|
513
522
|
input = wwid.fork_editor(input).strip
|
|
514
|
-
raise EmptyInput, 'No content' unless input
|
|
523
|
+
raise EmptyInput, 'No content' unless input.good?
|
|
515
524
|
|
|
516
525
|
d, title, note = wwid.format_input(input)
|
|
517
526
|
|
|
518
527
|
if options[:ask]
|
|
519
528
|
ask_note = Doing::Prompt.read_lines(prompt: 'Add a note')
|
|
520
|
-
note.add(ask_note)
|
|
529
|
+
note.add(ask_note) if ask_note.good?
|
|
521
530
|
end
|
|
522
531
|
|
|
523
532
|
date = d.nil? ? date : d
|
|
@@ -533,15 +542,16 @@ command %i[done did] do |c|
|
|
|
533
542
|
if (is_new)
|
|
534
543
|
Doing::Hooks.trigger :pre_entry_add, wwid, new_entry
|
|
535
544
|
wwid.content.push(new_entry)
|
|
536
|
-
Doing::Hooks.trigger :post_entry_added, wwid, new_entry
|
|
545
|
+
Doing::Hooks.trigger :post_entry_added, wwid, new_entry
|
|
537
546
|
else
|
|
547
|
+
old = old_entry.clone
|
|
538
548
|
wwid.content.update_item(old_entry, new_entry)
|
|
539
|
-
Doing::Hooks.trigger :post_entry_updated, wwid, new_entry unless options[:archive]
|
|
549
|
+
Doing::Hooks.trigger :post_entry_updated, wwid, new_entry, old unless options[:archive]
|
|
540
550
|
end
|
|
541
551
|
|
|
542
552
|
if options[:archive]
|
|
543
553
|
wwid.move_item(new_entry, 'Archive', label: true)
|
|
544
|
-
Doing::Hooks.trigger :post_entry_updated, wwid, new_entry
|
|
554
|
+
Doing::Hooks.trigger :post_entry_updated, wwid, new_entry, old_entry
|
|
545
555
|
end
|
|
546
556
|
|
|
547
557
|
wwid.write(wwid.doing_file)
|
|
@@ -580,7 +590,7 @@ command %i[done did] do |c|
|
|
|
580
590
|
|
|
581
591
|
Doing::Hooks.trigger :pre_entry_add, wwid, new_entry
|
|
582
592
|
wwid.content.push(new_entry)
|
|
583
|
-
Doing::Hooks.trigger :post_entry_added, wwid, new_entry
|
|
593
|
+
Doing::Hooks.trigger :post_entry_added, wwid, new_entry
|
|
584
594
|
wwid.write(wwid.doing_file)
|
|
585
595
|
Doing.logger.info('New entry:', %(added "#{new_entry.date.relative_date}: #{new_entry.title}" to #{section}))
|
|
586
596
|
elsif $stdin.stat.size.positive?
|
|
@@ -604,7 +614,7 @@ command %i[done did] do |c|
|
|
|
604
614
|
|
|
605
615
|
Doing::Hooks.trigger :pre_entry_add, wwid, new_entry
|
|
606
616
|
wwid.content.push(new_entry)
|
|
607
|
-
Doing::Hooks.trigger :post_entry_added, wwid, new_entry
|
|
617
|
+
Doing::Hooks.trigger :post_entry_added, wwid, new_entry
|
|
608
618
|
|
|
609
619
|
wwid.write(wwid.doing_file)
|
|
610
620
|
Doing.logger.info('New entry:', %(added "#{new_entry.date.relative_date}: #{new_entry.title}" to #{section}))
|
|
@@ -617,7 +627,7 @@ end
|
|
|
617
627
|
# @@finish
|
|
618
628
|
desc 'Mark last X entries as @done'
|
|
619
629
|
long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
|
|
620
|
-
arg_name 'COUNT'
|
|
630
|
+
arg_name 'COUNT', optional: true
|
|
621
631
|
command :finish do |c|
|
|
622
632
|
c.example 'doing finish', desc: 'Mark the last entry @done'
|
|
623
633
|
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'
|
|
@@ -897,7 +907,7 @@ long_desc 'The @meanwhile tag allows you to have long-running entries that encom
|
|
|
897
907
|
This command makes it easy to start and stop these overarching entries. Just run `doing meanwhile Starting work on this
|
|
898
908
|
big project` to start a @meanwhile entry, add other entries as you work on the project, then use `doing meanwhile` by
|
|
899
909
|
itself to mark the entry as @done.'
|
|
900
|
-
arg_name 'ENTRY'
|
|
910
|
+
arg_name 'ENTRY', optional: true
|
|
901
911
|
command :meanwhile do |c|
|
|
902
912
|
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'
|
|
903
913
|
c.example 'doing meanwhile', desc: 'Finish any open @meanwhile entry'
|
|
@@ -948,7 +958,7 @@ command :meanwhile do |c|
|
|
|
948
958
|
input += date.strftime('%F %R | ')
|
|
949
959
|
input += args.join(' ') unless args.empty?
|
|
950
960
|
input += "\n#{options[:note]}" if options[:note]
|
|
951
|
-
input += "\n#{ask_note}" unless ask_note.
|
|
961
|
+
input += "\n#{ask_note}" unless ask_note.good?
|
|
952
962
|
|
|
953
963
|
input = wwid.fork_editor(input).strip
|
|
954
964
|
elsif !args.empty?
|
|
@@ -957,7 +967,7 @@ command :meanwhile do |c|
|
|
|
957
967
|
input = $stdin.read.strip
|
|
958
968
|
end
|
|
959
969
|
|
|
960
|
-
if input
|
|
970
|
+
if input.good?
|
|
961
971
|
d, input, note = wwid.format_input(input)
|
|
962
972
|
unless d.nil?
|
|
963
973
|
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
|
@@ -970,7 +980,7 @@ command :meanwhile do |c|
|
|
|
970
980
|
|
|
971
981
|
unless options[:editor]
|
|
972
982
|
note.add(options[:note]) if options[:note]
|
|
973
|
-
note.add(ask_note)
|
|
983
|
+
note.add(ask_note) if ask_note.good?
|
|
974
984
|
end
|
|
975
985
|
|
|
976
986
|
wwid.stop_start('meanwhile', { new_item: input, back: date, section: section, archive: options[:archive], note: note })
|
|
@@ -987,7 +997,7 @@ long_desc %(
|
|
|
987
997
|
|
|
988
998
|
Use -e to load the last entry in a text editor where you can append a note.
|
|
989
999
|
)
|
|
990
|
-
arg_name 'NOTE_TEXT'
|
|
1000
|
+
arg_name 'NOTE_TEXT', optional: true
|
|
991
1001
|
command :note do |c|
|
|
992
1002
|
c.example 'doing note', desc: 'Open the last entry in $EDITOR to append a note'
|
|
993
1003
|
c.example 'doing note "Just a quick annotation"', desc: 'Add a quick note to the last entry'
|
|
@@ -1055,8 +1065,8 @@ command :note do |c|
|
|
|
1055
1065
|
options[:search] = search
|
|
1056
1066
|
end
|
|
1057
1067
|
|
|
1058
|
-
|
|
1059
1068
|
last_entry = wwid.last_entry(options)
|
|
1069
|
+
old_entry = last_entry.clone
|
|
1060
1070
|
|
|
1061
1071
|
unless last_entry
|
|
1062
1072
|
Doing.logger.warn('Not found:', 'No entry matching parameters was found.')
|
|
@@ -1065,44 +1075,44 @@ command :note do |c|
|
|
|
1065
1075
|
|
|
1066
1076
|
last_note = last_entry.note || Doing::Note.new
|
|
1067
1077
|
new_note = Doing::Note.new
|
|
1068
|
-
ask_note = options[:ask] ? Doing::Prompt.read_lines(prompt: 'Add a note') : ''
|
|
1069
1078
|
|
|
1070
|
-
if
|
|
1071
|
-
|
|
1079
|
+
if $stdin.stat.size.positive?
|
|
1080
|
+
new_note.add($stdin.read.strip)
|
|
1081
|
+
end
|
|
1072
1082
|
|
|
1073
|
-
|
|
1083
|
+
unless args.empty?
|
|
1084
|
+
new_note.add(args.join(' '))
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
if options[:editor]
|
|
1088
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
|
1074
1089
|
|
|
1075
1090
|
if options[:remove]
|
|
1076
|
-
|
|
1091
|
+
input = Doing::Note.new
|
|
1077
1092
|
else
|
|
1078
|
-
|
|
1093
|
+
input = last_entry.note || Doing::Note.new
|
|
1079
1094
|
end
|
|
1080
1095
|
|
|
1096
|
+
input.add(new_note)
|
|
1081
1097
|
|
|
1082
|
-
|
|
1083
|
-
input.add(ask_note) unless ask_note.empty?
|
|
1084
|
-
|
|
1085
|
-
input = wwid.fork_editor(prev_input.strip_lines.join("\n"), message: nil).strip
|
|
1086
|
-
note = input
|
|
1098
|
+
new_note = Doing::Note.new(wwid.fork_editor(input.strip_lines.join("\n"), message: nil).strip)
|
|
1087
1099
|
options[:remove] = true
|
|
1088
|
-
|
|
1089
|
-
elsif !args.empty?
|
|
1090
|
-
new_note.add(args.join(' '))
|
|
1091
|
-
elsif $stdin.stat.size.positive?
|
|
1092
|
-
new_note.add($stdin.read.strip)
|
|
1093
|
-
else
|
|
1094
|
-
raise EmptyInput, 'You must provide content when adding a note' unless options[:remove] || !ask_note.empty?
|
|
1100
|
+
end
|
|
1095
1101
|
|
|
1102
|
+
if (new_note.empty? && !options[:remove]) || options[:ask]
|
|
1103
|
+
$stderr.puts last_note if last_note.good?
|
|
1104
|
+
$stderr.puts new_note if new_note.good?
|
|
1105
|
+
new_note.add(Doing::Prompt.read_lines(prompt: 'Add a note'))
|
|
1096
1106
|
end
|
|
1097
1107
|
|
|
1098
|
-
|
|
1108
|
+
raise EmptyInput, 'You must provide content when adding a note' unless options[:remove] || new_note.good?
|
|
1099
1109
|
|
|
1100
1110
|
if last_note.equal?(new_note)
|
|
1101
1111
|
Doing.logger.debug('Skipped:', 'No note change')
|
|
1102
1112
|
else
|
|
1103
1113
|
last_note.add(new_note, replace: options[:remove])
|
|
1104
1114
|
Doing.logger.info('Entry updated:', last_entry.title)
|
|
1105
|
-
Doing::Hooks.trigger :post_entry_updated, wwid, last_entry
|
|
1115
|
+
Doing::Hooks.trigger :post_entry_updated, wwid, last_entry, old_entry
|
|
1106
1116
|
end
|
|
1107
1117
|
# new_entry = Doing::Item.new(last_entry.date, last_entry.title, last_entry.section, new_note)
|
|
1108
1118
|
wwid.write(wwid.doing_file)
|
|
@@ -1138,6 +1148,13 @@ command %i[now next] do |c|
|
|
|
1138
1148
|
c.arg_name 'DATE_STRING'
|
|
1139
1149
|
c.flag %i[b back started], type: DateBeginString
|
|
1140
1150
|
|
|
1151
|
+
c.desc %(
|
|
1152
|
+
Set a start and optionally end time as a date range ("from 1pm to 2:30pm").
|
|
1153
|
+
If an end time is provided, a dated @done tag will be added
|
|
1154
|
+
)
|
|
1155
|
+
c.arg_name 'TIME_RANGE'
|
|
1156
|
+
c.flag [:from], type: DateRangeString
|
|
1157
|
+
|
|
1141
1158
|
c.desc 'Timed entry, marks last entry in section as @done'
|
|
1142
1159
|
c.switch %i[f finish_last], negatable: false, default_value: false
|
|
1143
1160
|
|
|
@@ -1153,13 +1170,17 @@ command %i[now next] do |c|
|
|
|
1153
1170
|
# # c.flag [:a, :app]
|
|
1154
1171
|
|
|
1155
1172
|
c.action do |_global_options, options, args|
|
|
1173
|
+
raise InvalidArgument, "--back and --from cannot be used together" if options[:back] && options[:from]
|
|
1174
|
+
|
|
1156
1175
|
if options[:back]
|
|
1157
1176
|
date = options[:back]
|
|
1158
|
-
|
|
1159
|
-
|
|
1177
|
+
elsif options[:from]
|
|
1178
|
+
date, finish_date = options[:from]
|
|
1179
|
+
options[:done] = finish_date
|
|
1160
1180
|
else
|
|
1161
1181
|
date = Time.now
|
|
1162
1182
|
end
|
|
1183
|
+
raise InvalidTimeExpression.new('unable to parse date string', topic: 'Parser:') if date.nil?
|
|
1163
1184
|
|
|
1164
1185
|
if options[:section]
|
|
1165
1186
|
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
@@ -1174,16 +1195,17 @@ command %i[now next] do |c|
|
|
|
1174
1195
|
|
|
1175
1196
|
input = date.strftime('%F %R | ')
|
|
1176
1197
|
input += args.join(' ') unless args.empty?
|
|
1198
|
+
input += " @done(#{options[:done].strftime('%F %R')})" if options[:done]
|
|
1177
1199
|
input += "\n#{options[:note]}" if options[:note]
|
|
1178
|
-
input += "\n#{ask_note}"
|
|
1200
|
+
input += "\n#{ask_note}" if ask_note.good?
|
|
1179
1201
|
input = wwid.fork_editor(input).strip
|
|
1180
1202
|
|
|
1181
1203
|
d, title, note = wwid.format_input(input)
|
|
1182
|
-
raise EmptyInput, 'No content'
|
|
1204
|
+
raise EmptyInput, 'No content' unless title.good?
|
|
1183
1205
|
|
|
1184
1206
|
if ask_note.empty? && options[:ask]
|
|
1185
1207
|
ask_note = Doing::Prompt.read_lines(prompt: 'Add a note')
|
|
1186
|
-
note.add(ask_note)
|
|
1208
|
+
note.add(ask_note) if ask_note.good?
|
|
1187
1209
|
end
|
|
1188
1210
|
|
|
1189
1211
|
date = d.nil? ? date : d
|
|
@@ -1193,8 +1215,15 @@ command %i[now next] do |c|
|
|
|
1193
1215
|
d, title, note = wwid.format_input(args.join(' '))
|
|
1194
1216
|
date = d.nil? ? date : d
|
|
1195
1217
|
note.add(options[:note]) if options[:note]
|
|
1196
|
-
note.add(ask_note)
|
|
1197
|
-
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
1218
|
+
note.add(ask_note) if ask_note.good?
|
|
1219
|
+
entry = wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
1220
|
+
if options[:done] && entry.should_finish?
|
|
1221
|
+
if entry.should_time?
|
|
1222
|
+
entry.tag('done', value: options[:done])
|
|
1223
|
+
else
|
|
1224
|
+
entry.tag('done')
|
|
1225
|
+
end
|
|
1226
|
+
end
|
|
1198
1227
|
wwid.write(wwid.doing_file)
|
|
1199
1228
|
elsif $stdin.stat.size.positive?
|
|
1200
1229
|
input = $stdin.read.strip
|
|
@@ -1206,15 +1235,22 @@ command %i[now next] do |c|
|
|
|
1206
1235
|
note.add(options[:note]) if options[:note]
|
|
1207
1236
|
if ask_note.empty? && options[:ask]
|
|
1208
1237
|
ask_note = Doing::Prompt.read_lines(prompt: 'Add a note')
|
|
1209
|
-
note.add(ask_note)
|
|
1238
|
+
note.add(ask_note) if ask_note.good?
|
|
1239
|
+
end
|
|
1240
|
+
entry = wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
1241
|
+
if options[:done] && entry.should_finish?
|
|
1242
|
+
if entry.should_time?
|
|
1243
|
+
entry.tag('done', value: options[:done])
|
|
1244
|
+
else
|
|
1245
|
+
entry.tag('done')
|
|
1246
|
+
end
|
|
1210
1247
|
end
|
|
1211
|
-
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
1212
1248
|
wwid.write(wwid.doing_file)
|
|
1213
1249
|
else
|
|
1214
1250
|
tags = wwid.all_tags(wwid.content)
|
|
1215
1251
|
$stderr.puts Doing::Color.boldgreen("Add a new entry. Tab will autocomplete known tags. Ctrl-c to cancel.")
|
|
1216
1252
|
title = Doing::Prompt.read_line(prompt: 'Entry content', completions: tags)
|
|
1217
|
-
raise EmptyInput, 'You must provide content when creating a new entry'
|
|
1253
|
+
raise EmptyInput, 'You must provide content when creating a new entry' unless title.good?
|
|
1218
1254
|
|
|
1219
1255
|
note = Doing::Note.new
|
|
1220
1256
|
note.add(options[:note]) if options[:note]
|
|
@@ -1222,7 +1258,14 @@ command %i[now next] do |c|
|
|
|
1222
1258
|
ask_note = res ? Doing::Prompt.read_lines(prompt: 'Enter note') : []
|
|
1223
1259
|
note.add(ask_note)
|
|
1224
1260
|
|
|
1225
|
-
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
1261
|
+
entry = wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
|
1262
|
+
if options[:done] && entry.should_finish?
|
|
1263
|
+
if entry.should_time?
|
|
1264
|
+
entry.tag('done', value: options[:done])
|
|
1265
|
+
else
|
|
1266
|
+
entry.tag('done')
|
|
1267
|
+
end
|
|
1268
|
+
end
|
|
1226
1269
|
wwid.write(wwid.doing_file)
|
|
1227
1270
|
end
|
|
1228
1271
|
end
|
|
@@ -1233,7 +1276,7 @@ desc 'Reset the start time of an entry'
|
|
|
1233
1276
|
long_desc 'Update the start time of the last entry or the last entry matching a tag/search filter.
|
|
1234
1277
|
If no argument is provided, the start time will be reset to the current time.
|
|
1235
1278
|
If a date string is provided as an argument, the start time will be set to the parsed result.'
|
|
1236
|
-
arg_name 'DATE_STRING'
|
|
1279
|
+
arg_name 'DATE_STRING', optional: true
|
|
1237
1280
|
command %i[reset begin] do |c|
|
|
1238
1281
|
c.example 'doing reset', desc: 'Reset the start time of the last entry to the current time'
|
|
1239
1282
|
c.example 'doing reset --tag project1', desc: 'Reset the start time of the most recent entry tagged @project1 to the current time'
|
|
@@ -1247,6 +1290,9 @@ command %i[reset begin] do |c|
|
|
|
1247
1290
|
c.desc 'Resume entry (remove @done)'
|
|
1248
1291
|
c.switch %i[r resume], default_value: true
|
|
1249
1292
|
|
|
1293
|
+
c.desc 'Change start date but do not remove @done (shortcut for --no-resume)'
|
|
1294
|
+
c.switch [:n]
|
|
1295
|
+
|
|
1250
1296
|
c.desc 'Reset last entry matching tag. Wildcards allowed (*, ?)'
|
|
1251
1297
|
c.arg_name 'TAG'
|
|
1252
1298
|
c.flag [:tag]
|
|
@@ -1322,8 +1368,10 @@ command %i[reset begin] do |c|
|
|
|
1322
1368
|
return
|
|
1323
1369
|
end
|
|
1324
1370
|
|
|
1371
|
+
old_item = last_entry.clone
|
|
1372
|
+
|
|
1325
1373
|
wwid.reset_item(last_entry, date: reset_date, resume: options[:resume])
|
|
1326
|
-
Doing::Hooks.trigger :post_entry_updated, wwid, last_entry
|
|
1374
|
+
Doing::Hooks.trigger :post_entry_updated, wwid, last_entry, old_item
|
|
1327
1375
|
# new_entry = Doing::Item.new(last_entry.date, last_entry.title, last_entry.section, new_note)
|
|
1328
1376
|
|
|
1329
1377
|
wwid.write(wwid.doing_file)
|
|
@@ -1687,6 +1735,14 @@ command %i[grep search] do |c|
|
|
|
1687
1735
|
c.arg_name 'FORMAT'
|
|
1688
1736
|
c.flag %i[o output]
|
|
1689
1737
|
|
|
1738
|
+
c.desc "Output using a template from configuration"
|
|
1739
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
1740
|
+
c.flag [:config_template], type: TemplateName, default_value: 'default'
|
|
1741
|
+
|
|
1742
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
1743
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
1744
|
+
c.flag [:template]
|
|
1745
|
+
|
|
1690
1746
|
c.desc 'Show time intervals on @done tasks'
|
|
1691
1747
|
c.switch %i[t times], default_value: true, negatable: true
|
|
1692
1748
|
|
|
@@ -1741,7 +1797,7 @@ command %i[grep search] do |c|
|
|
|
1741
1797
|
options[:fuzzy] = false
|
|
1742
1798
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
1743
1799
|
|
|
1744
|
-
template = settings['templates'][
|
|
1800
|
+
template = settings['templates'][options[:config_template]].deep_merge(settings)
|
|
1745
1801
|
tags_color = template.key?('tags_color') ? template['tags_color'] : nil
|
|
1746
1802
|
|
|
1747
1803
|
section = wwid.guess_section(options[:section]) if options[:section]
|
|
@@ -1798,6 +1854,14 @@ command :last do |c|
|
|
|
1798
1854
|
c.arg_name 'QUERY'
|
|
1799
1855
|
c.flag [:search]
|
|
1800
1856
|
|
|
1857
|
+
c.desc "Output using a template from configuration"
|
|
1858
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
1859
|
+
c.flag [:config_template], type: TemplateName, default_value: 'last'
|
|
1860
|
+
|
|
1861
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
1862
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
1863
|
+
c.flag [:template]
|
|
1864
|
+
|
|
1801
1865
|
c.desc "Highlight search matches in output. Only affects command line output"
|
|
1802
1866
|
c.switch %i[h hilite], default_value: settings.dig('search', 'highlight')
|
|
1803
1867
|
|
|
@@ -1851,6 +1915,8 @@ command :last do |c|
|
|
|
1851
1915
|
else
|
|
1852
1916
|
last = wwid.last(times: true, section: options[:section],
|
|
1853
1917
|
options: {
|
|
1918
|
+
config_template: options[:config_template],
|
|
1919
|
+
template: options[:template],
|
|
1854
1920
|
duration: options[:duration],
|
|
1855
1921
|
search: options[:search],
|
|
1856
1922
|
fuzzy: options[:fuzzy],
|
|
@@ -1885,6 +1951,14 @@ command :recent do |c|
|
|
|
1885
1951
|
c.desc 'Show time intervals on @done tasks'
|
|
1886
1952
|
c.switch %i[t times], default_value: true, negatable: true
|
|
1887
1953
|
|
|
1954
|
+
c.desc "Output using a template from configuration"
|
|
1955
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
1956
|
+
c.flag [:config_template], type: TemplateName, default_value: 'recent'
|
|
1957
|
+
|
|
1958
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
1959
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
1960
|
+
c.flag [:template]
|
|
1961
|
+
|
|
1888
1962
|
c.desc 'Show elapsed time on entries without @done tag'
|
|
1889
1963
|
c.switch [:duration]
|
|
1890
1964
|
|
|
@@ -1928,7 +2002,9 @@ command :recent do |c|
|
|
|
1928
2002
|
times: options[:times],
|
|
1929
2003
|
totals: options[:totals],
|
|
1930
2004
|
interactive: options[:interactive],
|
|
1931
|
-
duration: options[:duration]
|
|
2005
|
+
duration: options[:duration],
|
|
2006
|
+
config_template: options[:config_template],
|
|
2007
|
+
template: options[:template]
|
|
1932
2008
|
}
|
|
1933
2009
|
|
|
1934
2010
|
Doing::Pager::page wwid.recent(count, section.cap_first, opts)
|
|
@@ -2043,6 +2119,14 @@ command :show do |c|
|
|
|
2043
2119
|
c.desc 'Only show items with recorded time intervals'
|
|
2044
2120
|
c.switch [:only_timed], default_value: false, negatable: false
|
|
2045
2121
|
|
|
2122
|
+
c.desc "Output using a template from configuration"
|
|
2123
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2124
|
+
c.flag [:config_template], type: TemplateName, default_value: 'default'
|
|
2125
|
+
|
|
2126
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2127
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2128
|
+
c.flag [:template]
|
|
2129
|
+
|
|
2046
2130
|
c.desc 'Select section or tag to display from a menu'
|
|
2047
2131
|
c.switch %i[m menu], negatable: false, default_value: false
|
|
2048
2132
|
|
|
@@ -2099,7 +2183,7 @@ command :show do |c|
|
|
|
2099
2183
|
|
|
2100
2184
|
options[:times] = true if options[:totals]
|
|
2101
2185
|
|
|
2102
|
-
template = settings['templates'][
|
|
2186
|
+
template = settings['templates'][options[:config_template]].deep_merge({
|
|
2103
2187
|
'wrap_width' => settings['wrap_width'] || 0,
|
|
2104
2188
|
'date_format' => settings['default_date_format'],
|
|
2105
2189
|
'order' => settings['order'] || 'asc',
|
|
@@ -2116,7 +2200,7 @@ command :show do |c|
|
|
|
2116
2200
|
|
|
2117
2201
|
options[:section] = section
|
|
2118
2202
|
|
|
2119
|
-
|
|
2203
|
+
if tags.good?
|
|
2120
2204
|
tag_filter = {
|
|
2121
2205
|
'tags' => tags,
|
|
2122
2206
|
'bool' => options[:bool].normalize_bool
|
|
@@ -2135,7 +2219,7 @@ command :show do |c|
|
|
|
2135
2219
|
# options[:bool] = :and unless tags.empty?
|
|
2136
2220
|
|
|
2137
2221
|
tags = tag.split(/ +/).map { |t| t.strip.sub(/^@?/, '') } if tag =~ /^@/
|
|
2138
|
-
|
|
2222
|
+
if tags.good?
|
|
2139
2223
|
tag_filter = {
|
|
2140
2224
|
'tags' => tags,
|
|
2141
2225
|
'bool' => options[:bool].normalize_bool
|
|
@@ -2146,7 +2230,7 @@ command :show do |c|
|
|
|
2146
2230
|
|
|
2147
2231
|
options[:age] ||= :newest
|
|
2148
2232
|
|
|
2149
|
-
opt = options.
|
|
2233
|
+
opt = options.clone
|
|
2150
2234
|
opt[:age] = options[:age].normalize_age(:newest) if options[:age]
|
|
2151
2235
|
opt[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2152
2236
|
opt[:count] = options[:count].to_i
|
|
@@ -2163,6 +2247,7 @@ end
|
|
|
2163
2247
|
|
|
2164
2248
|
# @@tags
|
|
2165
2249
|
desc 'List all tags in the current Doing file'
|
|
2250
|
+
arg_name 'MAX_COUNT', optional: true, type: Integer
|
|
2166
2251
|
command :tags do |c|
|
|
2167
2252
|
c.desc 'Section'
|
|
2168
2253
|
c.arg_name 'SECTION_NAME'
|
|
@@ -2171,6 +2256,9 @@ command :tags do |c|
|
|
|
2171
2256
|
c.desc 'Show count of occurrences'
|
|
2172
2257
|
c.switch %i[c counts]
|
|
2173
2258
|
|
|
2259
|
+
c.desc 'Output in a single line with @ symbols. Ignored if --counts is specified.'
|
|
2260
|
+
c.switch %i[l line]
|
|
2261
|
+
|
|
2174
2262
|
c.desc 'Sort by name or count'
|
|
2175
2263
|
c.arg_name 'SORT_ORDER'
|
|
2176
2264
|
c.flag %i[sort], default_value: 'name', must_match: /^(?:n(?:ame)?|c(?:ount)?)$/
|
|
@@ -2214,6 +2302,7 @@ command :tags do |c|
|
|
|
2214
2302
|
|
|
2215
2303
|
c.action do |_global, options, args|
|
|
2216
2304
|
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
2305
|
+
options[:count] = args.count.positive? ? args[0].to_i : 0
|
|
2217
2306
|
|
|
2218
2307
|
items = wwid.filter_items([], opt: options)
|
|
2219
2308
|
|
|
@@ -2241,7 +2330,11 @@ command :tags do |c|
|
|
|
2241
2330
|
if options[:counts]
|
|
2242
2331
|
tags.each { |t, c| puts "#{t} (#{c})" }
|
|
2243
2332
|
else
|
|
2244
|
-
|
|
2333
|
+
if options[:line]
|
|
2334
|
+
puts tags.map { |t, c| t }.to_tags.join(' ')
|
|
2335
|
+
else
|
|
2336
|
+
tags.each { |t, c| puts "#{t}" }
|
|
2337
|
+
end
|
|
2245
2338
|
end
|
|
2246
2339
|
end
|
|
2247
2340
|
end
|
|
@@ -2279,6 +2372,14 @@ command :today do |c|
|
|
|
2279
2372
|
c.arg_name 'FORMAT'
|
|
2280
2373
|
c.flag %i[o output]
|
|
2281
2374
|
|
|
2375
|
+
c.desc "Output using a template from configuration"
|
|
2376
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2377
|
+
c.flag [:config_template], type: TemplateName, default_value: 'today'
|
|
2378
|
+
|
|
2379
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2380
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2381
|
+
c.flag [:template]
|
|
2382
|
+
|
|
2282
2383
|
c.desc 'View entries before specified time (e.g. 8am, 12:30pm, 15:00)'
|
|
2283
2384
|
c.arg_name 'TIME_STRING'
|
|
2284
2385
|
c.flag [:before]
|
|
@@ -2298,7 +2399,7 @@ command :today do |c|
|
|
|
2298
2399
|
|
|
2299
2400
|
options[:times] = true if options[:totals]
|
|
2300
2401
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2301
|
-
filter_options = %i[after before duration from section sort_tags totals].each_with_object({}) { |k, hsh| hsh[k] = options[k] }
|
|
2402
|
+
filter_options = %i[after before duration from section sort_tags totals template config_template].each_with_object({}) { |k, hsh| hsh[k] = options[k] }
|
|
2302
2403
|
|
|
2303
2404
|
Doing::Pager.page wwid.today(options[:times], options[:output], filter_options).chomp
|
|
2304
2405
|
end
|
|
@@ -2338,6 +2439,14 @@ command :on do |c|
|
|
|
2338
2439
|
c.arg_name 'FORMAT'
|
|
2339
2440
|
c.flag %i[o output]
|
|
2340
2441
|
|
|
2442
|
+
c.desc "Output using a template from configuration"
|
|
2443
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2444
|
+
c.flag [:config_template], type: TemplateName, default_value: 'default'
|
|
2445
|
+
|
|
2446
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2447
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2448
|
+
c.flag [:template]
|
|
2449
|
+
|
|
2341
2450
|
c.action do |_global_options, options, args|
|
|
2342
2451
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
2343
2452
|
|
|
@@ -2359,7 +2468,7 @@ command :on do |c|
|
|
|
2359
2468
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2360
2469
|
|
|
2361
2470
|
Doing::Pager.page wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
|
2362
|
-
{ duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
2471
|
+
{ template: options[:template], config_template: options[:config_template], duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
2363
2472
|
end
|
|
2364
2473
|
end
|
|
2365
2474
|
|
|
@@ -2395,6 +2504,14 @@ command :since do |c|
|
|
|
2395
2504
|
c.arg_name 'FORMAT'
|
|
2396
2505
|
c.flag %i[o output]
|
|
2397
2506
|
|
|
2507
|
+
c.desc "Output using a template from configuration"
|
|
2508
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2509
|
+
c.flag [:config_template], type: TemplateName, default_value: 'default'
|
|
2510
|
+
|
|
2511
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2512
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2513
|
+
c.flag [:template]
|
|
2514
|
+
|
|
2398
2515
|
c.action do |_global_options, options, args|
|
|
2399
2516
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
2400
2517
|
|
|
@@ -2416,7 +2533,7 @@ command :since do |c|
|
|
|
2416
2533
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
|
2417
2534
|
|
|
2418
2535
|
Doing::Pager.page wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
|
2419
|
-
{ duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
2536
|
+
{ template: options[:template], config_template: options[:config_template], duration: options[:duration], totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
|
2420
2537
|
end
|
|
2421
2538
|
end
|
|
2422
2539
|
|
|
@@ -2551,17 +2668,17 @@ command :view do |c|
|
|
|
2551
2668
|
view = wwid.get_view(title)
|
|
2552
2669
|
|
|
2553
2670
|
if view
|
|
2554
|
-
page_title = view
|
|
2671
|
+
page_title = view['title'] || title.cap_first
|
|
2555
2672
|
only_timed = if (view.key?('only_timed') && view['only_timed']) || options[:only_timed]
|
|
2556
2673
|
true
|
|
2557
2674
|
else
|
|
2558
2675
|
false
|
|
2559
2676
|
end
|
|
2560
2677
|
|
|
2561
|
-
template = view
|
|
2562
|
-
date_format = view
|
|
2678
|
+
template = view['template'] || nil
|
|
2679
|
+
date_format = view['date_format'] || nil
|
|
2563
2680
|
|
|
2564
|
-
tags_color = view
|
|
2681
|
+
tags_color = view['tags_color'] || nil
|
|
2565
2682
|
tag_filter = false
|
|
2566
2683
|
if options[:tag]
|
|
2567
2684
|
tag_filter = { 'tags' => [], 'bool' => 'OR' }
|
|
@@ -2572,7 +2689,7 @@ command :view do |c|
|
|
|
2572
2689
|
else
|
|
2573
2690
|
options[:tag].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
|
|
2574
2691
|
end
|
|
2575
|
-
elsif view.key?('tags') &&
|
|
2692
|
+
elsif view.key?('tags') && view['tags'].good?
|
|
2576
2693
|
tag_filter = { 'tags' => [], 'bool' => 'OR' }
|
|
2577
2694
|
bool = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].normalize_bool : :pattern
|
|
2578
2695
|
tag_filter['bool'] = bool
|
|
@@ -2591,19 +2708,19 @@ command :view do |c|
|
|
|
2591
2708
|
section = if options[:section]
|
|
2592
2709
|
section
|
|
2593
2710
|
else
|
|
2594
|
-
view
|
|
2711
|
+
view['section'] || settings['current_section']
|
|
2595
2712
|
end
|
|
2596
|
-
order = view
|
|
2713
|
+
order = view['order']&.normalize_order || 'asc'
|
|
2597
2714
|
|
|
2598
2715
|
totals = if options[:totals]
|
|
2599
2716
|
true
|
|
2600
2717
|
else
|
|
2601
|
-
view
|
|
2718
|
+
view['totals'] || false
|
|
2602
2719
|
end
|
|
2603
2720
|
tag_order = if options[:tag_order]
|
|
2604
2721
|
options[:tag_order].normalize_order
|
|
2605
2722
|
else
|
|
2606
|
-
view
|
|
2723
|
+
view['tag_order']&.normalize_order || 'asc'
|
|
2607
2724
|
end
|
|
2608
2725
|
|
|
2609
2726
|
options[:times] = true if totals
|
|
@@ -2630,7 +2747,7 @@ command :view do |c|
|
|
|
2630
2747
|
|
|
2631
2748
|
options[:age] ||= :newest
|
|
2632
2749
|
|
|
2633
|
-
opts = options.
|
|
2750
|
+
opts = options.clone
|
|
2634
2751
|
opts[:age] = options[:age].normalize_age(:newest)
|
|
2635
2752
|
opts[:count] = count
|
|
2636
2753
|
opts[:format] = date_format
|
|
@@ -2676,6 +2793,14 @@ command :yesterday do |c|
|
|
|
2676
2793
|
c.arg_name 'FORMAT'
|
|
2677
2794
|
c.flag %i[o output]
|
|
2678
2795
|
|
|
2796
|
+
c.desc "Output using a template from configuration"
|
|
2797
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
2798
|
+
c.flag [:config_template], type: TemplateName, default_value: 'today'
|
|
2799
|
+
|
|
2800
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
2801
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
2802
|
+
c.flag [:template]
|
|
2803
|
+
|
|
2679
2804
|
c.desc 'Show time intervals on @done tasks'
|
|
2680
2805
|
c.switch %i[t times], default_value: true, negatable: true
|
|
2681
2806
|
|
|
@@ -2717,16 +2842,10 @@ command :yesterday do |c|
|
|
|
2717
2842
|
end.join(' to ').split_date_range
|
|
2718
2843
|
end
|
|
2719
2844
|
|
|
2720
|
-
opt =
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
from: options[:from],
|
|
2725
|
-
sort_tags: options[:sort_tags],
|
|
2726
|
-
tag_order: options[:tag_order].normalize_order,
|
|
2727
|
-
totals: options[:totals],
|
|
2728
|
-
order: settings.dig('templates', 'today', 'order')
|
|
2729
|
-
}
|
|
2845
|
+
opt = options.clone
|
|
2846
|
+
opt[:tag_order] = options[:tag_order].normalize_order
|
|
2847
|
+
opt[:order] = settings.dig('templates', options[:config_template], 'order')
|
|
2848
|
+
|
|
2730
2849
|
Doing::Pager.page wwid.yesterday(options[:section], options[:times], options[:output], opt).chomp
|
|
2731
2850
|
end
|
|
2732
2851
|
end
|
|
@@ -3015,7 +3134,7 @@ command :open do |c|
|
|
|
3015
3134
|
end
|
|
3016
3135
|
|
|
3017
3136
|
c.action do |_global_options, options, _args|
|
|
3018
|
-
params = options.
|
|
3137
|
+
params = options.clone
|
|
3019
3138
|
params.delete_if do |k, v|
|
|
3020
3139
|
k.instance_of?(String) || v.nil? || v == false
|
|
3021
3140
|
end
|
|
@@ -3167,7 +3286,7 @@ command %i[archive move] do |c|
|
|
|
3167
3286
|
search.sub!(/^'?/, "'") if options[:exact]
|
|
3168
3287
|
end
|
|
3169
3288
|
|
|
3170
|
-
opts = options.
|
|
3289
|
+
opts = options.clone
|
|
3171
3290
|
opts[:search] = search
|
|
3172
3291
|
opts[:bool] = options[:bool].normalize_bool
|
|
3173
3292
|
opts[:destination] = options[:to]
|
|
@@ -3587,17 +3706,44 @@ end
|
|
|
3587
3706
|
|
|
3588
3707
|
# @@changelog @@changes
|
|
3589
3708
|
desc 'List recent changes in Doing'
|
|
3590
|
-
long_desc
|
|
3591
|
-
|
|
3709
|
+
long_desc %(Display a formatted list of changes in recent versions.
|
|
3710
|
+
|
|
3711
|
+
Without flags, displays only the most recent version.
|
|
3712
|
+
Use --lookup or --all for history.)
|
|
3713
|
+
command %i[changes changelog] do |c|
|
|
3714
|
+
c.desc 'Display all versions'
|
|
3715
|
+
c.switch %i[a all], default_value: false, negatable: false
|
|
3716
|
+
|
|
3717
|
+
c.desc %(Look up a specific version. Specify versions as "MAJ.MIN.PATCH", MIN
|
|
3718
|
+
and PATCH are optional. Use > or < to see all changes since or prior
|
|
3719
|
+
to a version.)
|
|
3720
|
+
c.arg_name 'VERSION'
|
|
3721
|
+
c.flag %i[l lookup], must_match: /^(?:(?:(?:[<>=]|p(?:rior)|b(?:efore)|o(?:lder)|s(?:ince)|a(?:fter)|n(?:ewer))? *[\d.*?]+ *)+|(?:[\d.]+ *-+ *[\d.]+))$/
|
|
3722
|
+
|
|
3723
|
+
c.desc %(Show changelogs matching search terms (uses pattern-based searching).
|
|
3724
|
+
Add slashes to search with regular expressions, e.g. `--search "/output.*flag/"`)
|
|
3725
|
+
c.flag %i[s search]
|
|
3726
|
+
|
|
3727
|
+
c.example 'doing changes', desc: 'View changes in the current version'
|
|
3728
|
+
c.example 'doing changes --all', desc: 'See the entire changelog'
|
|
3729
|
+
c.example 'doing changes --lookup 2.0.21', desc: 'See changes from version 2.0.21'
|
|
3730
|
+
c.example 'doing changes --lookup "> 2.1"', desc: 'See all changes since 2.1.0'
|
|
3731
|
+
c.example 'doing changes --search "tags +bool"', desc: 'See all changes containing "tags" and "bool"'
|
|
3732
|
+
c.example 'doing changes -l "> 2.1" -s "pattern"', desc: 'Lookup and search can be combined'
|
|
3733
|
+
|
|
3734
|
+
|
|
3592
3735
|
c.action do |_global_options, options, args|
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3736
|
+
cl = Doing::Changes.new(lookup: options[:lookup], search: options[:search])
|
|
3737
|
+
|
|
3738
|
+
content = if options[:all] || options[:search] || options[:lookup]
|
|
3739
|
+
cl.to_s
|
|
3740
|
+
else
|
|
3741
|
+
cl.latest
|
|
3742
|
+
end
|
|
3743
|
+
|
|
3744
|
+
parsed = TTY::Markdown.parse(content, width: 80, symbols: {override: {bullet: "•"}})
|
|
3745
|
+
Doing::Pager.paginate = true
|
|
3746
|
+
Doing::Pager.page parsed
|
|
3601
3747
|
end
|
|
3602
3748
|
end
|
|
3603
3749
|
|
|
@@ -3654,7 +3800,7 @@ pre do |global, _command, _options, _args|
|
|
|
3654
3800
|
Doing::Pager.paginate = global[:pager]
|
|
3655
3801
|
|
|
3656
3802
|
$stdout.puts "doing v#{Doing::VERSION}" if global[:version]
|
|
3657
|
-
unless
|
|
3803
|
+
unless $stdout.isatty
|
|
3658
3804
|
Doing::Color.coloring = global[:pager] ? global[:color] : false
|
|
3659
3805
|
else
|
|
3660
3806
|
Doing::Color.coloring = global[:color]
|
|
@@ -3704,7 +3850,12 @@ around do |global, command, options, arguments, code|
|
|
|
3704
3850
|
Doing::Prompt.force_answer = false
|
|
3705
3851
|
Doing.config.force_answer = false
|
|
3706
3852
|
else
|
|
3707
|
-
Doing::Prompt.default_answer =
|
|
3853
|
+
Doing::Prompt.default_answer = if $stdout.isatty
|
|
3854
|
+
global[:default]
|
|
3855
|
+
else
|
|
3856
|
+
true
|
|
3857
|
+
end
|
|
3858
|
+
|
|
3708
3859
|
Doing.config.force_answer = global[:default] ? true : false
|
|
3709
3860
|
end
|
|
3710
3861
|
|