doing 2.0.25 → 2.1.0pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardoc/checksums +18 -15
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile.lock +8 -1
- data/README.md +1 -1
- data/Rakefile +23 -4
- data/bin/doing +205 -127
- data/doc/Array.html +354 -1
- data/doc/Doing/Color.html +104 -92
- data/doc/Doing/Completion.html +216 -0
- data/doc/Doing/Configuration.html +340 -5
- data/doc/Doing/Content.html +229 -0
- data/doc/Doing/Errors/DoingNoTraceError.html +1 -1
- data/doc/Doing/Errors/DoingRuntimeError.html +1 -1
- data/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/doc/Doing/Errors/EmptyInput.html +1 -1
- data/doc/Doing/Errors/NoResults.html +1 -1
- data/doc/Doing/Errors/PluginException.html +1 -1
- data/doc/Doing/Errors/UserCancelled.html +1 -1
- data/doc/Doing/Errors/WrongCommand.html +1 -1
- data/doc/Doing/Errors.html +1 -1
- data/doc/Doing/Hooks.html +1 -1
- data/doc/Doing/Item.html +337 -49
- data/doc/Doing/Items.html +444 -35
- data/doc/Doing/LogAdapter.html +139 -51
- data/doc/Doing/Note.html +253 -22
- data/doc/Doing/Pager.html +74 -36
- data/doc/Doing/Plugins.html +1 -1
- data/doc/Doing/Prompt.html +674 -0
- data/doc/Doing/Section.html +354 -0
- data/doc/Doing/Util.html +57 -1
- data/doc/Doing/WWID.html +477 -670
- data/doc/Doing/WWIDFile.html +398 -0
- data/doc/Doing.html +5 -5
- data/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/doc/GLI/Commands.html +1 -1
- data/doc/GLI.html +1 -1
- data/doc/Hash.html +97 -1
- data/doc/Status.html +37 -3
- data/doc/String.html +599 -23
- data/doc/Symbol.html +3 -3
- data/doc/Time.html +1 -1
- data/doc/_index.html +22 -1
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +8 -2
- data/doc/index.html +8 -2
- data/doc/method_list.html +453 -173
- data/doc/top-level-namespace.html +1 -1
- data/doing.gemspec +3 -0
- data/doing.rdoc +40 -12
- data/example_plugin.rb +3 -3
- data/lib/completion/_doing.zsh +1 -1
- data/lib/completion/doing.bash +8 -8
- data/lib/completion/doing.fish +1 -1
- data/lib/doing/array.rb +36 -0
- data/lib/doing/colors.rb +70 -66
- data/lib/doing/completion.rb +6 -0
- data/lib/doing/configuration.rb +69 -28
- data/lib/doing/hash.rb +37 -0
- data/lib/doing/item.rb +77 -12
- data/lib/doing/items.rb +125 -0
- data/lib/doing/log_adapter.rb +55 -3
- data/lib/doing/note.rb +53 -1
- data/lib/doing/pager.rb +49 -38
- data/lib/doing/plugins/export/markdown_export.rb +4 -4
- data/lib/doing/plugins/export/template_export.rb +2 -2
- data/lib/doing/plugins/import/calendar_import.rb +4 -4
- data/lib/doing/plugins/import/doing_import.rb +5 -7
- data/lib/doing/plugins/import/timing_import.rb +3 -3
- data/lib/doing/prompt.rb +206 -0
- data/lib/doing/section.rb +30 -0
- data/lib/doing/string.rb +103 -27
- data/lib/doing/util.rb +14 -6
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +306 -621
- data/lib/doing.rb +6 -2
- data/lib/examples/plugins/capture_thing_import.rb +162 -0
- metadata +73 -5
- data/lib/doing/wwidfile.rb +0 -117
data/bin/doing
CHANGED
@@ -35,8 +35,10 @@ colors = Doing::Color
|
|
35
35
|
wwid = Doing::WWID.new
|
36
36
|
|
37
37
|
Doing.logger.log_level = :info
|
38
|
+
env_log_level = nil
|
38
39
|
|
39
40
|
if ENV['DOING_LOG_LEVEL'] || ENV['DOING_DEBUG'] || ENV['DOING_QUIET'] || ENV['DOING_VERBOSE'] || ENV['DOING_PLUGIN_DEBUG']
|
41
|
+
env_log_level = true
|
40
42
|
# Quiet always wins
|
41
43
|
if ENV['DOING_QUIET'] && ENV['DOING_QUIET'].truthy?
|
42
44
|
Doing.logger.log_level = :error
|
@@ -82,6 +84,12 @@ switch %i[p pager], default_value: settings['paginate']
|
|
82
84
|
desc 'Answer yes/no menus with default option'
|
83
85
|
switch [:default], default_value: false
|
84
86
|
|
87
|
+
desc 'Answer all yes/no menus with yes'
|
88
|
+
switch [:yes], negatable: false
|
89
|
+
|
90
|
+
desc 'Answer all yes/no menus with no'
|
91
|
+
switch [:no], negatable: false
|
92
|
+
|
85
93
|
desc 'Exclude auto tags and default tags'
|
86
94
|
switch %i[x noauto], default_value: false, negatable: false
|
87
95
|
|
@@ -121,9 +129,9 @@ command %i[now next] do |c|
|
|
121
129
|
c.desc "Edit entry with #{Doing::Util.default_editor}"
|
122
130
|
c.switch %i[e editor], negatable: false, default_value: false
|
123
131
|
|
124
|
-
c.desc 'Backdate start time [4pm|20m|2h|yesterday noon]'
|
132
|
+
c.desc 'Backdate start time [4pm|20m|2h|"yesterday noon"]'
|
125
133
|
c.arg_name 'DATE_STRING'
|
126
|
-
c.flag %i[b back]
|
134
|
+
c.flag %i[b back started]
|
127
135
|
|
128
136
|
c.desc 'Timed entry, marks last entry in section as @done'
|
129
137
|
c.switch %i[f finish_last], negatable: false, default_value: false
|
@@ -151,29 +159,34 @@ command %i[now next] do |c|
|
|
151
159
|
options[:section] = settings['current_section']
|
152
160
|
end
|
153
161
|
|
154
|
-
if options[:
|
162
|
+
if options[:editor] || (args.empty? && $stdin.stat.size.zero?)
|
155
163
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
156
164
|
|
157
|
-
input = ''
|
165
|
+
input = date.strftime('%F %R | ')
|
158
166
|
input += args.join(' ') unless args.empty?
|
159
167
|
input = wwid.fork_editor(input).strip
|
160
168
|
|
161
169
|
raise EmptyInput, 'No content' if input.empty?
|
162
170
|
|
163
|
-
title, note = wwid.format_input(input)
|
164
|
-
note.
|
165
|
-
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:
|
171
|
+
date, title, note = wwid.format_input(input)
|
172
|
+
note.add(options[:note]) if options[:note]
|
173
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
166
174
|
wwid.write(wwid.doing_file)
|
167
175
|
elsif args.length.positive?
|
168
|
-
title, note = wwid.format_input(args.join(' '))
|
169
|
-
|
170
|
-
|
176
|
+
d, title, note = wwid.format_input(args.join(' '))
|
177
|
+
date = d.nil? ? date : d
|
178
|
+
note.add(options[:note]) if options[:note]
|
179
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
171
180
|
wwid.write(wwid.doing_file)
|
172
181
|
elsif $stdin.stat.size.positive?
|
173
|
-
input = $stdin.read
|
174
|
-
title, note = wwid.format_input(input)
|
175
|
-
|
176
|
-
|
182
|
+
input = $stdin.read.strip
|
183
|
+
d, title, note = wwid.format_input(input)
|
184
|
+
unless d.nil?
|
185
|
+
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
186
|
+
date = d
|
187
|
+
end
|
188
|
+
note.add(options[:note]) if options[:note]
|
189
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:finish_last] })
|
177
190
|
wwid.write(wwid.doing_file)
|
178
191
|
else
|
179
192
|
raise EmptyInput, 'You must provide content when creating a new entry'
|
@@ -238,14 +251,13 @@ command %i[reset begin] do |c|
|
|
238
251
|
items = wwid.filter_items([], opt: options)
|
239
252
|
|
240
253
|
if options[:interactive]
|
241
|
-
last_entry =
|
254
|
+
last_entry = Doing::Prompt.choose_from_items(items, include_section: options[:section].nil?,
|
242
255
|
menu: true,
|
243
256
|
header: '',
|
244
257
|
prompt: 'Select an entry to start/reset > ',
|
245
258
|
multiple: false,
|
246
259
|
sort: false,
|
247
|
-
show_if_single: true
|
248
|
-
}, include_section: options[:section].nil? )
|
260
|
+
show_if_single: true)
|
249
261
|
else
|
250
262
|
last_entry = items.last
|
251
263
|
end
|
@@ -344,7 +356,7 @@ command :note do |c|
|
|
344
356
|
last_note = last_entry.note || Doing::Note.new
|
345
357
|
new_note = Doing::Note.new
|
346
358
|
|
347
|
-
if options[:
|
359
|
+
if options[:editor] || (args.empty? && $stdin.stat.size.zero? && !options[:remove])
|
348
360
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
349
361
|
|
350
362
|
input = !args.empty? ? args.join(' ') : ''
|
@@ -357,14 +369,14 @@ command :note do |c|
|
|
357
369
|
|
358
370
|
input = prev_input.add(input)
|
359
371
|
|
360
|
-
input = wwid.fork_editor(
|
361
|
-
|
372
|
+
input = wwid.fork_editor(prev_input.strip_lines.join("\n"), message: nil).strip
|
373
|
+
note = input
|
362
374
|
options[:remove] = true
|
363
375
|
new_note.add(note)
|
364
376
|
elsif !args.empty?
|
365
377
|
new_note.add(args.join(' '))
|
366
378
|
elsif $stdin.stat.size.positive?
|
367
|
-
new_note.add($stdin.read)
|
379
|
+
new_note.add($stdin.read.strip)
|
368
380
|
else
|
369
381
|
raise EmptyInput, 'You must provide content when adding a note' unless options[:remove]
|
370
382
|
end
|
@@ -423,31 +435,35 @@ command :meanwhile do |c|
|
|
423
435
|
end
|
424
436
|
input = ''
|
425
437
|
|
426
|
-
if options[:
|
438
|
+
if options[:editor]
|
427
439
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
428
|
-
|
440
|
+
input += date.strftime('%F %R | ')
|
429
441
|
input += args.join(' ') unless args.empty?
|
430
442
|
input = wwid.fork_editor(input).strip
|
431
443
|
elsif !args.empty?
|
432
444
|
input = args.join(' ')
|
433
445
|
elsif $stdin.stat.size.positive?
|
434
|
-
input = $stdin.read
|
446
|
+
input = $stdin.read.strip
|
435
447
|
end
|
436
448
|
|
437
449
|
if input && !input.empty?
|
438
|
-
input, note = wwid.format_input(input)
|
450
|
+
d, input, note = wwid.format_input(input)
|
451
|
+
unless d.nil?
|
452
|
+
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
453
|
+
date = d
|
454
|
+
end
|
439
455
|
else
|
440
456
|
input = nil
|
441
457
|
note = []
|
442
458
|
end
|
443
459
|
|
444
|
-
if options[:
|
445
|
-
note.push(options[:
|
460
|
+
if options[:note]
|
461
|
+
note.push(options[:note])
|
446
462
|
elsif note.empty?
|
447
463
|
note = nil
|
448
464
|
end
|
449
465
|
|
450
|
-
wwid.stop_start('meanwhile', { new_item: input, back: date, section: section, archive: options[:
|
466
|
+
wwid.stop_start('meanwhile', { new_item: input, back: date, section: section, archive: options[:archive], note: note })
|
451
467
|
wwid.write(wwid.doing_file)
|
452
468
|
end
|
453
469
|
end
|
@@ -465,11 +481,11 @@ command :template do |c|
|
|
465
481
|
c.switch %i[l list], negatable: false
|
466
482
|
|
467
483
|
c.desc 'List in single column for completion'
|
468
|
-
c.switch %i[c]
|
484
|
+
c.switch %i[c column]
|
469
485
|
|
470
486
|
c.action do |_global_options, options, args|
|
471
|
-
if options[:list] || options[:
|
472
|
-
if options[:
|
487
|
+
if options[:list] || options[:column]
|
488
|
+
if options[:column]
|
473
489
|
$stdout.print Doing::Plugins.plugin_templates.join("\n")
|
474
490
|
else
|
475
491
|
$stdout.puts "Available templates: #{Doing::Plugins.plugin_templates.join(', ')}"
|
@@ -478,7 +494,7 @@ command :template do |c|
|
|
478
494
|
end
|
479
495
|
|
480
496
|
if args.empty?
|
481
|
-
type =
|
497
|
+
type = Doing::Prompt.choose_from(Doing::Plugins.plugin_templates, sorted: false, prompt: 'Select template type > ')
|
482
498
|
else
|
483
499
|
type = args[0]
|
484
500
|
end
|
@@ -613,22 +629,29 @@ command :later do |c|
|
|
613
629
|
if options[:editor] || (args.empty? && $stdin.stat.size.zero?)
|
614
630
|
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
615
631
|
|
616
|
-
input
|
632
|
+
input += date.strftime('%F %R | ')
|
633
|
+
input += args.empty? ? '' : args.join(' ')
|
617
634
|
input = wwid.fork_editor(input).strip
|
618
635
|
raise EmptyInput, 'No content' unless input && !input.empty?
|
619
636
|
|
620
|
-
title, note = wwid.format_input(input)
|
621
|
-
|
637
|
+
d, title, note = wwid.format_input(input)
|
638
|
+
date = d.nil? ? date : d
|
639
|
+
note.add(options[:note]) if options[:note]
|
622
640
|
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
623
641
|
wwid.write(wwid.doing_file)
|
624
642
|
elsif !args.empty?
|
625
|
-
title, note = wwid.format_input(args.join(' '))
|
626
|
-
|
643
|
+
d, title, note = wwid.format_input(args.join(' '))
|
644
|
+
date = d.nil? ? date : d
|
645
|
+
note.add(options[:note]) if options[:note]
|
627
646
|
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
628
647
|
wwid.write(wwid.doing_file)
|
629
648
|
elsif $stdin.stat.size.positive?
|
630
|
-
title, note = wwid.format_input($stdin.read)
|
631
|
-
|
649
|
+
d, title, note = wwid.format_input($stdin.read)
|
650
|
+
unless d.nil?
|
651
|
+
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
652
|
+
date = d
|
653
|
+
end
|
654
|
+
note.add(options[:note]) if options[:note]
|
632
655
|
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
633
656
|
wwid.write(wwid.doing_file)
|
634
657
|
else
|
@@ -659,9 +682,9 @@ command %i[done did] do |c|
|
|
659
682
|
c.arg_name 'DATE_STRING'
|
660
683
|
c.flag [:at]
|
661
684
|
|
662
|
-
c.desc 'Backdate start date by interval [4pm|20m|2h|yesterday noon]'
|
685
|
+
c.desc 'Backdate start date by interval or set to time [4pm|20m|2h|"yesterday noon"]'
|
663
686
|
c.arg_name 'DATE_STRING'
|
664
|
-
c.flag %i[b back]
|
687
|
+
c.flag %i[b back started]
|
665
688
|
|
666
689
|
c.desc %(Set completion date to start date plus interval (XX[mhd] or HH:MM).
|
667
690
|
If used without the --back option, the start date will be moved back to allow
|
@@ -710,8 +733,6 @@ command %i[done did] do |c|
|
|
710
733
|
date = options[:took] ? finish_date - took : finish_date
|
711
734
|
elsif options[:took]
|
712
735
|
finish_date = date + took
|
713
|
-
elsif options[:back]
|
714
|
-
finish_date = date
|
715
736
|
else
|
716
737
|
finish_date = Time.now
|
717
738
|
end
|
@@ -743,16 +764,17 @@ command %i[done did] do |c|
|
|
743
764
|
|
744
765
|
old_entry = last_entry.dup
|
745
766
|
last_entry.note.add(note)
|
746
|
-
input = [last_entry.title, last_entry.note.
|
767
|
+
input = ["#{last_entry.date.strftime('%F %R | ')}#{last_entry.title}", last_entry.note.strip_lines.join("\n")].join("\n")
|
747
768
|
else
|
748
769
|
is_new = true
|
749
|
-
input = [args.join(' '), note.
|
770
|
+
input = ["#{date.strftime('%F %R | ')}#{args.join(' ')}", note.strip_lines.join("\n")].join("\n")
|
750
771
|
end
|
751
772
|
|
752
773
|
input = wwid.fork_editor(input).strip
|
753
774
|
raise EmptyInput, 'No content' unless input && !input.empty?
|
754
775
|
|
755
|
-
title, note = wwid.format_input(input)
|
776
|
+
d, title, note = wwid.format_input(input)
|
777
|
+
date = d.nil? ? date : d
|
756
778
|
new_entry = Doing::Item.new(date, title, section, note)
|
757
779
|
if new_entry.should_finish?
|
758
780
|
if new_entry.should_time?
|
@@ -763,23 +785,23 @@ command %i[done did] do |c|
|
|
763
785
|
end
|
764
786
|
|
765
787
|
if (is_new)
|
766
|
-
wwid.content
|
788
|
+
wwid.content.push(new_entry)
|
767
789
|
else
|
768
|
-
wwid.update_item(old_entry, new_entry)
|
790
|
+
wwid.content.update_item(old_entry, new_entry)
|
769
791
|
end
|
770
792
|
|
771
|
-
if options[:
|
793
|
+
if options[:archive]
|
772
794
|
wwid.move_item(new_entry, 'Archive', label: true)
|
773
795
|
end
|
774
796
|
|
775
797
|
wwid.write(wwid.doing_file)
|
776
798
|
elsif args.empty? && $stdin.stat.size.zero?
|
777
|
-
if options[:
|
799
|
+
if options[:remove]
|
778
800
|
wwid.tag_last({ tags: ['done'], count: 1, section: section, remove: true })
|
779
801
|
else
|
780
802
|
note = options[:note] ? Doing::Note.new(options[:note]) : nil
|
781
803
|
opt = {
|
782
|
-
archive: options[:
|
804
|
+
archive: options[:archive],
|
783
805
|
back: finish_date,
|
784
806
|
count: 1,
|
785
807
|
date: options[:date],
|
@@ -793,9 +815,10 @@ command %i[done did] do |c|
|
|
793
815
|
end
|
794
816
|
elsif !args.empty?
|
795
817
|
note = Doing::Note.new(options[:note])
|
796
|
-
title, new_note = wwid.format_input([args.join(' '), note.
|
818
|
+
d, title, new_note = wwid.format_input([args.join(' '), note.strip_lines.join("\n")].join("\n"))
|
819
|
+
date = d.nil? ? date : d
|
797
820
|
title.chomp!
|
798
|
-
section = 'Archive' if options[:
|
821
|
+
section = 'Archive' if options[:archive]
|
799
822
|
new_entry = Doing::Item.new(date, title, section, new_note)
|
800
823
|
if new_entry.should_finish?
|
801
824
|
if new_entry.should_time?
|
@@ -804,13 +827,17 @@ command %i[done did] do |c|
|
|
804
827
|
new_entry.tag('done')
|
805
828
|
end
|
806
829
|
end
|
807
|
-
wwid.content
|
830
|
+
wwid.content.push(new_entry)
|
808
831
|
wwid.write(wwid.doing_file)
|
809
832
|
Doing.logger.info('Entry Added:', new_entry.title)
|
810
833
|
elsif $stdin.stat.size.positive?
|
811
|
-
title, note = wwid.format_input($stdin.read)
|
834
|
+
d, title, note = wwid.format_input($stdin.read.strip)
|
835
|
+
unless d.nil?
|
836
|
+
Doing.logger.debug('Parser:', 'Date detected in input, overriding command line values')
|
837
|
+
date = d
|
838
|
+
end
|
812
839
|
note.add(options[:note]) if options[:note]
|
813
|
-
section = options[:
|
840
|
+
section = options[:archive] ? 'Archive' : section
|
814
841
|
new_entry = Doing::Item.new(date, title, section, note)
|
815
842
|
|
816
843
|
if new_entry.should_finish?
|
@@ -821,7 +848,7 @@ command %i[done did] do |c|
|
|
821
848
|
end
|
822
849
|
end
|
823
850
|
|
824
|
-
wwid.content
|
851
|
+
wwid.content.push(new_entry)
|
825
852
|
wwid.write(wwid.doing_file)
|
826
853
|
Doing.logger.info('Entry Added:', new_entry.title)
|
827
854
|
else
|
@@ -907,7 +934,7 @@ command :cancel do |c|
|
|
907
934
|
end
|
908
935
|
|
909
936
|
opts = {
|
910
|
-
archive: options[:
|
937
|
+
archive: options[:archive],
|
911
938
|
case: options[:case].normalize_case,
|
912
939
|
count: count,
|
913
940
|
date: false,
|
@@ -1279,15 +1306,15 @@ command :tag do |c|
|
|
1279
1306
|
end
|
1280
1307
|
|
1281
1308
|
|
1282
|
-
question = if options[:
|
1309
|
+
question = if options[:aarchive]
|
1283
1310
|
"Are you sure you want to autotag all records#{section_q}"
|
1284
|
-
elsif options[:
|
1311
|
+
elsif options[:remove]
|
1285
1312
|
"Are you sure you want to remove #{tags.join(' and ')} from all records#{section_q}"
|
1286
1313
|
else
|
1287
1314
|
"Are you sure you want to add #{tags.join(' and ')} to all records#{section_q}"
|
1288
1315
|
end
|
1289
1316
|
|
1290
|
-
res =
|
1317
|
+
res = Doing::Prompt.yn(question, default_response: false)
|
1291
1318
|
|
1292
1319
|
raise UserCancelled unless res
|
1293
1320
|
end
|
@@ -1408,7 +1435,7 @@ command [:mark, :flag] do |c|
|
|
1408
1435
|
"Are you sure you want to flag all records#{section_q}"
|
1409
1436
|
end
|
1410
1437
|
|
1411
|
-
res =
|
1438
|
+
res = Doing::Prompt.yn(question, default_response: false)
|
1412
1439
|
|
1413
1440
|
exit_now! 'Cancelled' unless res
|
1414
1441
|
end
|
@@ -1598,7 +1625,6 @@ command :show do |c|
|
|
1598
1625
|
end
|
1599
1626
|
|
1600
1627
|
opt = options.dup
|
1601
|
-
|
1602
1628
|
opt[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1603
1629
|
opt[:count] = options[:count].to_i
|
1604
1630
|
opt[:date_filter] = dates
|
@@ -1729,7 +1755,7 @@ command :recent do |c|
|
|
1729
1755
|
c.switch %i[i interactive], negatable: false, default_value: false
|
1730
1756
|
|
1731
1757
|
c.action do |global_options, options, args|
|
1732
|
-
section = wwid.guess_section(options[:
|
1758
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
1733
1759
|
|
1734
1760
|
unless global_options[:version]
|
1735
1761
|
if settings['templates']['recent'].key?('count')
|
@@ -1744,7 +1770,7 @@ command :recent do |c|
|
|
1744
1770
|
count = args.empty? ? config_count : args[0].to_i
|
1745
1771
|
end
|
1746
1772
|
|
1747
|
-
options[:
|
1773
|
+
options[:times] = true if options[:totals]
|
1748
1774
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1749
1775
|
|
1750
1776
|
template = settings['templates']['recent'].deep_merge(settings['templates']['default'])
|
@@ -1753,7 +1779,7 @@ command :recent do |c|
|
|
1753
1779
|
opts = {
|
1754
1780
|
sort_tags: options[:sort_tags],
|
1755
1781
|
tags_color: tags_color,
|
1756
|
-
times: options[:
|
1782
|
+
times: options[:times],
|
1757
1783
|
totals: options[:totals],
|
1758
1784
|
interactive: options[:interactive]
|
1759
1785
|
}
|
@@ -1802,7 +1828,7 @@ command :today do |c|
|
|
1802
1828
|
c.action do |_global_options, options, _args|
|
1803
1829
|
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
1804
1830
|
|
1805
|
-
options[:
|
1831
|
+
options[:times] = true if options[:totals]
|
1806
1832
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1807
1833
|
opt = {
|
1808
1834
|
after: options[:after],
|
@@ -1863,14 +1889,14 @@ command :on do |c|
|
|
1863
1889
|
|
1864
1890
|
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
1865
1891
|
|
1866
|
-
message = "
|
1892
|
+
message = "date interpreted as #{start}"
|
1867
1893
|
message += " to #{finish}" if finish
|
1868
|
-
Doing.logger.debug(message)
|
1894
|
+
Doing.logger.debug('Interpreter:', message)
|
1869
1895
|
|
1870
|
-
options[:
|
1896
|
+
options[:times] = true if options[:totals]
|
1871
1897
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1872
1898
|
|
1873
|
-
Doing::Pager.page wwid.list_date([start, finish], options[:
|
1899
|
+
Doing::Pager.page wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
1874
1900
|
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
1875
1901
|
end
|
1876
1902
|
end
|
@@ -1918,12 +1944,12 @@ command :since do |c|
|
|
1918
1944
|
|
1919
1945
|
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
1920
1946
|
|
1921
|
-
Doing.logger.debug("
|
1947
|
+
Doing.logger.debug('Interpreter:', "date interpreted as #{start} through the current time")
|
1922
1948
|
|
1923
|
-
options[:
|
1949
|
+
options[:times] = true if options[:totals]
|
1924
1950
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1925
1951
|
|
1926
|
-
Doing::Pager.page wwid.list_date([start, finish], options[:
|
1952
|
+
Doing::Pager.page wwid.list_date([start, finish], options[:section], options[:times], options[:output],
|
1927
1953
|
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
1928
1954
|
end
|
1929
1955
|
end
|
@@ -2053,9 +2079,9 @@ command :last do |c|
|
|
2053
2079
|
end
|
2054
2080
|
|
2055
2081
|
if options[:editor]
|
2056
|
-
wwid.edit_last(section: options[:
|
2082
|
+
wwid.edit_last(section: options[:section], options: { search: search, fuzzy: options[:fuzzy], case: options[:case], tag: tags, tag_bool: options[:bool], not: options[:not] })
|
2057
2083
|
else
|
2058
|
-
Doing::Pager::page wwid.last(times: true, section: options[:
|
2084
|
+
Doing::Pager::page wwid.last(times: true, section: options[:section],
|
2059
2085
|
options: { search: search, fuzzy: options[:fuzzy], case: options[:case], negate: options[:not], tag: tags, tag_bool: options[:bool] }).strip
|
2060
2086
|
end
|
2061
2087
|
end
|
@@ -2067,8 +2093,8 @@ command :sections do |c|
|
|
2067
2093
|
c.switch %i[c column], negatable: false, default_value: false
|
2068
2094
|
|
2069
2095
|
c.action do |_global_options, options, _args|
|
2070
|
-
joiner = options[:
|
2071
|
-
print wwid.
|
2096
|
+
joiner = options[:column] ? "\n" : "\t"
|
2097
|
+
print wwid.content.section_titles.join(joiner)
|
2072
2098
|
end
|
2073
2099
|
end
|
2074
2100
|
|
@@ -2089,7 +2115,7 @@ command :add_section do |c|
|
|
2089
2115
|
c.action do |_global_options, _options, args|
|
2090
2116
|
raise InvalidArgument, "Section #{args[0]} already exists" if wwid.sections.include?(args[0])
|
2091
2117
|
|
2092
|
-
wwid.add_section(args.join(' ').cap_first)
|
2118
|
+
wwid.content.add_section(args.join(' ').cap_first, log: true)
|
2093
2119
|
wwid.write(wwid.doing_file)
|
2094
2120
|
end
|
2095
2121
|
end
|
@@ -2247,6 +2273,7 @@ command :view do |c|
|
|
2247
2273
|
rescue WrongCommand => exception
|
2248
2274
|
cmd = commands[:show]
|
2249
2275
|
options[:sort] = 'asc'
|
2276
|
+
options[:tag_order] = 'asc'
|
2250
2277
|
action = cmd.send(:get_action, nil)
|
2251
2278
|
return action.call(global_options, options, args)
|
2252
2279
|
end
|
@@ -2289,12 +2316,9 @@ command :view do |c|
|
|
2289
2316
|
# If the -o/--output flag was specified, override any default in the view template
|
2290
2317
|
options[:output] ||= view.key?('output_format') ? view['output_format'] : 'template'
|
2291
2318
|
|
2292
|
-
count =
|
2293
|
-
|
2294
|
-
|
2295
|
-
view.key?('count') ? view['count'] : 10
|
2296
|
-
end
|
2297
|
-
section = if options[:s]
|
2319
|
+
count = options[:count] ? options[:count] : view.key?('count') ? view['count'] : 10
|
2320
|
+
|
2321
|
+
section = if options[:section]
|
2298
2322
|
section
|
2299
2323
|
else
|
2300
2324
|
view.key?('section') ? view['section'] : settings['current_section']
|
@@ -2312,7 +2336,7 @@ command :view do |c|
|
|
2312
2336
|
view.key?('tag_order') ? view['tag_order'].normalize_order : 'asc'
|
2313
2337
|
end
|
2314
2338
|
|
2315
|
-
options[:
|
2339
|
+
options[:times] = true if totals
|
2316
2340
|
output_format = options[:output]&.downcase || 'template'
|
2317
2341
|
|
2318
2342
|
options[:sort_tags] = if options[:tag_sort]
|
@@ -2386,7 +2410,7 @@ command :views do |c|
|
|
2386
2410
|
c.switch %i[c column], default_value: false
|
2387
2411
|
|
2388
2412
|
c.action do |_global_options, options, _args|
|
2389
|
-
joiner = options[:
|
2413
|
+
joiner = options[:column] ? "\n" : "\t"
|
2390
2414
|
print wwid.views.join(joiner)
|
2391
2415
|
end
|
2392
2416
|
end
|
@@ -2552,6 +2576,10 @@ end
|
|
2552
2576
|
desc 'Open the "doing" file in an editor'
|
2553
2577
|
long_desc "`doing open` defaults to using the editor_app setting in #{config.config_file} (#{settings.key?('editor_app') ? settings['editor_app'] : 'not set'})."
|
2554
2578
|
command :open do |c|
|
2579
|
+
c.desc 'Open with editor command (e.g. vim, mate)'
|
2580
|
+
c.arg_name 'COMMAND'
|
2581
|
+
c.flag %i[e editor]
|
2582
|
+
|
2555
2583
|
if `uname` =~ /Darwin/
|
2556
2584
|
c.desc 'Open with app name'
|
2557
2585
|
c.arg_name 'APP_NAME'
|
@@ -2567,11 +2595,17 @@ command :open do |c|
|
|
2567
2595
|
params.delete_if do |k, v|
|
2568
2596
|
k.instance_of?(String) || v.nil? || v == false
|
2569
2597
|
end
|
2570
|
-
|
2598
|
+
|
2599
|
+
if options[:editor]
|
2600
|
+
raise MissingEditor, "Editor #{options[:editor]} not found" unless Doing::Util.exec_available(options[:editor])
|
2601
|
+
|
2602
|
+
editor = TTY::Which.which(options[:editor])
|
2603
|
+
system %(#{editor} "#{File.expand_path(wwid.doing_file)}")
|
2604
|
+
elsif `uname` =~ /Darwin/
|
2571
2605
|
if options[:app]
|
2572
|
-
system %(open -a "#{options[:
|
2606
|
+
system %(open -a "#{options[:app]}" "#{File.expand_path(wwid.doing_file)}")
|
2573
2607
|
elsif options[:bundle_id]
|
2574
|
-
system %(open -b "#{options[:
|
2608
|
+
system %(open -b "#{options[:bundle_id]}" "#{File.expand_path(wwid.doing_file)}")
|
2575
2609
|
elsif Doing::Util.find_default_editor('doing_file')
|
2576
2610
|
editor = Doing::Util.find_default_editor('doing_file')
|
2577
2611
|
if Doing::Util.exec_available(editor)
|
@@ -2615,6 +2649,16 @@ command :config do |c|
|
|
2615
2649
|
c.desc 'DEPRECATED'
|
2616
2650
|
c.switch %i[u update]
|
2617
2651
|
|
2652
|
+
c.desc 'List configuration paths, including .doingrc files in the current and parent directories'
|
2653
|
+
c.long_desc 'Config files are listed in order of precedence (if there are multiple configs detected).
|
2654
|
+
Values defined in the top item in the list will override values in configutations below it.'
|
2655
|
+
c.command :list do |list|
|
2656
|
+
list.action do |global, options, args|
|
2657
|
+
puts config.additional_configs.join("\n")
|
2658
|
+
puts config.config_file
|
2659
|
+
end
|
2660
|
+
end
|
2661
|
+
|
2618
2662
|
c.desc 'Open config file in editor'
|
2619
2663
|
c.command :edit do |edit|
|
2620
2664
|
edit.example 'doing config edit', desc: 'Open a config file in the default editor'
|
@@ -2627,14 +2671,14 @@ command :config do |c|
|
|
2627
2671
|
if `uname` =~ /Darwin/
|
2628
2672
|
edit.desc 'Application to use'
|
2629
2673
|
edit.arg_name 'APP_NAME'
|
2630
|
-
edit.flag [
|
2674
|
+
edit.flag %i[a app]
|
2631
2675
|
|
2632
2676
|
edit.desc 'Application bundle id to use'
|
2633
2677
|
edit.arg_name 'BUNDLE_ID'
|
2634
|
-
edit.flag [
|
2678
|
+
edit.flag %i[b bundle_id]
|
2635
2679
|
|
2636
2680
|
edit.desc "Use the config_editor_app defined in ~/.config/doing/config.yml (#{settings.key?('config_editor_app') ? settings['config_editor_app'] : 'config_editor_app not set'})"
|
2637
|
-
edit.switch [
|
2681
|
+
edit.switch %i[x default]
|
2638
2682
|
end
|
2639
2683
|
|
2640
2684
|
edit.action do |global, options, args|
|
@@ -2656,7 +2700,7 @@ command :config do |c|
|
|
2656
2700
|
config_file = config.choose_config
|
2657
2701
|
|
2658
2702
|
if `uname` =~ /Darwin/
|
2659
|
-
if options[:
|
2703
|
+
if options[:default]
|
2660
2704
|
editor = Doing::Util.find_default_editor('config')
|
2661
2705
|
if editor
|
2662
2706
|
if Doing::Util.exec_available(editor)
|
@@ -2667,14 +2711,14 @@ command :config do |c|
|
|
2667
2711
|
else
|
2668
2712
|
raise InvalidArgument, 'No viable editor found in config or environment.'
|
2669
2713
|
end
|
2670
|
-
elsif options[:
|
2671
|
-
if options[:
|
2672
|
-
`open -a "#{options[:
|
2673
|
-
elsif options[:
|
2674
|
-
`open -b #{options[:
|
2714
|
+
elsif options[:app] || options[:bundle_id]
|
2715
|
+
if options[:app]
|
2716
|
+
`open -a "#{options[:app]}" "#{config_file}"`
|
2717
|
+
elsif options[:bundle_id]
|
2718
|
+
`open -b #{options[:bundle_id]} "#{config_file}"`
|
2675
2719
|
end
|
2676
2720
|
else
|
2677
|
-
editor = options[:
|
2721
|
+
editor = options[:editor] || Doing::Util.find_default_editor('config')
|
2678
2722
|
|
2679
2723
|
raise MissingEditor, 'No viable editor defined in config or environment' unless editor
|
2680
2724
|
|
@@ -2685,7 +2729,7 @@ command :config do |c|
|
|
2685
2729
|
end
|
2686
2730
|
end
|
2687
2731
|
else
|
2688
|
-
editor = options[:
|
2732
|
+
editor = options[:editor] || Doing::Util.default_editor
|
2689
2733
|
raise MissingEditor, 'No EDITOR variable defined in environment' unless editor && Doing::Util.exec_available(editor)
|
2690
2734
|
|
2691
2735
|
system %(#{editor} "#{config_file}")
|
@@ -2725,16 +2769,33 @@ command :config do |c|
|
|
2725
2769
|
|
2726
2770
|
keypath = args.join('.')
|
2727
2771
|
cfg = config.value_for_key(keypath)
|
2772
|
+
real_path = config.resolve_key_path(keypath)
|
2728
2773
|
|
2729
2774
|
if cfg
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
2733
|
-
|
2734
|
-
|
2735
|
-
|
2736
|
-
|
2737
|
-
|
2775
|
+
val = cfg.map {|k, v| v }[0]
|
2776
|
+
if real_path.count.positive?
|
2777
|
+
nested_cfg = {}
|
2778
|
+
nested_cfg.deep_set(real_path, val)
|
2779
|
+
else
|
2780
|
+
nested_cfg = val
|
2781
|
+
end
|
2782
|
+
|
2783
|
+
if options[:output] =~ /^r/
|
2784
|
+
if val.is_a?(Hash)
|
2785
|
+
$stdout.puts YAML.dump(val)
|
2786
|
+
elsif val.is_a?(Array)
|
2787
|
+
$stdout.puts val.join(', ')
|
2788
|
+
else
|
2789
|
+
$stdout.puts val.to_s
|
2790
|
+
end
|
2791
|
+
else
|
2792
|
+
$stdout.puts case options[:output]
|
2793
|
+
when /^j/
|
2794
|
+
JSON.pretty_generate(val)
|
2795
|
+
else
|
2796
|
+
YAML.dump(nested_cfg)
|
2797
|
+
end
|
2798
|
+
end
|
2738
2799
|
else
|
2739
2800
|
Doing.logger.log_now(:error, 'Config:', "Key #{keypath} not found")
|
2740
2801
|
end
|
@@ -2749,46 +2810,55 @@ command :config do |c|
|
|
2749
2810
|
set.example 'doing config set timer_format human', desc: 'Set the value of timer_format to "human"'
|
2750
2811
|
set.example 'doing config set plug.plugpath ~/my_plugins', desc: 'Key path is fuzzy matched: set the value of plugins->plugin_path'
|
2751
2812
|
|
2813
|
+
set.desc 'Delete specified key'
|
2814
|
+
set.switch %i[r remove], default_value: false, negatable: false
|
2815
|
+
|
2752
2816
|
set.action do |_global, options, args|
|
2753
|
-
if args.count < 2
|
2817
|
+
if args.count < 2 && !options[:remove]
|
2754
2818
|
raise InvalidArgument, 'config set requires at least two arguments, key path and value'
|
2755
2819
|
|
2756
2820
|
end
|
2757
2821
|
|
2758
|
-
value = args.pop
|
2822
|
+
value = options[:remove] ? nil : args.pop
|
2759
2823
|
keypath = args.join('.')
|
2760
2824
|
old_value = config.value_for_key(keypath).map { |k, v| v.to_s }
|
2761
2825
|
real_path = config.resolve_key_path(keypath)
|
2762
2826
|
raise InvalidArgument, 'Invalid key path' if real_path.empty?
|
2763
2827
|
|
2764
|
-
key = real_path.last
|
2765
|
-
real_path[0...-1].inject(config.settings, :fetch)[key] = value
|
2766
|
-
|
2767
2828
|
config_file = config.choose_config
|
2829
|
+
cfg = YAML.safe_load_file(config_file) || {}
|
2768
2830
|
|
2769
|
-
|
2770
|
-
|
2771
|
-
|
2772
|
-
|
2773
|
-
|
2831
|
+
$stderr.puts "Updating #{config_file}".yellow
|
2832
|
+
|
2833
|
+
if options[:remove]
|
2834
|
+
cfg.deep_set(real_path, nil)
|
2835
|
+
$stderr.puts "#{'Deleting key:'.yellow} #{real_path.join('->').boldwhite}"
|
2836
|
+
else
|
2837
|
+
old_value = cfg.dig(*real_path) || 'empty'
|
2838
|
+
cfg.deep_set(real_path, value.set_type)
|
2839
|
+
$stderr.puts "#{'Key path:'.yellow} #{real_path.join('->').boldwhite}"
|
2840
|
+
$stderr.puts "#{'Previous:'.yellow} #{old_value.to_s.boldwhite}"
|
2841
|
+
$stderr.puts "#{' New:'.yellow} #{value.set_type.to_s.boldwhite}"
|
2842
|
+
end
|
2843
|
+
|
2844
|
+
res = Doing::Prompt.yn('Update selected config', default_response: true)
|
2774
2845
|
|
2775
2846
|
raise UserCancelled, 'Cancelled' unless res
|
2776
2847
|
|
2777
|
-
Doing.
|
2778
|
-
Doing::Util.write_to_file(config_file, YAML.dump(config.settings), backup: true)
|
2848
|
+
Doing::Util.write_to_file(config_file, YAML.dump(cfg), backup: true)
|
2779
2849
|
Doing.logger.warn('Config:', "#{config_file} updated")
|
2780
2850
|
end
|
2781
2851
|
end
|
2782
2852
|
end
|
2783
2853
|
|
2784
|
-
desc 'Undo the last change to the
|
2854
|
+
desc 'Undo the last change to the Doing file'
|
2785
2855
|
command :undo do |c|
|
2786
2856
|
c.desc 'Specify alternate doing file'
|
2787
2857
|
c.arg_name 'PATH'
|
2788
2858
|
c.flag %i[f file], default_value: wwid.doing_file
|
2789
2859
|
|
2790
2860
|
c.action do |_global_options, options, _args|
|
2791
|
-
file = options[:
|
2861
|
+
file = options[:file] || wwid.doing_file
|
2792
2862
|
wwid.restore_backup(file)
|
2793
2863
|
end
|
2794
2864
|
end
|
@@ -2895,9 +2965,9 @@ pre do |global, _command, _options, _args|
|
|
2895
2965
|
|
2896
2966
|
$stdout.puts "doing v#{Doing::VERSION}" if global[:version]
|
2897
2967
|
unless STDOUT.isatty
|
2898
|
-
Doing::Color
|
2968
|
+
Doing::Color.coloring = global[:pager] ? global[:color] : false
|
2899
2969
|
else
|
2900
|
-
Doing::Color
|
2970
|
+
Doing::Color.coloring = global[:color]
|
2901
2971
|
end
|
2902
2972
|
|
2903
2973
|
# Return true to proceed; false to abort and not call the
|
@@ -2925,13 +2995,21 @@ end
|
|
2925
2995
|
|
2926
2996
|
around do |global, command, options, arguments, code|
|
2927
2997
|
# Doing.logger.debug('Pager:', "Global: #{global[:pager]}, Config: #{settings['paginate']}, Pager: #{Doing::Pager.paginate}")
|
2928
|
-
|
2998
|
+
if env_log_level.nil?
|
2999
|
+
Doing.logger.adjust_verbosity(global)
|
3000
|
+
end
|
2929
3001
|
|
2930
3002
|
if global[:stdout]
|
2931
3003
|
Doing.logger.logdev = $stdout
|
2932
3004
|
end
|
2933
3005
|
|
2934
|
-
|
3006
|
+
if global[:yes]
|
3007
|
+
Doing::Prompt.force_answer = true
|
3008
|
+
elsif global[:no]
|
3009
|
+
Doing::Prompt.force_answer = false
|
3010
|
+
else
|
3011
|
+
Doing::Prompt.default_answer = global[:default]
|
3012
|
+
end
|
2935
3013
|
|
2936
3014
|
if global[:config_file] && global[:config_file] != config.config_file
|
2937
3015
|
Doing.logger.warn(format('%sWARNING:%s %sThe use of --config_file is deprecated, please set the environment variable DOING_CONFIG instead.', colors.flamingo, colors.default, colors.boldred))
|