doing 2.1.21 → 2.1.25
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|