doing 2.0.3.pre → 2.0.8.pre
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/CHANGELOG.md +40 -1
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/bin/doing +316 -114
- data/doing.rdoc +244 -19
- data/example_plugin.rb +1 -1
- data/generate_completions.sh +1 -0
- data/lib/completion/_doing.zsh +179 -127
- data/lib/completion/doing.bash +60 -27
- data/lib/completion/doing.fish +74 -23
- data/lib/doing/cli_status.rb +4 -0
- data/lib/doing/configuration.rb +2 -0
- data/lib/doing/errors.rb +22 -15
- data/lib/doing/item.rb +12 -11
- data/lib/doing/log_adapter.rb +27 -25
- data/lib/doing/plugin_manager.rb +1 -1
- data/lib/doing/plugins/export/json_export.rb +2 -2
- data/lib/doing/plugins/export/template_export.rb +1 -1
- data/lib/doing/plugins/import/calendar_import.rb +7 -1
- data/lib/doing/plugins/import/doing_import.rb +6 -6
- data/lib/doing/plugins/import/timing_import.rb +7 -1
- data/lib/doing/string.rb +9 -7
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +160 -92
- data/lib/examples/commands/autotag.rb +63 -0
- data/lib/examples/commands/wiki.rb +1 -0
- data/lib/examples/plugins/say_export.rb +1 -1
- data/lib/examples/plugins/{templates → wiki_export/templates}/wiki.css +0 -0
- data/lib/examples/plugins/{templates → wiki_export/templates}/wiki.haml +0 -0
- data/lib/examples/plugins/{templates → wiki_export/templates}/wiki_index.haml +0 -0
- data/lib/examples/plugins/{wiki_export.rb → wiki_export/wiki_export.rb} +0 -0
- data/scripts/generate_bash_completions.rb +3 -2
- data/scripts/generate_fish_completions.rb +4 -1
- data/scripts/generate_zsh_completions.rb +44 -39
- metadata +7 -7
- data/doing.fish +0 -278
data/bin/doing
CHANGED
@@ -57,8 +57,8 @@ config = Doing.config
|
|
57
57
|
settings = config.settings
|
58
58
|
wwid.config = settings
|
59
59
|
|
60
|
-
if
|
61
|
-
commands_from File.expand_path(
|
60
|
+
if settings.dig('plugins', 'command_path')
|
61
|
+
commands_from File.expand_path(settings.dig('plugins', 'command_path'))
|
62
62
|
end
|
63
63
|
|
64
64
|
program_desc 'A CLI for a What Was I Doing system'
|
@@ -137,7 +137,7 @@ command %i[now next] do |c|
|
|
137
137
|
if options[:back]
|
138
138
|
date = wwid.chronify(options[:back], guess: :begin)
|
139
139
|
|
140
|
-
raise
|
140
|
+
raise InvalidTimeExpression.new('unable to parse date string', topic: 'Date parser:') if date.nil?
|
141
141
|
else
|
142
142
|
date = Time.now
|
143
143
|
end
|
@@ -149,13 +149,13 @@ command %i[now next] do |c|
|
|
149
149
|
end
|
150
150
|
|
151
151
|
if options[:e] || (args.empty? && $stdin.stat.size.zero?)
|
152
|
-
raise
|
152
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
153
153
|
|
154
154
|
input = ''
|
155
155
|
input += args.join(' ') unless args.empty?
|
156
156
|
input = wwid.fork_editor(input).strip
|
157
157
|
|
158
|
-
raise
|
158
|
+
raise EmptyInput, 'No content' if input.empty?
|
159
159
|
|
160
160
|
title, note = wwid.format_input(input)
|
161
161
|
note.push(options[:n]) if options[:n]
|
@@ -173,14 +173,14 @@ command %i[now next] do |c|
|
|
173
173
|
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
174
174
|
wwid.write(wwid.doing_file)
|
175
175
|
else
|
176
|
-
raise
|
176
|
+
raise EmptyInput, 'You must provide content when creating a new entry'
|
177
177
|
end
|
178
178
|
end
|
179
179
|
end
|
180
180
|
|
181
181
|
desc 'Reset the start time of an entry'
|
182
182
|
command %i[reset begin] do |c|
|
183
|
-
c.desc '
|
183
|
+
c.desc 'Limit search to section'
|
184
184
|
c.arg_name 'NAME'
|
185
185
|
c.flag %i[s section], default_value: 'All'
|
186
186
|
|
@@ -195,12 +195,18 @@ command %i[reset begin] do |c|
|
|
195
195
|
c.arg_name 'QUERY'
|
196
196
|
c.flag [:search]
|
197
197
|
|
198
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
199
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
200
|
+
|
201
|
+
c.desc 'Reset items that *don\'t* match search/tag filters'
|
202
|
+
c.switch [:not], default_value: false, negatable: false
|
203
|
+
|
198
204
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
199
205
|
c.arg_name 'BOOLEAN'
|
200
206
|
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
|
201
207
|
|
202
208
|
c.desc 'Select from a menu of matching entries'
|
203
|
-
c.switch %i[i interactive]
|
209
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
204
210
|
|
205
211
|
c.action do |global_options, options, args|
|
206
212
|
if options[:section]
|
@@ -209,6 +215,13 @@ command %i[reset begin] do |c|
|
|
209
215
|
|
210
216
|
options[:tag_bool] = options[:bool].normalize_bool
|
211
217
|
|
218
|
+
if options[:search]
|
219
|
+
search = options[:search]
|
220
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
221
|
+
options[:search] = search
|
222
|
+
end
|
223
|
+
|
224
|
+
|
212
225
|
items = wwid.filter_items([], opt: options)
|
213
226
|
|
214
227
|
if options[:interactive]
|
@@ -266,12 +279,18 @@ command :note do |c|
|
|
266
279
|
c.arg_name 'QUERY'
|
267
280
|
c.flag [:search]
|
268
281
|
|
282
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
283
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
284
|
+
|
285
|
+
c.desc 'Add note to item that *doesn\'t* match search/tag filters'
|
286
|
+
c.switch [:not], default_value: false, negatable: false
|
287
|
+
|
269
288
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
270
289
|
c.arg_name 'BOOLEAN'
|
271
290
|
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
|
272
291
|
|
273
292
|
c.desc 'Select item for new note from a menu of matching entries'
|
274
|
-
c.switch %i[i interactive]
|
293
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
275
294
|
|
276
295
|
c.action do |_global_options, options, args|
|
277
296
|
if options[:section]
|
@@ -280,6 +299,13 @@ command :note do |c|
|
|
280
299
|
|
281
300
|
options[:tag_bool] = options[:bool].normalize_bool
|
282
301
|
|
302
|
+
if options[:search]
|
303
|
+
search = options[:search]
|
304
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
305
|
+
options[:search] = search
|
306
|
+
end
|
307
|
+
|
308
|
+
|
283
309
|
last_entry = wwid.last_entry(options)
|
284
310
|
|
285
311
|
unless last_entry
|
@@ -291,7 +317,7 @@ command :note do |c|
|
|
291
317
|
new_note = Doing::Note.new
|
292
318
|
|
293
319
|
if options[:e] || (args.empty? && $stdin.stat.size.zero? && !options[:r])
|
294
|
-
raise
|
320
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
295
321
|
|
296
322
|
input = !args.empty? ? args.join(' ') : ''
|
297
323
|
|
@@ -312,7 +338,7 @@ command :note do |c|
|
|
312
338
|
elsif $stdin.stat.size.positive?
|
313
339
|
new_note.add($stdin.read)
|
314
340
|
else
|
315
|
-
raise
|
341
|
+
raise EmptyInput, 'You must provide content when adding a note' unless options[:remove]
|
316
342
|
end
|
317
343
|
|
318
344
|
if last_note.equal?(new_note)
|
@@ -338,7 +364,7 @@ command :meanwhile do |c|
|
|
338
364
|
c.switch %i[e editor], negatable: false, default_value: false
|
339
365
|
|
340
366
|
c.desc 'Archive previous @meanwhile entry'
|
341
|
-
c.switch %i[a archive], default_value: false
|
367
|
+
c.switch %i[a archive], negatable: false, default_value: false
|
342
368
|
|
343
369
|
c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
|
344
370
|
c.arg_name 'DATE_STRING'
|
@@ -352,7 +378,7 @@ command :meanwhile do |c|
|
|
352
378
|
if options[:back]
|
353
379
|
date = wwid.chronify(options[:back], guess: :begin)
|
354
380
|
|
355
|
-
raise
|
381
|
+
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
356
382
|
else
|
357
383
|
date = Time.now
|
358
384
|
end
|
@@ -365,7 +391,7 @@ command :meanwhile do |c|
|
|
365
391
|
input = ''
|
366
392
|
|
367
393
|
if options[:e]
|
368
|
-
raise
|
394
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
369
395
|
|
370
396
|
input += args.join(' ') unless args.empty?
|
371
397
|
input = wwid.fork_editor(input).strip
|
@@ -403,7 +429,7 @@ long_desc %(
|
|
403
429
|
arg_name 'TYPE', must_match: Doing::Plugins.template_regex
|
404
430
|
command :template do |c|
|
405
431
|
c.desc 'List all available templates'
|
406
|
-
c.switch %i[l list]
|
432
|
+
c.switch %i[l list], negatable: false
|
407
433
|
|
408
434
|
c.desc 'List in single column for completion'
|
409
435
|
c.switch %i[c]
|
@@ -424,7 +450,7 @@ command :template do |c|
|
|
424
450
|
type = args[0]
|
425
451
|
end
|
426
452
|
|
427
|
-
raise
|
453
|
+
raise InvalidPluginType, "No type specified, use `doing template [#{Doing::Plugins.plugin_templates.join('|')}]`" unless type
|
428
454
|
|
429
455
|
$stdout.puts Doing::Plugins.template_for_trigger(type)
|
430
456
|
|
@@ -473,6 +499,12 @@ command :select do |c|
|
|
473
499
|
c.arg_name 'QUERY'
|
474
500
|
c.flag %i[q query search]
|
475
501
|
|
502
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
503
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
504
|
+
|
505
|
+
c.desc 'Select items that *don\'t* match search/tag filters'
|
506
|
+
c.switch [:not], default_value: false, negatable: false
|
507
|
+
|
476
508
|
c.desc 'Use --no-menu to skip the interactive menu. Use with --query to filter items and act on results automatically. Test with `--output doing` to preview matches.'
|
477
509
|
c.switch %i[menu], negatable: true, default_value: true
|
478
510
|
|
@@ -503,12 +535,12 @@ command :select do |c|
|
|
503
535
|
c.flag %i[o output]
|
504
536
|
|
505
537
|
c.desc "Copy selection as a new entry with current time and no @done tag. Only works with single selections. Can be combined with --editor."
|
506
|
-
c.switch %i[again resume]
|
538
|
+
c.switch %i[again resume], negatable: false, default_value: false
|
507
539
|
|
508
540
|
c.action do |_global_options, options, args|
|
509
|
-
raise
|
541
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
510
542
|
|
511
|
-
raise
|
543
|
+
raise InvalidArgument, '--no-menu requires --query' if !options[:menu] && !options[:query]
|
512
544
|
|
513
545
|
wwid.interactive(options)
|
514
546
|
end
|
@@ -531,17 +563,17 @@ command :later do |c|
|
|
531
563
|
c.action do |_global_options, options, args|
|
532
564
|
if options[:back]
|
533
565
|
date = wwid.chronify(options[:back], guess: :begin)
|
534
|
-
raise
|
566
|
+
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
535
567
|
else
|
536
568
|
date = Time.now
|
537
569
|
end
|
538
570
|
|
539
571
|
if options[:editor] || (args.empty? && $stdin.stat.size.zero?)
|
540
|
-
raise
|
572
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
541
573
|
|
542
574
|
input = args.empty? ? '' : args.join(' ')
|
543
575
|
input = wwid.fork_editor(input).strip
|
544
|
-
raise
|
576
|
+
raise EmptyInput, 'No content' unless input && !input.empty?
|
545
577
|
|
546
578
|
title, note = wwid.format_input(input)
|
547
579
|
note.push(options[:n]) if options[:n]
|
@@ -558,7 +590,7 @@ command :later do |c|
|
|
558
590
|
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
559
591
|
wwid.write(wwid.doing_file)
|
560
592
|
else
|
561
|
-
raise
|
593
|
+
raise EmptyInput, 'You must provide content when creating a new entry'
|
562
594
|
end
|
563
595
|
end
|
564
596
|
end
|
@@ -614,19 +646,19 @@ command %i[done did] do |c|
|
|
614
646
|
|
615
647
|
if options[:took]
|
616
648
|
took = wwid.chronify_qty(options[:took])
|
617
|
-
raise
|
649
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
618
650
|
end
|
619
651
|
|
620
652
|
if options[:back]
|
621
653
|
date = wwid.chronify(options[:back], guess: :begin)
|
622
|
-
raise
|
654
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
|
623
655
|
else
|
624
656
|
date = options[:took] ? Time.now - took : Time.now
|
625
657
|
end
|
626
658
|
|
627
659
|
if options[:at]
|
628
660
|
finish_date = wwid.chronify(options[:at], guess: :begin)
|
629
|
-
raise
|
661
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
630
662
|
|
631
663
|
date = options[:took] ? finish_date - took : finish_date
|
632
664
|
elsif options[:took]
|
@@ -651,7 +683,7 @@ command %i[done did] do |c|
|
|
651
683
|
note.add(options[:note]) if options[:note]
|
652
684
|
|
653
685
|
if options[:editor]
|
654
|
-
raise
|
686
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
655
687
|
is_new = false
|
656
688
|
|
657
689
|
if args.empty?
|
@@ -659,7 +691,7 @@ command %i[done did] do |c|
|
|
659
691
|
|
660
692
|
unless last_entry
|
661
693
|
Doing.logger.debug('Skipped:', options[:unfinished] ? 'No unfinished entry' : 'Last entry already @done')
|
662
|
-
raise
|
694
|
+
raise NoResults, 'No results'
|
663
695
|
end
|
664
696
|
|
665
697
|
old_entry = last_entry.dup
|
@@ -671,7 +703,7 @@ command %i[done did] do |c|
|
|
671
703
|
end
|
672
704
|
|
673
705
|
input = wwid.fork_editor(input).strip
|
674
|
-
raise
|
706
|
+
raise EmptyInput, 'No content' unless input && !input.empty?
|
675
707
|
|
676
708
|
title, note = wwid.format_input(input)
|
677
709
|
new_entry = Doing::Item.new(date, title, section, note)
|
@@ -746,7 +778,7 @@ command %i[done did] do |c|
|
|
746
778
|
wwid.write(wwid.doing_file)
|
747
779
|
Doing.logger.info('Entry Added:', new_entry.title)
|
748
780
|
else
|
749
|
-
raise
|
781
|
+
raise EmptyInput, 'You must provide content when creating a new entry'
|
750
782
|
end
|
751
783
|
end
|
752
784
|
end
|
@@ -774,7 +806,7 @@ command :cancel do |c|
|
|
774
806
|
c.switch %i[u unfinished], negatable: false, default_value: false
|
775
807
|
|
776
808
|
c.desc 'Select item(s) to cancel from a menu of matching entries'
|
777
|
-
c.switch %i[i interactive]
|
809
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
778
810
|
|
779
811
|
c.action do |_global_options, options, args|
|
780
812
|
if options[:section]
|
@@ -789,9 +821,9 @@ command :cancel do |c|
|
|
789
821
|
tags = options[:tag].to_tags
|
790
822
|
end
|
791
823
|
|
792
|
-
raise
|
824
|
+
raise InvalidArgument, 'Only one argument allowed' if args.length > 1
|
793
825
|
|
794
|
-
raise
|
826
|
+
raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)' unless args.empty? || args[0] =~ /\d+/
|
795
827
|
|
796
828
|
if options[:interactive]
|
797
829
|
count = 0
|
@@ -844,6 +876,12 @@ command :finish do |c|
|
|
844
876
|
c.arg_name 'QUERY'
|
845
877
|
c.flag [:search]
|
846
878
|
|
879
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
880
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
881
|
+
|
882
|
+
c.desc 'Finish items that *don\'t* match search/tag filters'
|
883
|
+
c.switch [:not], default_value: false, negatable: false
|
884
|
+
|
847
885
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
848
886
|
c.arg_name 'BOOLEAN'
|
849
887
|
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
|
@@ -867,28 +905,28 @@ command :finish do |c|
|
|
867
905
|
c.flag %i[s section]
|
868
906
|
|
869
907
|
c.desc 'Select item(s) to finish from a menu of matching entries'
|
870
|
-
c.switch %i[i interactive]
|
908
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
871
909
|
|
872
910
|
c.action do |_global_options, options, args|
|
873
911
|
unless options[:auto]
|
874
912
|
if options[:took]
|
875
913
|
took = wwid.chronify_qty(options[:took])
|
876
|
-
raise
|
914
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
|
877
915
|
end
|
878
916
|
|
879
|
-
raise
|
917
|
+
raise InvalidArgument, '--back and --took can not be used together' if options[:back] && options[:took]
|
880
918
|
|
881
|
-
raise
|
919
|
+
raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
|
882
920
|
|
883
921
|
if options[:at]
|
884
922
|
finish_date = wwid.chronify(options[:at], guess: :begin)
|
885
|
-
raise
|
923
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
|
886
924
|
|
887
925
|
date = options[:took] ? finish_date - took : finish_date
|
888
926
|
elsif options[:back]
|
889
927
|
date = wwid.chronify(options[:back])
|
890
928
|
|
891
|
-
raise
|
929
|
+
raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
|
892
930
|
elsif options[:took]
|
893
931
|
date = wwid.chronify_qty(options[:took])
|
894
932
|
else
|
@@ -902,9 +940,9 @@ command :finish do |c|
|
|
902
940
|
tags = options[:tag].to_tags
|
903
941
|
end
|
904
942
|
|
905
|
-
raise
|
943
|
+
raise InvalidArgument, 'Only one argument allowed' if args.length > 1
|
906
944
|
|
907
|
-
raise
|
945
|
+
raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)' unless args.length == 0 || args[0] =~ /\d+/
|
908
946
|
|
909
947
|
if options[:interactive]
|
910
948
|
count = 0
|
@@ -912,12 +950,20 @@ command :finish do |c|
|
|
912
950
|
count = args[0] ? args[0].to_i : 1
|
913
951
|
end
|
914
952
|
|
953
|
+
search = nil
|
954
|
+
|
955
|
+
if options[:search]
|
956
|
+
search = options[:search]
|
957
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
958
|
+
end
|
959
|
+
|
915
960
|
opts = {
|
916
961
|
archive: options[:archive],
|
917
962
|
back: date,
|
918
963
|
count: count,
|
919
964
|
date: options[:date],
|
920
|
-
search:
|
965
|
+
search: search,
|
966
|
+
not: options[:not],
|
921
967
|
section: options[:section],
|
922
968
|
sequential: options[:auto],
|
923
969
|
tag: tags,
|
@@ -951,6 +997,12 @@ command %i[again resume] do |c|
|
|
951
997
|
c.arg_name 'QUERY'
|
952
998
|
c.flag [:search]
|
953
999
|
|
1000
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
1001
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
1002
|
+
|
1003
|
+
c.desc 'Resume items that *don\'t* match search/tag filters'
|
1004
|
+
c.switch [:not], default_value: false, negatable: false
|
1005
|
+
|
954
1006
|
c.desc 'Boolean used to combine multiple tags'
|
955
1007
|
c.arg_name 'BOOLEAN'
|
956
1008
|
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
|
@@ -963,11 +1015,19 @@ command %i[again resume] do |c|
|
|
963
1015
|
c.flag %i[n note]
|
964
1016
|
|
965
1017
|
c.desc 'Select item to resume from a menu of matching entries'
|
966
|
-
c.switch %i[i interactive]
|
1018
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
967
1019
|
|
968
1020
|
c.action do |_global_options, options, _args|
|
969
1021
|
tags = options[:tag].nil? ? [] : options[:tag].to_tags
|
1022
|
+
|
1023
|
+
if options[:search]
|
1024
|
+
search = options[:search]
|
1025
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
1026
|
+
options[:search] = search
|
1027
|
+
end
|
1028
|
+
|
970
1029
|
opts = options
|
1030
|
+
|
971
1031
|
opts[:tag] = tags
|
972
1032
|
opts[:tag_bool] = options[:bool].normalize_bool
|
973
1033
|
opts[:interactive] = options[:interactive]
|
@@ -1037,17 +1097,23 @@ command :tag do |c|
|
|
1037
1097
|
c.arg_name 'QUERY'
|
1038
1098
|
c.flag [:search]
|
1039
1099
|
|
1100
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
1101
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
1102
|
+
|
1103
|
+
c.desc 'Tag items that *don\'t* match search/tag filters'
|
1104
|
+
c.switch [:not], default_value: false, negatable: false
|
1105
|
+
|
1040
1106
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
1041
1107
|
c.arg_name 'BOOLEAN'
|
1042
1108
|
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
|
1043
1109
|
|
1044
1110
|
c.desc 'Select item(s) to tag from a menu of matching entries'
|
1045
|
-
c.switch %i[i interactive]
|
1111
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
1046
1112
|
|
1047
1113
|
c.action do |_global_options, options, args|
|
1048
|
-
raise
|
1114
|
+
raise MissingArgument, 'You must specify at least one tag' if args.empty? && !options[:autotag]
|
1049
1115
|
|
1050
|
-
raise
|
1116
|
+
raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
|
1051
1117
|
|
1052
1118
|
section = 'All'
|
1053
1119
|
|
@@ -1081,6 +1147,11 @@ command :tag do |c|
|
|
1081
1147
|
count = options[:count].to_i
|
1082
1148
|
end
|
1083
1149
|
|
1150
|
+
if options[:search]
|
1151
|
+
search = options[:search]
|
1152
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
1153
|
+
options[:search] = search
|
1154
|
+
end
|
1084
1155
|
|
1085
1156
|
if count.zero? && !options[:force]
|
1086
1157
|
if options[:search]
|
@@ -1104,7 +1175,7 @@ command :tag do |c|
|
|
1104
1175
|
|
1105
1176
|
res = wwid.yn(question, default_response: false)
|
1106
1177
|
|
1107
|
-
|
1178
|
+
raise UserCancelled unless res
|
1108
1179
|
end
|
1109
1180
|
|
1110
1181
|
options[:count] = count
|
@@ -1117,19 +1188,6 @@ command :tag do |c|
|
|
1117
1188
|
end
|
1118
1189
|
end
|
1119
1190
|
|
1120
|
-
# desc 'Autotag last X entries'
|
1121
|
-
# arg_name 'COUNT'
|
1122
|
-
# command :autotag do |c|
|
1123
|
-
# c.action do |global_options, options, args|
|
1124
|
-
# options = {
|
1125
|
-
# autotag: true,
|
1126
|
-
# count: args[0].to_i
|
1127
|
-
# }
|
1128
|
-
# cmd = commands[:tag]
|
1129
|
-
# cmd.action.(global_options, options, [])
|
1130
|
-
# end
|
1131
|
-
# end
|
1132
|
-
|
1133
1191
|
desc 'Mark last entry as flagged'
|
1134
1192
|
command [:mark, :flag] do |c|
|
1135
1193
|
c.example 'doing flag', desc: 'Add @flagged to the last entry created'
|
@@ -1165,17 +1223,23 @@ command [:mark, :flag] do |c|
|
|
1165
1223
|
c.arg_name 'QUERY'
|
1166
1224
|
c.flag [:search]
|
1167
1225
|
|
1226
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
1227
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
1228
|
+
|
1229
|
+
c.desc 'Flag items that *don\'t* match search/tag/date filters'
|
1230
|
+
c.switch [:not], default_value: false, negatable: false
|
1231
|
+
|
1168
1232
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
1169
1233
|
c.arg_name 'BOOLEAN'
|
1170
1234
|
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'AND'
|
1171
1235
|
|
1172
1236
|
c.desc 'Select item(s) to flag from a menu of matching entries'
|
1173
|
-
c.switch %i[i interactive]
|
1237
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
1174
1238
|
|
1175
1239
|
c.action do |_global_options, options, _args|
|
1176
1240
|
mark = settings['marker_tag'] || 'flagged'
|
1177
1241
|
|
1178
|
-
raise
|
1242
|
+
raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
|
1179
1243
|
|
1180
1244
|
section = 'All'
|
1181
1245
|
|
@@ -1196,6 +1260,12 @@ command [:mark, :flag] do |c|
|
|
1196
1260
|
count = options[:count].to_i
|
1197
1261
|
end
|
1198
1262
|
|
1263
|
+
if options[:search]
|
1264
|
+
search = options[:search]
|
1265
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
1266
|
+
options[:search] = search
|
1267
|
+
end
|
1268
|
+
|
1199
1269
|
if count.zero? && !options[:force]
|
1200
1270
|
if options[:search]
|
1201
1271
|
section_q = ' matching your search terms'
|
@@ -1270,6 +1340,12 @@ command :show do |c|
|
|
1270
1340
|
c.arg_name 'QUERY'
|
1271
1341
|
c.flag [:search]
|
1272
1342
|
|
1343
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
1344
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
1345
|
+
|
1346
|
+
c.desc 'Show items that *don\'t* match search/tag/date filters'
|
1347
|
+
c.switch [:not], default_value: false, negatable: false
|
1348
|
+
|
1273
1349
|
c.desc 'Sort order (asc/desc)'
|
1274
1350
|
c.arg_name 'ORDER'
|
1275
1351
|
c.flag %i[s sort], must_match: REGEX_SORT_ORDER, default_value: 'asc'
|
@@ -1302,13 +1378,13 @@ command :show do |c|
|
|
1302
1378
|
c.switch [:only_timed], default_value: false, negatable: false
|
1303
1379
|
|
1304
1380
|
c.desc 'Select from a menu of matching entries to perform additional operations'
|
1305
|
-
c.switch %i[i interactive]
|
1381
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
1306
1382
|
|
1307
1383
|
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
1308
1384
|
c.arg_name 'FORMAT'
|
1309
1385
|
c.flag %i[o output]
|
1310
|
-
c.action do |
|
1311
|
-
raise
|
1386
|
+
c.action do |global_options, options, args|
|
1387
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
1312
1388
|
|
1313
1389
|
tag_filter = false
|
1314
1390
|
tags = []
|
@@ -1323,8 +1399,15 @@ command :show do |c|
|
|
1323
1399
|
when /^@/
|
1324
1400
|
section = 'All'
|
1325
1401
|
else
|
1326
|
-
|
1327
|
-
|
1402
|
+
begin
|
1403
|
+
section = wwid.guess_section(args[0])
|
1404
|
+
rescue WrongCommand => exception
|
1405
|
+
cmd = commands[:view]
|
1406
|
+
action = cmd.send(:get_action, nil)
|
1407
|
+
return action.call(global_options, options, args)
|
1408
|
+
end
|
1409
|
+
|
1410
|
+
raise InvalidSection, "No such section: #{args[0]}" unless section
|
1328
1411
|
|
1329
1412
|
args.shift
|
1330
1413
|
end
|
@@ -1359,7 +1442,7 @@ command :show do |c|
|
|
1359
1442
|
start = wwid.chronify(date_string, guess: :begin)
|
1360
1443
|
finish = false
|
1361
1444
|
end
|
1362
|
-
raise
|
1445
|
+
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
1363
1446
|
dates = [start, finish]
|
1364
1447
|
end
|
1365
1448
|
|
@@ -1367,6 +1450,12 @@ command :show do |c|
|
|
1367
1450
|
|
1368
1451
|
tags_color = settings.key?('tags_color') ? settings['tags_color'] : nil
|
1369
1452
|
|
1453
|
+
if options[:search]
|
1454
|
+
search = options[:search]
|
1455
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
1456
|
+
options[:search] = search
|
1457
|
+
end
|
1458
|
+
|
1370
1459
|
opt = options.dup
|
1371
1460
|
|
1372
1461
|
opt[:sort_tags] = options[:tag_sort] =~ /^n/i
|
@@ -1429,20 +1518,31 @@ command %i[grep search] do |c|
|
|
1429
1518
|
c.desc 'Only show items with recorded time intervals'
|
1430
1519
|
c.switch [:only_timed], default_value: false, negatable: false
|
1431
1520
|
|
1521
|
+
c.desc 'Force exact string matching (case sensitive)'
|
1522
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
1523
|
+
|
1524
|
+
c.desc 'Force case sensitive matching'
|
1525
|
+
c.switch %i[case]
|
1526
|
+
|
1527
|
+
c.desc 'Show items that *don\'t* match search string'
|
1528
|
+
c.switch [:not], default_value: false, negatable: false
|
1529
|
+
|
1432
1530
|
c.desc 'Display an interactive menu of results to perform further operations'
|
1433
1531
|
c.switch %i[i interactive], default_value: false, negatable: false
|
1434
1532
|
|
1435
1533
|
c.action do |_global_options, options, args|
|
1436
|
-
raise
|
1534
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
1437
1535
|
|
1438
1536
|
tags_color = settings.key?('tags_color') ? settings['tags_color'] : nil
|
1439
1537
|
|
1440
1538
|
section = wwid.guess_section(options[:section]) if options[:section]
|
1539
|
+
search = args.join(' ')
|
1540
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
1441
1541
|
|
1442
1542
|
options[:times] = true if options[:totals]
|
1443
1543
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1444
1544
|
options[:highlight] = true
|
1445
|
-
options[:search] =
|
1545
|
+
options[:search] = search
|
1446
1546
|
options[:section] = section
|
1447
1547
|
options[:tags_color] = tags_color
|
1448
1548
|
|
@@ -1476,7 +1576,7 @@ command :recent do |c|
|
|
1476
1576
|
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
1477
1577
|
|
1478
1578
|
c.desc 'Select from a menu of matching entries to perform additional operations'
|
1479
|
-
c.switch %i[i interactive]
|
1579
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
1480
1580
|
|
1481
1581
|
c.action do |global_options, options, args|
|
1482
1582
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
@@ -1548,7 +1648,7 @@ command :today do |c|
|
|
1548
1648
|
c.flag [:after]
|
1549
1649
|
|
1550
1650
|
c.action do |_global_options, options, _args|
|
1551
|
-
raise
|
1651
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
1552
1652
|
|
1553
1653
|
options[:t] = true if options[:totals]
|
1554
1654
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
@@ -1595,9 +1695,9 @@ command :on do |c|
|
|
1595
1695
|
c.flag %i[o output]
|
1596
1696
|
|
1597
1697
|
c.action do |_global_options, options, args|
|
1598
|
-
raise
|
1698
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
1599
1699
|
|
1600
|
-
raise
|
1700
|
+
raise MissingArgument, 'Missing date argument' if args.empty?
|
1601
1701
|
|
1602
1702
|
date_string = args.join(' ')
|
1603
1703
|
|
@@ -1610,7 +1710,7 @@ command :on do |c|
|
|
1610
1710
|
finish = false
|
1611
1711
|
end
|
1612
1712
|
|
1613
|
-
raise
|
1713
|
+
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
1614
1714
|
|
1615
1715
|
message = "Date interpreted as #{start}"
|
1616
1716
|
message += " to #{finish}" if finish
|
@@ -1653,9 +1753,9 @@ command :since do |c|
|
|
1653
1753
|
c.flag %i[o output]
|
1654
1754
|
|
1655
1755
|
c.action do |_global_options, options, args|
|
1656
|
-
raise
|
1756
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
1657
1757
|
|
1658
|
-
raise
|
1758
|
+
raise MissingArgument, 'Missing date argument' if args.empty?
|
1659
1759
|
|
1660
1760
|
date_string = args.join(' ')
|
1661
1761
|
|
@@ -1665,7 +1765,7 @@ command :since do |c|
|
|
1665
1765
|
start = wwid.chronify(date_string, guess: :begin)
|
1666
1766
|
finish = Time.now
|
1667
1767
|
|
1668
|
-
raise
|
1768
|
+
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
1669
1769
|
|
1670
1770
|
Doing.logger.debug("Date interpreted as #{start} through the current time")
|
1671
1771
|
|
@@ -1698,7 +1798,6 @@ command :yesterday do |c|
|
|
1698
1798
|
c.switch [:totals], default_value: false, negatable: false
|
1699
1799
|
|
1700
1800
|
c.desc 'Sort tags by (name|time)'
|
1701
|
-
default = 'time'
|
1702
1801
|
default = settings['tag_sort'] || 'name'
|
1703
1802
|
c.arg_name 'KEY'
|
1704
1803
|
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
@@ -1716,7 +1815,7 @@ command :yesterday do |c|
|
|
1716
1815
|
c.flag [:tag_order], must_match: REGEX_SORT_ORDER, default_value: 'asc'
|
1717
1816
|
|
1718
1817
|
c.action do |_global_options, options, _args|
|
1719
|
-
raise
|
1818
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
1720
1819
|
|
1721
1820
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1722
1821
|
|
@@ -1761,8 +1860,14 @@ command :last do |c|
|
|
1761
1860
|
c.arg_name 'QUERY'
|
1762
1861
|
c.flag [:search]
|
1763
1862
|
|
1863
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
1864
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
1865
|
+
|
1866
|
+
c.desc 'Show items that *don\'t* match search string or tag filter'
|
1867
|
+
c.switch [:not], default_value: false, negatable: false
|
1868
|
+
|
1764
1869
|
c.action do |global_options, options, _args|
|
1765
|
-
raise
|
1870
|
+
raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
|
1766
1871
|
|
1767
1872
|
if options[:tag].nil?
|
1768
1873
|
tags = []
|
@@ -1779,11 +1884,18 @@ command :last do |c|
|
|
1779
1884
|
|
1780
1885
|
end
|
1781
1886
|
|
1887
|
+
search = nil
|
1888
|
+
|
1889
|
+
if options[:search]
|
1890
|
+
search = options[:search]
|
1891
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
1892
|
+
end
|
1893
|
+
|
1782
1894
|
if options[:editor]
|
1783
|
-
wwid.edit_last(section: options[:s], options: { search:
|
1895
|
+
wwid.edit_last(section: options[:s], options: { search: search, tag: tags, tag_bool: options[:bool], not: options[:not] })
|
1784
1896
|
else
|
1785
1897
|
Doing::Pager::page wwid.last(times: true, section: options[:s],
|
1786
|
-
options: { search: options[:
|
1898
|
+
options: { search: search, negate: options[:not], tag: tags, tag_bool: options[:bool] }).strip
|
1787
1899
|
end
|
1788
1900
|
end
|
1789
1901
|
end
|
@@ -1791,7 +1903,7 @@ end
|
|
1791
1903
|
desc 'List sections'
|
1792
1904
|
command :sections do |c|
|
1793
1905
|
c.desc 'List in single column'
|
1794
|
-
c.switch %i[c column], default_value: false
|
1906
|
+
c.switch %i[c column], negatable: false, default_value: false
|
1795
1907
|
|
1796
1908
|
c.action do |_global_options, options, _args|
|
1797
1909
|
joiner = options[:c] ? "\n" : "\t"
|
@@ -1814,7 +1926,7 @@ command :add_section do |c|
|
|
1814
1926
|
c.example 'doing add_section Ideas', desc: 'Add a section called Ideas to the doing file'
|
1815
1927
|
|
1816
1928
|
c.action do |_global_options, _options, args|
|
1817
|
-
raise
|
1929
|
+
raise InvalidArgument, "Section #{args[0]} already exists" if wwid.sections.include?(args[0])
|
1818
1930
|
|
1819
1931
|
wwid.add_section(args.join(' ').cap_first)
|
1820
1932
|
wwid.write(wwid.doing_file)
|
@@ -1853,16 +1965,54 @@ command :plugins do |c|
|
|
1853
1965
|
|
1854
1966
|
c.desc 'List plugins of type (import, export)'
|
1855
1967
|
c.arg_name 'TYPE'
|
1856
|
-
c.flag %i[t type], must_match: /^[iea]
|
1968
|
+
c.flag %i[t type], must_match: /^(?:[iea].*)$/i, default_value: 'all'
|
1857
1969
|
|
1858
1970
|
c.desc 'List in single column for completion'
|
1859
|
-
c.switch %i[c column], default_value: false
|
1971
|
+
c.switch %i[c column], negatable: false, default_value: false
|
1860
1972
|
|
1861
1973
|
c.action do |_global_options, options, _args|
|
1862
1974
|
Doing::Plugins.list_plugins(options)
|
1863
1975
|
end
|
1864
1976
|
end
|
1865
1977
|
|
1978
|
+
desc 'Generate shell completion scripts'
|
1979
|
+
command :completion do |c|
|
1980
|
+
c.example 'doing completion', desc: 'Output zsh (default) to STDOUT'
|
1981
|
+
c.example 'doing completion --type zsh --file ~/.zsh-completions/_doing.zsh', desc: 'Output zsh completions to file'
|
1982
|
+
c.example 'doing completion --type fish --file ~/.config/fish/completions/doing.fish', desc: 'Output fish completions to file'
|
1983
|
+
c.example 'doing completion --type bash --file ~/.bash_it/completion/enabled/doing.bash', desc: 'Output bash completions to file'
|
1984
|
+
|
1985
|
+
c.desc 'Shell to generate for (bash, zsh, fish)'
|
1986
|
+
c.arg_name 'SHELL'
|
1987
|
+
c.flag %i[t type], must_match: /^[bzf](?:[ai]?sh)?$/i, default_value: 'zsh'
|
1988
|
+
|
1989
|
+
c.desc 'File to write output to'
|
1990
|
+
c.arg_name 'PATH'
|
1991
|
+
c.flag %i[f file], default_value: 'stdout'
|
1992
|
+
|
1993
|
+
c.action do |_global_options, options, _args|
|
1994
|
+
script_dir = File.join(File.dirname(__FILE__), '..', 'scripts')
|
1995
|
+
|
1996
|
+
case options[:type]
|
1997
|
+
when /^b/
|
1998
|
+
result = `ruby #{File.join(script_dir, 'generate_bash_completions.rb')}`
|
1999
|
+
when /^z/
|
2000
|
+
result = `ruby #{File.join(script_dir, 'generate_zsh_completions.rb')}`
|
2001
|
+
when /^f/
|
2002
|
+
result = `ruby #{File.join(script_dir, 'generate_fish_completions.rb')}`
|
2003
|
+
end
|
2004
|
+
|
2005
|
+
if options[:file] =~ /^stdout$/i
|
2006
|
+
$stdout.puts result
|
2007
|
+
else
|
2008
|
+
File.open(File.expand_path(options[:file]), 'w') do |f|
|
2009
|
+
f.puts result
|
2010
|
+
end
|
2011
|
+
Doing.logger.warn('File written:', "#{options[:type]} completions written to #{options[:file]}")
|
2012
|
+
end
|
2013
|
+
end
|
2014
|
+
end
|
2015
|
+
|
1866
2016
|
desc 'Display a user-created view'
|
1867
2017
|
long_desc 'Command line options override view configuration'
|
1868
2018
|
arg_name 'VIEW_NAME'
|
@@ -1903,6 +2053,12 @@ command :view do |c|
|
|
1903
2053
|
c.arg_name 'QUERY'
|
1904
2054
|
c.flag [:search]
|
1905
2055
|
|
2056
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
2057
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
2058
|
+
|
2059
|
+
c.desc 'Show items that *don\'t* match search string'
|
2060
|
+
c.switch [:not], default_value: false, negatable: false
|
2061
|
+
|
1906
2062
|
c.desc 'Sort tags by (name|time)'
|
1907
2063
|
c.arg_name 'KEY'
|
1908
2064
|
c.flag [:tag_sort], must_match: /^(?:name|time)$/i
|
@@ -1923,17 +2079,24 @@ command :view do |c|
|
|
1923
2079
|
c.switch [:only_timed], default_value: false, negatable: false
|
1924
2080
|
|
1925
2081
|
c.desc 'Select from a menu of matching entries to perform additional operations'
|
1926
|
-
c.switch %i[i interactive]
|
2082
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
1927
2083
|
|
1928
|
-
c.action do |
|
1929
|
-
raise
|
2084
|
+
c.action do |global_options, options, args|
|
2085
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
1930
2086
|
|
1931
|
-
raise
|
2087
|
+
raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
|
1932
2088
|
|
1933
2089
|
title = if args.empty?
|
1934
2090
|
wwid.choose_view
|
1935
2091
|
else
|
1936
|
-
|
2092
|
+
begin
|
2093
|
+
wwid.guess_view(args[0])
|
2094
|
+
rescue WrongCommand => exception
|
2095
|
+
cmd = commands[:show]
|
2096
|
+
options[:sort] = 'asc'
|
2097
|
+
action = cmd.send(:get_action, nil)
|
2098
|
+
return action.call(global_options, options, args)
|
2099
|
+
end
|
1937
2100
|
end
|
1938
2101
|
|
1939
2102
|
if options[:section]
|
@@ -2023,11 +2186,19 @@ command :view do |c|
|
|
2023
2186
|
start = wwid.chronify(date_string, guess: :begin)
|
2024
2187
|
finish = false
|
2025
2188
|
end
|
2026
|
-
raise
|
2189
|
+
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
2027
2190
|
dates = [start, finish]
|
2028
2191
|
end
|
2029
2192
|
|
2193
|
+
search = nil
|
2194
|
+
|
2195
|
+
if options[:search]
|
2196
|
+
search = options[:search]
|
2197
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
2198
|
+
end
|
2199
|
+
|
2030
2200
|
opts = options
|
2201
|
+
opts[:search] = search
|
2031
2202
|
opts[:output] = output_format
|
2032
2203
|
opts[:count] = count
|
2033
2204
|
opts[:format] = date_format
|
@@ -2046,9 +2217,9 @@ command :view do |c|
|
|
2046
2217
|
|
2047
2218
|
Doing::Pager.page wwid.list_section(opts)
|
2048
2219
|
elsif title.instance_of?(FalseClass)
|
2049
|
-
|
2220
|
+
raise UserCancelled, 'Cancelled' unless res
|
2050
2221
|
else
|
2051
|
-
raise
|
2222
|
+
raise InvalidView, "View #{title} not found in config"
|
2052
2223
|
end
|
2053
2224
|
end
|
2054
2225
|
end
|
@@ -2100,6 +2271,12 @@ command %i[archive move] do |c|
|
|
2100
2271
|
c.arg_name 'QUERY'
|
2101
2272
|
c.flag [:search]
|
2102
2273
|
|
2274
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
2275
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
2276
|
+
|
2277
|
+
c.desc 'Show items that *don\'t* match search string'
|
2278
|
+
c.switch [:not], default_value: false, negatable: false
|
2279
|
+
|
2103
2280
|
c.desc 'Archive entries older than date
|
2104
2281
|
(Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
|
2105
2282
|
c.arg_name 'DATE_STRING'
|
@@ -2119,11 +2296,19 @@ command %i[archive move] do |c|
|
|
2119
2296
|
tags = args.length > 1 ? args[1..].map { |t| t.sub(/^@/, '').strip } : []
|
2120
2297
|
end
|
2121
2298
|
|
2122
|
-
raise
|
2299
|
+
raise InvalidArgument, '--keep and --count can not be used together' if options[:keep] && options[:count]
|
2123
2300
|
|
2124
2301
|
tags.concat(options[:tag].to_tags) if options[:tag]
|
2125
2302
|
|
2303
|
+
search = nil
|
2304
|
+
|
2305
|
+
if options[:search]
|
2306
|
+
search = options[:search]
|
2307
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
2308
|
+
end
|
2309
|
+
|
2126
2310
|
opts = options
|
2311
|
+
opts[:search] = search
|
2127
2312
|
opts[:bool] = options[:bool].normalize_bool
|
2128
2313
|
opts[:destination] = options[:to]
|
2129
2314
|
opts[:tags] = tags
|
@@ -2158,6 +2343,12 @@ command :rotate do |c|
|
|
2158
2343
|
c.arg_name 'QUERY'
|
2159
2344
|
c.flag [:search]
|
2160
2345
|
|
2346
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
2347
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
2348
|
+
|
2349
|
+
c.desc 'Rotate items that *don\'t* match search string or tag filter'
|
2350
|
+
c.switch [:not], default_value: false, negatable: false
|
2351
|
+
|
2161
2352
|
c.desc 'Rotate entries older than date
|
2162
2353
|
(Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
|
2163
2354
|
c.arg_name 'DATE_STRING'
|
@@ -2170,6 +2361,14 @@ command :rotate do |c|
|
|
2170
2361
|
|
2171
2362
|
options[:bool] = options[:bool].normalize_bool
|
2172
2363
|
|
2364
|
+
search = nil
|
2365
|
+
|
2366
|
+
if options[:search]
|
2367
|
+
search = options[:search]
|
2368
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
2369
|
+
options[:search] = search
|
2370
|
+
end
|
2371
|
+
|
2173
2372
|
wwid.rotate(options)
|
2174
2373
|
end
|
2175
2374
|
end
|
@@ -2208,7 +2407,7 @@ command :open do |c|
|
|
2208
2407
|
system %(open "#{File.expand_path(wwid.doing_file)}")
|
2209
2408
|
end
|
2210
2409
|
else
|
2211
|
-
raise
|
2410
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
|
2212
2411
|
|
2213
2412
|
system %(#{Doing::Util.default_editor} "#{File.expand_path(wwid.doing_file)}")
|
2214
2413
|
end
|
@@ -2236,7 +2435,7 @@ command :config do |c|
|
|
2236
2435
|
c.flag %i[e editor], default_value: nil
|
2237
2436
|
|
2238
2437
|
c.desc 'Show a config key value based on arguments. Separate key paths with colons or dots, e.g. "export_templates.html". Empty arguments outputs the entire config.'
|
2239
|
-
c.switch %i[d dump]
|
2438
|
+
c.switch %i[d dump], negatable: false
|
2240
2439
|
|
2241
2440
|
c.desc 'Format for --dump (json|yaml|raw)'
|
2242
2441
|
c.arg_name 'FORMAT'
|
@@ -2261,7 +2460,6 @@ command :config do |c|
|
|
2261
2460
|
c.action do |_global_options, options, args|
|
2262
2461
|
if options[:update]
|
2263
2462
|
config.configure({rewrite: true, ignore_local: true})
|
2264
|
-
# Doing.logger.warn("Config file rewritten: #{config.config_file}")
|
2265
2463
|
return
|
2266
2464
|
end
|
2267
2465
|
|
@@ -2276,7 +2474,6 @@ command :config do |c|
|
|
2276
2474
|
when /^r/
|
2277
2475
|
cfg
|
2278
2476
|
else
|
2279
|
-
# cfg = { last_key => cfg } unless last_key.nil?
|
2280
2477
|
YAML.dump(cfg)
|
2281
2478
|
end
|
2282
2479
|
else
|
@@ -2291,7 +2488,7 @@ command :config do |c|
|
|
2291
2488
|
choices.concat(config.additional_configs)
|
2292
2489
|
res = wwid.choose_from(choices.uniq.sort.reverse, sorted: false, prompt: 'Local configs found, select which to edit > ')
|
2293
2490
|
|
2294
|
-
raise
|
2491
|
+
raise UserCancelled, 'Cancelled' unless res
|
2295
2492
|
|
2296
2493
|
config_file = res.strip || config.config_file
|
2297
2494
|
else
|
@@ -2308,7 +2505,7 @@ command :config do |c|
|
|
2308
2505
|
`open -a "#{editor}" "#{config_file}"`
|
2309
2506
|
end
|
2310
2507
|
else
|
2311
|
-
raise
|
2508
|
+
raise InvalidArgument, 'No viable editor found in config or environment.'
|
2312
2509
|
end
|
2313
2510
|
elsif options[:a] || options[:b]
|
2314
2511
|
if options[:a]
|
@@ -2319,7 +2516,7 @@ command :config do |c|
|
|
2319
2516
|
else
|
2320
2517
|
editor = options[:e] || Doing::Util.find_default_editor('config')
|
2321
2518
|
|
2322
|
-
raise
|
2519
|
+
raise MissingEditor, 'No viable editor defined in config or environment' unless editor
|
2323
2520
|
|
2324
2521
|
if Doing::Util.exec_available(editor)
|
2325
2522
|
system %(#{editor} "#{config_file}")
|
@@ -2329,7 +2526,7 @@ command :config do |c|
|
|
2329
2526
|
end
|
2330
2527
|
else
|
2331
2528
|
editor = options[:e] || Doing::Util.default_editor
|
2332
|
-
raise
|
2529
|
+
raise MissingEditor, 'No EDITOR variable defined in environment' unless editor && Doing::Util.exec_available(editor)
|
2333
2530
|
|
2334
2531
|
system %(#{editor} "#{config_file}")
|
2335
2532
|
end
|
@@ -2360,6 +2557,12 @@ command :import do |c|
|
|
2360
2557
|
c.arg_name 'QUERY'
|
2361
2558
|
c.flag [:search]
|
2362
2559
|
|
2560
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
2561
|
+
c.switch %i[x exact], default_value: false, negatable: false
|
2562
|
+
|
2563
|
+
c.desc 'Import items that *don\'t* match search/tag/date filters'
|
2564
|
+
c.switch [:not], default_value: false, negatable: false
|
2565
|
+
|
2363
2566
|
c.desc 'Only import items with recorded time intervals'
|
2364
2567
|
c.switch [:only_timed], default_value: false, negatable: false
|
2365
2568
|
|
@@ -2413,7 +2616,7 @@ command :import do |c|
|
|
2413
2616
|
start = wwid.chronify(date_string, guess: :begin)
|
2414
2617
|
finish = false
|
2415
2618
|
end
|
2416
|
-
raise
|
2619
|
+
raise InvalidTimeExpression, 'Unrecognized date string' unless start
|
2417
2620
|
dates = [start, finish]
|
2418
2621
|
end
|
2419
2622
|
|
@@ -2423,7 +2626,7 @@ command :import do |c|
|
|
2423
2626
|
wwid.import(args, options)
|
2424
2627
|
wwid.write(wwid.doing_file)
|
2425
2628
|
else
|
2426
|
-
raise
|
2629
|
+
raise InvalidPluginType, "Invalid import type: #{options[:type]}"
|
2427
2630
|
end
|
2428
2631
|
end
|
2429
2632
|
end
|
@@ -2448,14 +2651,13 @@ pre do |global, _command, _options, _args|
|
|
2448
2651
|
end
|
2449
2652
|
|
2450
2653
|
on_error do |exception|
|
2451
|
-
|
2452
|
-
|
2453
|
-
|
2454
|
-
|
2455
|
-
|
2456
|
-
|
2457
|
-
|
2458
|
-
false
|
2654
|
+
if exception.kind_of?(SystemExit)
|
2655
|
+
false
|
2656
|
+
else
|
2657
|
+
# Doing.logger.error('Fatal:', exception)
|
2658
|
+
Doing.logger.output_results
|
2659
|
+
true
|
2660
|
+
end
|
2459
2661
|
end
|
2460
2662
|
|
2461
2663
|
post do |global, _command, _options, _args|
|