doing 2.0.5.pre → 2.0.6.pre

Sign up to get free protection for your applications and to get access to all the features.
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 config.settings.dig('plugins', 'command_path')
61
- commands_from File.expand_path(config.settings.dig('plugins', 'command_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 Doing::Errors::InvalidTimeExpression, 'Unable to parse date string' if date.nil?
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 Doing::Errors::MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
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 Doing::Errors::EmptyInput, 'No content' if input.empty?
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,7 +173,7 @@ 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 Doing::Errors::EmptyInput, 'You must provide content when creating a new entry'
176
+ raise EmptyInput, 'You must provide content when creating a new entry'
177
177
  end
178
178
  end
179
179
  end
@@ -291,7 +291,7 @@ command :note do |c|
291
291
  new_note = Doing::Note.new
292
292
 
293
293
  if options[:e] || (args.empty? && $stdin.stat.size.zero? && !options[:r])
294
- raise Doing::Errors::MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
294
+ raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
295
295
 
296
296
  input = !args.empty? ? args.join(' ') : ''
297
297
 
@@ -312,7 +312,7 @@ command :note do |c|
312
312
  elsif $stdin.stat.size.positive?
313
313
  new_note.add($stdin.read)
314
314
  else
315
- raise Doing::Errors::EmptyInput, 'You must provide content when adding a note' unless options[:remove]
315
+ raise EmptyInput, 'You must provide content when adding a note' unless options[:remove]
316
316
  end
317
317
 
318
318
  if last_note.equal?(new_note)
@@ -338,7 +338,7 @@ command :meanwhile do |c|
338
338
  c.switch %i[e editor], negatable: false, default_value: false
339
339
 
340
340
  c.desc 'Archive previous @meanwhile entry'
341
- c.switch %i[a archive], default_value: false
341
+ c.switch %i[a archive], negatable: false, default_value: false
342
342
 
343
343
  c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
344
344
  c.arg_name 'DATE_STRING'
@@ -352,7 +352,7 @@ command :meanwhile do |c|
352
352
  if options[:back]
353
353
  date = wwid.chronify(options[:back], guess: :begin)
354
354
 
355
- raise Doing::Errors::InvalidTimeExpression, 'Unable to parse date string' if date.nil?
355
+ raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
356
356
  else
357
357
  date = Time.now
358
358
  end
@@ -365,7 +365,7 @@ command :meanwhile do |c|
365
365
  input = ''
366
366
 
367
367
  if options[:e]
368
- raise Doing::Errors::MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
368
+ raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
369
369
 
370
370
  input += args.join(' ') unless args.empty?
371
371
  input = wwid.fork_editor(input).strip
@@ -403,7 +403,7 @@ long_desc %(
403
403
  arg_name 'TYPE', must_match: Doing::Plugins.template_regex
404
404
  command :template do |c|
405
405
  c.desc 'List all available templates'
406
- c.switch %i[l list]
406
+ c.switch %i[l list], negatable: false
407
407
 
408
408
  c.desc 'List in single column for completion'
409
409
  c.switch %i[c]
@@ -424,7 +424,7 @@ command :template do |c|
424
424
  type = args[0]
425
425
  end
426
426
 
427
- raise Doing::Errors::InvalidPluginType, "No type specified, use `doing template [#{Doing::Plugins.plugin_templates.join('|')}]`" unless type
427
+ raise InvalidPluginType, "No type specified, use `doing template [#{Doing::Plugins.plugin_templates.join('|')}]`" unless type
428
428
 
429
429
  $stdout.puts Doing::Plugins.template_for_trigger(type)
430
430
 
@@ -503,12 +503,12 @@ command :select do |c|
503
503
  c.flag %i[o output]
504
504
 
505
505
  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]
506
+ c.switch %i[again resume], negatable: false, default_value: false
507
507
 
508
508
  c.action do |_global_options, options, args|
509
- raise InvalidExportType, "Invalid export type: #{options[:output]}" if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
509
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
510
510
 
511
- raise Doing::Errors::InvalidArgument, '--no-menu requires --query' if !options[:menu] && !options[:query]
511
+ raise InvalidArgument, '--no-menu requires --query' if !options[:menu] && !options[:query]
512
512
 
513
513
  wwid.interactive(options)
514
514
  end
@@ -531,17 +531,17 @@ command :later do |c|
531
531
  c.action do |_global_options, options, args|
532
532
  if options[:back]
533
533
  date = wwid.chronify(options[:back], guess: :begin)
534
- raise Doing::Errors::InvalidTimeExpression, 'Unable to parse date string' if date.nil?
534
+ raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
535
535
  else
536
536
  date = Time.now
537
537
  end
538
538
 
539
539
  if options[:editor] || (args.empty? && $stdin.stat.size.zero?)
540
- raise Doing::Errors::MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
540
+ raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
541
541
 
542
542
  input = args.empty? ? '' : args.join(' ')
543
543
  input = wwid.fork_editor(input).strip
544
- raise Doing::Errors::EmptyInput, 'No content' unless input && !input.empty?
544
+ raise EmptyInput, 'No content' unless input && !input.empty?
545
545
 
546
546
  title, note = wwid.format_input(input)
547
547
  note.push(options[:n]) if options[:n]
@@ -558,7 +558,7 @@ command :later do |c|
558
558
  wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
559
559
  wwid.write(wwid.doing_file)
560
560
  else
561
- raise Doing::Errors::EmptyInput, 'You must provide content when creating a new entry'
561
+ raise EmptyInput, 'You must provide content when creating a new entry'
562
562
  end
563
563
  end
564
564
  end
@@ -614,19 +614,19 @@ command %i[done did] do |c|
614
614
 
615
615
  if options[:took]
616
616
  took = wwid.chronify_qty(options[:took])
617
- raise Doing::Errors::InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
617
+ raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
618
618
  end
619
619
 
620
620
  if options[:back]
621
621
  date = wwid.chronify(options[:back], guess: :begin)
622
- raise Doing::Errors::InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
622
+ raise InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
623
623
  else
624
624
  date = options[:took] ? Time.now - took : Time.now
625
625
  end
626
626
 
627
627
  if options[:at]
628
628
  finish_date = wwid.chronify(options[:at], guess: :begin)
629
- raise Doing::Errors::InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
629
+ raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
630
630
 
631
631
  date = options[:took] ? finish_date - took : finish_date
632
632
  elsif options[:took]
@@ -651,7 +651,7 @@ command %i[done did] do |c|
651
651
  note.add(options[:note]) if options[:note]
652
652
 
653
653
  if options[:editor]
654
- raise Doing::Errors::MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
654
+ raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
655
655
  is_new = false
656
656
 
657
657
  if args.empty?
@@ -659,7 +659,7 @@ command %i[done did] do |c|
659
659
 
660
660
  unless last_entry
661
661
  Doing.logger.debug('Skipped:', options[:unfinished] ? 'No unfinished entry' : 'Last entry already @done')
662
- raise Doing::Errors::NoResults, 'No results'
662
+ raise NoResults, 'No results'
663
663
  end
664
664
 
665
665
  old_entry = last_entry.dup
@@ -671,7 +671,7 @@ command %i[done did] do |c|
671
671
  end
672
672
 
673
673
  input = wwid.fork_editor(input).strip
674
- raise Doing::Errors::EmptyInput, 'No content' unless input && !input.empty?
674
+ raise EmptyInput, 'No content' unless input && !input.empty?
675
675
 
676
676
  title, note = wwid.format_input(input)
677
677
  new_entry = Doing::Item.new(date, title, section, note)
@@ -746,7 +746,7 @@ command %i[done did] do |c|
746
746
  wwid.write(wwid.doing_file)
747
747
  Doing.logger.info('Entry Added:', new_entry.title)
748
748
  else
749
- raise Doing::Errors::EmptyInput, 'You must provide content when creating a new entry'
749
+ raise EmptyInput, 'You must provide content when creating a new entry'
750
750
  end
751
751
  end
752
752
  end
@@ -789,9 +789,9 @@ command :cancel do |c|
789
789
  tags = options[:tag].to_tags
790
790
  end
791
791
 
792
- raise Doing::Errors::InvalidArgument, 'Only one argument allowed' if args.length > 1
792
+ raise InvalidArgument, 'Only one argument allowed' if args.length > 1
793
793
 
794
- raise Doing::Errors::InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)' unless args.empty? || args[0] =~ /\d+/
794
+ raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)' unless args.empty? || args[0] =~ /\d+/
795
795
 
796
796
  if options[:interactive]
797
797
  count = 0
@@ -873,22 +873,22 @@ command :finish do |c|
873
873
  unless options[:auto]
874
874
  if options[:took]
875
875
  took = wwid.chronify_qty(options[:took])
876
- raise Doing::Errors::InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
876
+ raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?
877
877
  end
878
878
 
879
- raise Doing::Errors::InvalidArgument, '--back and --took can not be used together' if options[:back] && options[:took]
879
+ raise InvalidArgument, '--back and --took can not be used together' if options[:back] && options[:took]
880
880
 
881
- raise Doing::Errors::InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
881
+ raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
882
882
 
883
883
  if options[:at]
884
884
  finish_date = wwid.chronify(options[:at], guess: :begin)
885
- raise Doing::Errors::InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
885
+ raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?
886
886
 
887
887
  date = options[:took] ? finish_date - took : finish_date
888
888
  elsif options[:back]
889
889
  date = wwid.chronify(options[:back])
890
890
 
891
- raise Doing::Errors::InvalidTimeExpression, 'Unable to parse date string' if date.nil?
891
+ raise InvalidTimeExpression, 'Unable to parse date string' if date.nil?
892
892
  elsif options[:took]
893
893
  date = wwid.chronify_qty(options[:took])
894
894
  else
@@ -902,9 +902,9 @@ command :finish do |c|
902
902
  tags = options[:tag].to_tags
903
903
  end
904
904
 
905
- raise Doing::Errors::InvalidArgument, 'Only one argument allowed' if args.length > 1
905
+ raise InvalidArgument, 'Only one argument allowed' if args.length > 1
906
906
 
907
- raise Doing::Errors::InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)' unless args.length == 0 || args[0] =~ /\d+/
907
+ raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)' unless args.length == 0 || args[0] =~ /\d+/
908
908
 
909
909
  if options[:interactive]
910
910
  count = 0
@@ -1045,9 +1045,9 @@ command :tag do |c|
1045
1045
  c.switch %i[i interactive], negatable: false, default_value: false
1046
1046
 
1047
1047
  c.action do |_global_options, options, args|
1048
- raise Doing::Errors::MissingArgument, 'You must specify at least one tag' if args.empty? && !options[:a]
1048
+ raise MissingArgument, 'You must specify at least one tag' if args.empty? && !options[:autotag]
1049
1049
 
1050
- raise Doing::Errors::InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
1050
+ raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
1051
1051
 
1052
1052
  section = 'All'
1053
1053
 
@@ -1117,19 +1117,6 @@ command :tag do |c|
1117
1117
  end
1118
1118
  end
1119
1119
 
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
1120
  desc 'Mark last entry as flagged'
1134
1121
  command [:mark, :flag] do |c|
1135
1122
  c.example 'doing flag', desc: 'Add @flagged to the last entry created'
@@ -1175,7 +1162,7 @@ command [:mark, :flag] do |c|
1175
1162
  c.action do |_global_options, options, _args|
1176
1163
  mark = settings['marker_tag'] || 'flagged'
1177
1164
 
1178
- raise Doing::Errors::InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
1165
+ raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
1179
1166
 
1180
1167
  section = 'All'
1181
1168
 
@@ -1307,8 +1294,8 @@ command :show do |c|
1307
1294
  c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
1308
1295
  c.arg_name 'FORMAT'
1309
1296
  c.flag %i[o output]
1310
- c.action do |_global_options, options, args|
1311
- raise InvalidExportType, "Invalid export type: #{options[:output]}" if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1297
+ c.action do |global_options, options, args|
1298
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1312
1299
 
1313
1300
  tag_filter = false
1314
1301
  tags = []
@@ -1323,8 +1310,15 @@ command :show do |c|
1323
1310
  when /^@/
1324
1311
  section = 'All'
1325
1312
  else
1326
- section = wwid.guess_section(args[0])
1327
- raise Doing::Errors::InvalidSection, "No such section: #{args[0]}" unless section
1313
+ begin
1314
+ section = wwid.guess_section(args[0])
1315
+ rescue WrongCommand => exception
1316
+ cmd = commands[:view]
1317
+ action = cmd.send(:get_action, nil)
1318
+ return action.call(global_options, options, args)
1319
+ end
1320
+
1321
+ raise InvalidSection, "No such section: #{args[0]}" unless section
1328
1322
 
1329
1323
  args.shift
1330
1324
  end
@@ -1359,7 +1353,7 @@ command :show do |c|
1359
1353
  start = wwid.chronify(date_string, guess: :begin)
1360
1354
  finish = false
1361
1355
  end
1362
- raise Doing::Errors::InvalidTimeExpression, 'Unrecognized date string' unless start
1356
+ raise InvalidTimeExpression, 'Unrecognized date string' unless start
1363
1357
  dates = [start, finish]
1364
1358
  end
1365
1359
 
@@ -1433,7 +1427,7 @@ command %i[grep search] do |c|
1433
1427
  c.switch %i[i interactive], default_value: false, negatable: false
1434
1428
 
1435
1429
  c.action do |_global_options, options, args|
1436
- raise InvalidExportType, "Invalid export type: #{options[:output]}" if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1430
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1437
1431
 
1438
1432
  tags_color = settings.key?('tags_color') ? settings['tags_color'] : nil
1439
1433
 
@@ -1548,7 +1542,7 @@ command :today do |c|
1548
1542
  c.flag [:after]
1549
1543
 
1550
1544
  c.action do |_global_options, options, _args|
1551
- raise InvalidExportType, "Invalid export type: #{options[:output]}" if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1545
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1552
1546
 
1553
1547
  options[:t] = true if options[:totals]
1554
1548
  options[:sort_tags] = options[:tag_sort] =~ /^n/i
@@ -1595,9 +1589,9 @@ command :on do |c|
1595
1589
  c.flag %i[o output]
1596
1590
 
1597
1591
  c.action do |_global_options, options, args|
1598
- raise InvalidExportType, "Invalid export type: #{options[:output]}" if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1592
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1599
1593
 
1600
- raise Doing::Errors::MissingArgument, 'Missing date argument' if args.empty?
1594
+ raise MissingArgument, 'Missing date argument' if args.empty?
1601
1595
 
1602
1596
  date_string = args.join(' ')
1603
1597
 
@@ -1610,7 +1604,7 @@ command :on do |c|
1610
1604
  finish = false
1611
1605
  end
1612
1606
 
1613
- raise Doing::Errors::InvalidTimeExpression, 'Unrecognized date string' unless start
1607
+ raise InvalidTimeExpression, 'Unrecognized date string' unless start
1614
1608
 
1615
1609
  message = "Date interpreted as #{start}"
1616
1610
  message += " to #{finish}" if finish
@@ -1653,9 +1647,9 @@ command :since do |c|
1653
1647
  c.flag %i[o output]
1654
1648
 
1655
1649
  c.action do |_global_options, options, args|
1656
- raise InvalidExportType, "Invalid export type: #{options[:output]}" if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1650
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1657
1651
 
1658
- raise Doing::Errors::MissingArgument, 'Missing date argument' if args.empty?
1652
+ raise MissingArgument, 'Missing date argument' if args.empty?
1659
1653
 
1660
1654
  date_string = args.join(' ')
1661
1655
 
@@ -1665,7 +1659,7 @@ command :since do |c|
1665
1659
  start = wwid.chronify(date_string, guess: :begin)
1666
1660
  finish = Time.now
1667
1661
 
1668
- raise Doing::Errors::InvalidTimeExpression, 'Unrecognized date string' unless start
1662
+ raise InvalidTimeExpression, 'Unrecognized date string' unless start
1669
1663
 
1670
1664
  Doing.logger.debug("Date interpreted as #{start} through the current time")
1671
1665
 
@@ -1698,7 +1692,6 @@ command :yesterday do |c|
1698
1692
  c.switch [:totals], default_value: false, negatable: false
1699
1693
 
1700
1694
  c.desc 'Sort tags by (name|time)'
1701
- default = 'time'
1702
1695
  default = settings['tag_sort'] || 'name'
1703
1696
  c.arg_name 'KEY'
1704
1697
  c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
@@ -1716,7 +1709,7 @@ command :yesterday do |c|
1716
1709
  c.flag [:tag_order], must_match: REGEX_SORT_ORDER, default_value: 'asc'
1717
1710
 
1718
1711
  c.action do |_global_options, options, _args|
1719
- raise InvalidExportType, "Invalid export type: #{options[:output]}" if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1712
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1720
1713
 
1721
1714
  options[:sort_tags] = options[:tag_sort] =~ /^n/i
1722
1715
 
@@ -1762,7 +1755,7 @@ command :last do |c|
1762
1755
  c.flag [:search]
1763
1756
 
1764
1757
  c.action do |global_options, options, _args|
1765
- raise Doing::Errors::InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
1758
+ raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
1766
1759
 
1767
1760
  if options[:tag].nil?
1768
1761
  tags = []
@@ -1791,7 +1784,7 @@ end
1791
1784
  desc 'List sections'
1792
1785
  command :sections do |c|
1793
1786
  c.desc 'List in single column'
1794
- c.switch %i[c column], default_value: false
1787
+ c.switch %i[c column], negatable: false, default_value: false
1795
1788
 
1796
1789
  c.action do |_global_options, options, _args|
1797
1790
  joiner = options[:c] ? "\n" : "\t"
@@ -1814,7 +1807,7 @@ command :add_section do |c|
1814
1807
  c.example 'doing add_section Ideas', desc: 'Add a section called Ideas to the doing file'
1815
1808
 
1816
1809
  c.action do |_global_options, _options, args|
1817
- raise Doing::Errors::InvalidArgument, "Section #{args[0]} already exists" if wwid.sections.include?(args[0])
1810
+ raise InvalidArgument, "Section #{args[0]} already exists" if wwid.sections.include?(args[0])
1818
1811
 
1819
1812
  wwid.add_section(args.join(' ').cap_first)
1820
1813
  wwid.write(wwid.doing_file)
@@ -1853,16 +1846,54 @@ command :plugins do |c|
1853
1846
 
1854
1847
  c.desc 'List plugins of type (import, export)'
1855
1848
  c.arg_name 'TYPE'
1856
- c.flag %i[t type], must_match: /^[iea].*$/i, default_value: 'all'
1849
+ c.flag %i[t type], must_match: /^(?:[iea].*)$/i, default_value: 'all'
1857
1850
 
1858
1851
  c.desc 'List in single column for completion'
1859
- c.switch %i[c column], default_value: false
1852
+ c.switch %i[c column], negatable: false, default_value: false
1860
1853
 
1861
1854
  c.action do |_global_options, options, _args|
1862
1855
  Doing::Plugins.list_plugins(options)
1863
1856
  end
1864
1857
  end
1865
1858
 
1859
+ desc 'Generate shell completion scripts'
1860
+ command :completion do |c|
1861
+ c.example 'doing completion', desc: 'Output zsh (default) to STDOUT'
1862
+ c.example 'doing completion --type zsh --file ~/.zsh-completions/_doing.zsh', desc: 'Output zsh completions to file'
1863
+ c.example 'doing completion --type fish --file ~/.config/fish/completions/doing.fish', desc: 'Output fish completions to file'
1864
+ c.example 'doing completion --type bash --file ~/.bash_it/completion/enabled/doing.bash', desc: 'Output bash completions to file'
1865
+
1866
+ c.desc 'Shell to generate for (bash, zsh, fish)'
1867
+ c.arg_name 'SHELL'
1868
+ c.flag %i[t type], must_match: /^[bzf](?:[ai]?sh)?$/i, default_value: 'zsh'
1869
+
1870
+ c.desc 'File to write output to'
1871
+ c.arg_name 'PATH'
1872
+ c.flag %i[f file], default_value: 'stdout'
1873
+
1874
+ c.action do |_global_options, options, _args|
1875
+ script_dir = File.join(File.dirname(__FILE__), '..', 'scripts')
1876
+
1877
+ case options[:type]
1878
+ when /^b/
1879
+ result = `ruby #{File.join(script_dir, 'generate_bash_completions.rb')}`
1880
+ when /^z/
1881
+ result = `ruby #{File.join(script_dir, 'generate_zsh_completions.rb')}`
1882
+ when /^f/
1883
+ result = `ruby #{File.join(script_dir, 'generate_fish_completions.rb')}`
1884
+ end
1885
+
1886
+ if options[:file] =~ /^stdout$/i
1887
+ $stdout.puts result
1888
+ else
1889
+ File.open(File.expand_path(options[:file]), 'w') do |f|
1890
+ f.puts result
1891
+ end
1892
+ Doing.logger.warn('File written:', "#{options[:type]} completions written to #{options[:file]}")
1893
+ end
1894
+ end
1895
+ end
1896
+
1866
1897
  desc 'Display a user-created view'
1867
1898
  long_desc 'Command line options override view configuration'
1868
1899
  arg_name 'VIEW_NAME'
@@ -1925,15 +1956,22 @@ command :view do |c|
1925
1956
  c.desc 'Select from a menu of matching entries to perform additional operations'
1926
1957
  c.switch %i[i interactive], negatable: false, default_value: false
1927
1958
 
1928
- c.action do |_global_options, options, args|
1929
- raise InvalidExportType, "Invalid export type: #{options[:output]}" if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1959
+ c.action do |global_options, options, args|
1960
+ raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
1930
1961
 
1931
- raise Doing::Errors::InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
1962
+ raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
1932
1963
 
1933
1964
  title = if args.empty?
1934
1965
  wwid.choose_view
1935
1966
  else
1936
- wwid.guess_view(args[0])
1967
+ begin
1968
+ wwid.guess_view(args[0])
1969
+ rescue WrongCommand => exception
1970
+ cmd = commands[:show]
1971
+ options[:sort] = 'asc'
1972
+ action = cmd.send(:get_action, nil)
1973
+ return action.call(global_options, options, args)
1974
+ end
1937
1975
  end
1938
1976
 
1939
1977
  if options[:section]
@@ -2023,7 +2061,7 @@ command :view do |c|
2023
2061
  start = wwid.chronify(date_string, guess: :begin)
2024
2062
  finish = false
2025
2063
  end
2026
- raise Doing::Errors::InvalidTimeExpression, 'Unrecognized date string' unless start
2064
+ raise InvalidTimeExpression, 'Unrecognized date string' unless start
2027
2065
  dates = [start, finish]
2028
2066
  end
2029
2067
 
@@ -2046,9 +2084,9 @@ command :view do |c|
2046
2084
 
2047
2085
  Doing::Pager.page wwid.list_section(opts)
2048
2086
  elsif title.instance_of?(FalseClass)
2049
- exit_now! 'Cancelled'
2087
+ raise UserCancelled, 'Cancelled' unless res
2050
2088
  else
2051
- raise Doing::Errors::InvalidView, "View #{title} not found in config"
2089
+ raise InvalidView, "View #{title} not found in config"
2052
2090
  end
2053
2091
  end
2054
2092
  end
@@ -2119,7 +2157,7 @@ command %i[archive move] do |c|
2119
2157
  tags = args.length > 1 ? args[1..].map { |t| t.sub(/^@/, '').strip } : []
2120
2158
  end
2121
2159
 
2122
- raise Doing::Errors::InvalidArgument, '--keep and --count can not be used together' if options[:keep] && options[:count]
2160
+ raise InvalidArgument, '--keep and --count can not be used together' if options[:keep] && options[:count]
2123
2161
 
2124
2162
  tags.concat(options[:tag].to_tags) if options[:tag]
2125
2163
 
@@ -2208,7 +2246,7 @@ command :open do |c|
2208
2246
  system %(open "#{File.expand_path(wwid.doing_file)}")
2209
2247
  end
2210
2248
  else
2211
- raise Doing::Errors::MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
2249
+ raise MissingEditor, 'No EDITOR variable defined in environment' if Doing::Util.default_editor.nil?
2212
2250
 
2213
2251
  system %(#{Doing::Util.default_editor} "#{File.expand_path(wwid.doing_file)}")
2214
2252
  end
@@ -2236,7 +2274,7 @@ command :config do |c|
2236
2274
  c.flag %i[e editor], default_value: nil
2237
2275
 
2238
2276
  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]
2277
+ c.switch %i[d dump], negatable: false
2240
2278
 
2241
2279
  c.desc 'Format for --dump (json|yaml|raw)'
2242
2280
  c.arg_name 'FORMAT'
@@ -2261,7 +2299,6 @@ command :config do |c|
2261
2299
  c.action do |_global_options, options, args|
2262
2300
  if options[:update]
2263
2301
  config.configure({rewrite: true, ignore_local: true})
2264
- # Doing.logger.warn("Config file rewritten: #{config.config_file}")
2265
2302
  return
2266
2303
  end
2267
2304
 
@@ -2276,7 +2313,6 @@ command :config do |c|
2276
2313
  when /^r/
2277
2314
  cfg
2278
2315
  else
2279
- # cfg = { last_key => cfg } unless last_key.nil?
2280
2316
  YAML.dump(cfg)
2281
2317
  end
2282
2318
  else
@@ -2291,7 +2327,7 @@ command :config do |c|
2291
2327
  choices.concat(config.additional_configs)
2292
2328
  res = wwid.choose_from(choices.uniq.sort.reverse, sorted: false, prompt: 'Local configs found, select which to edit > ')
2293
2329
 
2294
- raise Doing::Errors::UserCancelled, 'Cancelled' unless res
2330
+ raise UserCancelled, 'Cancelled' unless res
2295
2331
 
2296
2332
  config_file = res.strip || config.config_file
2297
2333
  else
@@ -2308,7 +2344,7 @@ command :config do |c|
2308
2344
  `open -a "#{editor}" "#{config_file}"`
2309
2345
  end
2310
2346
  else
2311
- raise Doing::Errors::InvalidArgument, 'No viable editor found in config or environment.'
2347
+ raise InvalidArgument, 'No viable editor found in config or environment.'
2312
2348
  end
2313
2349
  elsif options[:a] || options[:b]
2314
2350
  if options[:a]
@@ -2319,7 +2355,7 @@ command :config do |c|
2319
2355
  else
2320
2356
  editor = options[:e] || Doing::Util.find_default_editor('config')
2321
2357
 
2322
- raise Doing::Errors::MissingEditor, 'No viable editor defined in config or environment' unless editor
2358
+ raise MissingEditor, 'No viable editor defined in config or environment' unless editor
2323
2359
 
2324
2360
  if Doing::Util.exec_available(editor)
2325
2361
  system %(#{editor} "#{config_file}")
@@ -2329,7 +2365,7 @@ command :config do |c|
2329
2365
  end
2330
2366
  else
2331
2367
  editor = options[:e] || Doing::Util.default_editor
2332
- raise Doing::Errors::MissingEditor, 'No EDITOR variable defined in environment' unless editor && Doing::Util.exec_available(editor)
2368
+ raise MissingEditor, 'No EDITOR variable defined in environment' unless editor && Doing::Util.exec_available(editor)
2333
2369
 
2334
2370
  system %(#{editor} "#{config_file}")
2335
2371
  end
@@ -2413,7 +2449,7 @@ command :import do |c|
2413
2449
  start = wwid.chronify(date_string, guess: :begin)
2414
2450
  finish = false
2415
2451
  end
2416
- raise Doing::Errors::InvalidTimeExpression, 'Unrecognized date string' unless start
2452
+ raise InvalidTimeExpression, 'Unrecognized date string' unless start
2417
2453
  dates = [start, finish]
2418
2454
  end
2419
2455
 
@@ -2423,7 +2459,7 @@ command :import do |c|
2423
2459
  wwid.import(args, options)
2424
2460
  wwid.write(wwid.doing_file)
2425
2461
  else
2426
- raise Doing::Errors::InvalidPluginType, "Invalid import type: #{options[:type]}"
2462
+ raise InvalidPluginType, "Invalid import type: #{options[:type]}"
2427
2463
  end
2428
2464
  end
2429
2465
  end
@@ -2448,14 +2484,13 @@ pre do |global, _command, _options, _args|
2448
2484
  end
2449
2485
 
2450
2486
  on_error do |exception|
2451
- # if exception.kind_of?(SystemExit)
2452
- # false
2453
- # else
2454
- # p exception.inspect
2455
- # Doing.logger.output_results
2456
- # true
2457
- # end
2458
- false
2487
+ if exception.kind_of?(SystemExit)
2488
+ false
2489
+ else
2490
+ # Doing.logger.error('Fatal:', exception)
2491
+ Doing.logger.output_results
2492
+ true
2493
+ end
2459
2494
  end
2460
2495
 
2461
2496
  post do |global, _command, _options, _args|