doing 2.1.4pre → 2.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +14 -13
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/.yardopts +1 -1
  6. data/CHANGELOG.md +37 -1
  7. data/Gemfile.lock +3 -1
  8. data/README.md +5 -1
  9. data/bin/doing +192 -95
  10. data/docs/_config.yml +1 -0
  11. data/{doc → docs/doc}/Array.html +63 -1
  12. data/docs/doc/BooleanTermParser/Clause.html +293 -0
  13. data/docs/doc/BooleanTermParser/Operator.html +172 -0
  14. data/docs/doc/BooleanTermParser/Query.html +417 -0
  15. data/docs/doc/BooleanTermParser/QueryParser.html +135 -0
  16. data/docs/doc/BooleanTermParser/QueryTransformer.html +124 -0
  17. data/docs/doc/BooleanTermParser.html +115 -0
  18. data/docs/doc/Doing/CLIFormat.html +131 -0
  19. data/{doc → docs/doc}/Doing/Color.html +2 -2
  20. data/{doc → docs/doc}/Doing/Completion.html +1 -1
  21. data/{doc → docs/doc}/Doing/Configuration.html +117 -12
  22. data/{doc → docs/doc}/Doing/Content.html +0 -0
  23. data/{doc → docs/doc}/Doing/Errors/DoingNoTraceError.html +1 -1
  24. data/{doc → docs/doc}/Doing/Errors/DoingRuntimeError.html +1 -1
  25. data/{doc → docs/doc}/Doing/Errors/DoingStandardError.html +1 -1
  26. data/{doc → docs/doc}/Doing/Errors/EmptyInput.html +1 -1
  27. data/{doc → docs/doc}/Doing/Errors/NoResults.html +1 -1
  28. data/{doc → docs/doc}/Doing/Errors/PluginException.html +1 -1
  29. data/{doc → docs/doc}/Doing/Errors/UserCancelled.html +1 -1
  30. data/{doc → docs/doc}/Doing/Errors/WrongCommand.html +1 -1
  31. data/{doc → docs/doc}/Doing/Errors.html +1 -1
  32. data/{doc → docs/doc}/Doing/Hooks.html +1 -1
  33. data/{doc → docs/doc}/Doing/Item.html +100 -73
  34. data/{doc → docs/doc}/Doing/Items.html +2 -2
  35. data/{doc → docs/doc}/Doing/LogAdapter.html +70 -1
  36. data/{doc → docs/doc}/Doing/Note.html +5 -134
  37. data/{doc → docs/doc}/Doing/Pager.html +1 -1
  38. data/{doc → docs/doc}/Doing/Plugins.html +431 -35
  39. data/{doc → docs/doc}/Doing/Prompt.html +1 -1
  40. data/{doc → docs/doc}/Doing/Section.html +1 -1
  41. data/docs/doc/Doing/TemplateString.html +713 -0
  42. data/docs/doc/Doing/Util/Backup.html +686 -0
  43. data/{doc → docs/doc}/Doing/Util.html +1 -1
  44. data/{doc → docs/doc}/Doing/WWID.html +5 -5
  45. data/{doc → docs/doc}/Doing/WWIDFile.html +0 -0
  46. data/{doc → docs/doc}/Doing.html +4 -4
  47. data/{doc → docs/doc}/GLI/Commands/MarkdownDocumentListener.html +1 -1
  48. data/{doc → docs/doc}/GLI/Commands.html +1 -1
  49. data/{doc → docs/doc}/GLI.html +1 -1
  50. data/{doc → docs/doc}/Hash.html +1 -1
  51. data/docs/doc/PhraseParser/Operator.html +172 -0
  52. data/docs/doc/PhraseParser/PhraseClause.html +303 -0
  53. data/docs/doc/PhraseParser/Query.html +495 -0
  54. data/docs/doc/PhraseParser/QueryParser.html +136 -0
  55. data/docs/doc/PhraseParser/QueryTransformer.html +124 -0
  56. data/docs/doc/PhraseParser/TermClause.html +293 -0
  57. data/docs/doc/PhraseParser.html +115 -0
  58. data/{doc → docs/doc}/Status.html +1 -1
  59. data/{doc → docs/doc}/String.html +182 -12
  60. data/{doc → docs/doc}/Symbol.html +35 -1
  61. data/{doc → docs/doc}/Time.html +1 -1
  62. data/{doc → docs/doc}/_index.html +21 -14
  63. data/{doc → docs/doc}/class_list.html +1 -1
  64. data/{doc → docs/doc}/css/common.css +0 -0
  65. data/{doc → docs/doc}/css/full_list.css +0 -0
  66. data/{doc → docs/doc}/css/style.css +0 -0
  67. data/{doc → docs/doc}/file.README.html +6 -2
  68. data/{doc → docs/doc}/file_list.html +0 -0
  69. data/{doc → docs/doc}/frames.html +0 -0
  70. data/{doc → docs/doc}/index.html +6 -2
  71. data/{doc → docs/doc}/js/app.js +0 -0
  72. data/{doc → docs/doc}/js/full_list.js +0 -0
  73. data/{doc → docs/doc}/js/jquery.js +0 -0
  74. data/{doc → docs/doc}/method_list.html +313 -161
  75. data/{doc → docs/doc}/top-level-namespace.html +1 -1
  76. data/docs/index.md +60 -0
  77. data/doing.gemspec +1 -0
  78. data/doing.rdoc +74 -15
  79. data/example_plugin.rb +3 -1
  80. data/lib/completion/_doing.zsh +53 -41
  81. data/lib/completion/doing.bash +17 -6
  82. data/lib/completion/doing.fish +321 -2
  83. data/lib/doing/array.rb +9 -0
  84. data/lib/doing/completion/fish_completion.rb +46 -3
  85. data/lib/doing/completion/zsh_completion.rb +1 -1
  86. data/lib/doing/configuration.rb +33 -11
  87. data/lib/doing/item.rb +12 -3
  88. data/lib/doing/log_adapter.rb +28 -0
  89. data/lib/doing/note.rb +31 -30
  90. data/lib/doing/plugin_manager.rb +84 -21
  91. data/lib/doing/plugins/export/dayone_export.rb +209 -0
  92. data/lib/doing/plugins/export/html_export.rb +2 -2
  93. data/lib/doing/plugins/export/json_export.rb +1 -0
  94. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  95. data/lib/doing/plugins/export/template_export.rb +90 -85
  96. data/lib/doing/prompt.rb +9 -6
  97. data/lib/doing/string.rb +68 -27
  98. data/lib/doing/symbol.rb +4 -0
  99. data/lib/doing/template_string.rb +197 -0
  100. data/lib/doing/util.rb +4 -2
  101. data/lib/doing/util_backup.rb +55 -3
  102. data/lib/doing/version.rb +1 -1
  103. data/lib/doing/wwid.rb +37 -22
  104. data/lib/doing.rb +3 -0
  105. data/lib/examples/plugins/say_export.rb +1 -1
  106. data/lib/examples/plugins/wiki_export/wiki_export.rb +3 -3
  107. data/lib/templates/doing-dayone-entry.erb +6 -0
  108. data/lib/templates/doing-dayone.erb +5 -0
  109. metadata +95 -53
data/bin/doing CHANGED
@@ -51,11 +51,16 @@ if ENV['DOING_LOG_LEVEL'] || ENV['DOING_DEBUG'] || ENV['DOING_QUIET'] || ENV['DO
51
51
  end
52
52
  end
53
53
 
54
+ Doing.logger.benchmark(:total, :start)
55
+
54
56
  if ENV['DOING_CONFIG']
55
57
  Doing.config_with(ENV['DOING_CONFIG'], { ignore_local: true })
56
58
  end
57
59
 
60
+ Doing.logger.benchmark(:configure, :start)
58
61
  config = Doing.config
62
+ Doing.logger.benchmark(:configure, :finish)
63
+
59
64
  settings = config.settings
60
65
  wwid.config = settings
61
66
 
@@ -224,14 +229,14 @@ command %i[reset begin] do |c|
224
229
  # c.switch [:fuzzy], default_value: false, negatable: false
225
230
 
226
231
  c.desc 'Force exact search string matching (case sensitive)'
227
- c.switch %i[x exact], default_value: false, negatable: false
232
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
228
233
 
229
234
  c.desc 'Reset items that *don\'t* match search/tag filters'
230
235
  c.switch [:not], default_value: false, negatable: false
231
236
 
232
237
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
233
238
  c.arg_name 'TYPE'
234
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
239
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
235
240
 
236
241
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
237
242
  c.arg_name 'BOOLEAN'
@@ -329,14 +334,14 @@ command :note do |c|
329
334
  # c.switch [:fuzzy], default_value: false, negatable: false
330
335
 
331
336
  c.desc 'Force exact search string matching (case sensitive)'
332
- c.switch %i[x exact], default_value: false, negatable: false
337
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
333
338
 
334
339
  c.desc 'Add note to item that *doesn\'t* match search/tag filters'
335
340
  c.switch [:not], default_value: false, negatable: false
336
341
 
337
342
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
338
343
  c.arg_name 'TYPE'
339
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
344
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
340
345
 
341
346
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans.'
342
347
  c.arg_name 'BOOLEAN'
@@ -503,6 +508,13 @@ command :template do |c|
503
508
  c.desc 'List in single column for completion'
504
509
  c.switch %i[c column]
505
510
 
511
+ c.desc 'Save template to file instead of STDOUT'
512
+ c.switch %i[s save], default_value: false, negatable: false
513
+
514
+ c.desc 'Save template to alternate location'
515
+ c.arg_name 'DIRECTORY'
516
+ c.flag %i[p path], default_value: File.join(Doing::Util.user_home, '.config', 'doing', 'templates')
517
+
506
518
  c.action do |_global_options, options, args|
507
519
  if options[:list] || options[:column]
508
520
  if options[:column]
@@ -515,13 +527,19 @@ command :template do |c|
515
527
 
516
528
  if args.empty?
517
529
  type = Doing::Prompt.choose_from(Doing::Plugins.plugin_templates, sorted: false, prompt: 'Select template type > ')
530
+ type.sub!(/ \(.*?\)$/, '').strip!
531
+ options[:save] = Doing::Prompt.yn("Save to #{options[:path]}? (No outputs to STDOUT)", default_response: false)
518
532
  else
519
533
  type = args[0]
520
534
  end
521
535
 
522
536
  raise InvalidPluginType, "No type specified, use `doing template [#{Doing::Plugins.plugin_templates.join('|')}]`" unless type
523
537
 
524
- $stdout.puts Doing::Plugins.template_for_trigger(type)
538
+ if options[:save]
539
+ Doing::Plugins.template_for_trigger(type, save_to: options[:path])
540
+ else
541
+ $stdout.puts Doing::Plugins.template_for_trigger(type, save_to: nil)
542
+ end
525
543
 
526
544
  # case args[0]
527
545
  # when /html|haml/i
@@ -599,14 +617,14 @@ command :select do |c|
599
617
  c.flag [:from]
600
618
 
601
619
  c.desc 'Force exact search string matching (case sensitive)'
602
- c.switch %i[x exact], default_value: false, negatable: false
620
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
603
621
 
604
622
  c.desc 'Select items that *don\'t* match search/tag filters'
605
623
  c.switch [:not], default_value: false, negatable: false
606
624
 
607
625
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
608
626
  c.arg_name 'TYPE'
609
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
627
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
610
628
 
611
629
  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.'
612
630
  c.switch %i[menu], negatable: true, default_value: true
@@ -711,7 +729,7 @@ command :later do |c|
711
729
  end
712
730
 
713
731
  desc 'Add a completed item with @done(date). No argument finishes last entry.'
714
- desc 'Use this command to add an entry after you\'ve already finished it. It will be immediately marked as @done.
732
+ long_desc 'Use this command to add an entry after you\'ve already finished it. It will be immediately marked as @done.
715
733
  You can modify the start and end times of the entry using the --back, --took, and --at flags, making it an easy
716
734
  way to add entries in post and maintain accurate (albeit manual) time tracking.'
717
735
  arg_name 'ENTRY'
@@ -940,14 +958,14 @@ command :cancel do |c|
940
958
  # c.switch [:fuzzy], default_value: false, negatable: false
941
959
 
942
960
  c.desc 'Force exact search string matching (case sensitive)'
943
- c.switch %i[x exact], default_value: false, negatable: false
961
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
944
962
 
945
963
  c.desc 'Finish items that *don\'t* match search/tag filters'
946
964
  c.switch [:not], default_value: false, negatable: false
947
965
 
948
966
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
949
967
  c.arg_name 'TYPE'
950
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
968
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
951
969
 
952
970
  c.desc 'Cancel last entry (or entries) not already marked @done'
953
971
  c.switch %i[u unfinished], negatable: false, default_value: false
@@ -1043,14 +1061,14 @@ command :finish do |c|
1043
1061
  # c.switch [:fuzzy], default_value: false, negatable: false
1044
1062
 
1045
1063
  c.desc 'Force exact search string matching (case sensitive)'
1046
- c.switch %i[x exact], default_value: false, negatable: false
1064
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
1047
1065
 
1048
1066
  c.desc 'Finish items that *don\'t* match search/tag filters'
1049
1067
  c.switch [:not], default_value: false, negatable: false
1050
1068
 
1051
1069
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
1052
1070
  c.arg_name 'TYPE'
1053
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
1071
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
1054
1072
 
1055
1073
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans.'
1056
1074
  c.arg_name 'BOOLEAN'
@@ -1182,14 +1200,14 @@ command %i[again resume] do |c|
1182
1200
  # c.switch [:fuzzy], default_value: false, negatable: false
1183
1201
 
1184
1202
  c.desc 'Force exact search string matching (case sensitive)'
1185
- c.switch %i[x exact], default_value: false, negatable: false
1203
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
1186
1204
 
1187
1205
  c.desc 'Resume items that *don\'t* match search/tag filters'
1188
1206
  c.switch [:not], default_value: false, negatable: false
1189
1207
 
1190
1208
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
1191
1209
  c.arg_name 'TYPE'
1192
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
1210
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
1193
1211
 
1194
1212
  c.desc 'Boolean used to combine multiple tags. Use PATTERN to parse + and - as booleans.'
1195
1213
  c.arg_name 'BOOLEAN'
@@ -1244,10 +1262,41 @@ command :tags do |c|
1244
1262
  c.arg_name 'ORDER'
1245
1263
  c.flag %i[o order], must_match: REGEX_SORT_ORDER, default_value: 'asc'
1246
1264
 
1265
+ c.desc 'Get tags for entries matching tags. Combine multiple tags with a comma. Wildcards allowed (*, ?).'
1266
+ c.arg_name 'TAG'
1267
+ c.flag [:tag]
1268
+
1269
+ c.desc 'Get tags for items matching search. Surround with
1270
+ slashes for regex (e.g. "/query/"), start with a single quote for exact match ("\'query").'
1271
+ c.arg_name 'QUERY'
1272
+ c.flag [:search]
1273
+
1274
+ # c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
1275
+ # c.switch [:fuzzy], default_value: false, negatable: false
1276
+
1277
+ c.desc 'Force exact search string matching (case sensitive)'
1278
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
1279
+
1280
+ c.desc 'Get tags from items that *don\'t* match search/tag filters'
1281
+ c.switch [:not], default_value: false, negatable: false
1282
+
1283
+ c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
1284
+ c.arg_name 'TYPE'
1285
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
1286
+
1287
+ c.desc 'Boolean used to combine multiple tags. Use PATTERN to parse + and - as booleans.'
1288
+ c.arg_name 'BOOLEAN'
1289
+ c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
1290
+
1291
+ c.desc 'Select items to scan from a menu of matching entries'
1292
+ c.switch %i[i interactive], negatable: false, default_value: false
1293
+
1247
1294
  c.action do |_global, options, args|
1248
1295
  section = wwid.guess_section(options[:section]) || options[:section].cap_first
1249
1296
 
1250
- items = wwid.content.in_section(section)
1297
+ items = wwid.filter_items([], opt: options)
1298
+
1299
+ # items = wwid.content.in_section(section)
1251
1300
  tags = wwid.all_tags(items, counts: true)
1252
1301
 
1253
1302
  if options[:sort] =~ /^n/i
@@ -1332,14 +1381,14 @@ command :tag do |c|
1332
1381
  # c.switch [:fuzzy], default_value: false, negatable: false
1333
1382
 
1334
1383
  c.desc 'Force exact search string matching (case sensitive)'
1335
- c.switch %i[x exact], default_value: false, negatable: false
1384
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
1336
1385
 
1337
1386
  c.desc 'Tag items that *don\'t* match search/tag filters'
1338
1387
  c.switch [:not], default_value: false, negatable: false
1339
1388
 
1340
1389
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
1341
1390
  c.arg_name 'TYPE'
1342
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
1391
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
1343
1392
 
1344
1393
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans.'
1345
1394
  c.arg_name 'BOOLEAN'
@@ -1395,37 +1444,41 @@ command :tag do |c|
1395
1444
  options[:search] = search
1396
1445
  end
1397
1446
 
1447
+ options[:count] = count
1448
+ options[:section] = section
1449
+ options[:tag] = search_tags
1450
+ options[:tags] = tags
1451
+ options[:tag_bool] = options[:bool].normalize_bool
1452
+
1398
1453
  if count.zero? && !options[:force]
1399
- if options[:search]
1400
- section_q = ' matching your search terms'
1401
- elsif options[:tag]
1402
- section_q = ' matching your tag search'
1403
- elsif section == 'All'
1404
- section_q = ''
1405
- else
1406
- section_q = " in section #{section}"
1407
- end
1454
+ matches = wwid.filter_items([], opt: options).count
1455
+
1456
+ if matches > 5
1457
+ if options[:search]
1458
+ section_q = ' matching your search terms'
1459
+ elsif options[:tag]
1460
+ section_q = ' matching your tag search'
1461
+ elsif section == 'All'
1462
+ section_q = ''
1463
+ else
1464
+ section_q = " in section #{section}"
1465
+ end
1408
1466
 
1409
1467
 
1410
- question = if options[:aarchive]
1411
- "Are you sure you want to autotag all records#{section_q}"
1412
- elsif options[:remove]
1413
- "Are you sure you want to remove #{tags.join(' and ')} from all records#{section_q}"
1414
- else
1415
- "Are you sure you want to add #{tags.join(' and ')} to all records#{section_q}"
1416
- end
1468
+ question = if options[:autotag]
1469
+ "Are you sure you want to autotag #{matches} records#{section_q}"
1470
+ elsif options[:remove]
1471
+ "Are you sure you want to remove #{tags.join(' and ')} from #{matches} records#{section_q}"
1472
+ else
1473
+ "Are you sure you want to add #{tags.join(' and ')} to #{matches} records#{section_q}"
1474
+ end
1417
1475
 
1418
- res = Doing::Prompt.yn(question, default_response: false)
1476
+ res = Doing::Prompt.yn(question, default_response: false)
1419
1477
 
1420
- raise UserCancelled unless res
1478
+ raise UserCancelled unless res
1479
+ end
1421
1480
  end
1422
1481
 
1423
- options[:count] = count
1424
- options[:section] = section
1425
- options[:tag] = search_tags
1426
- options[:tags] = tags
1427
- options[:tag_bool] = options[:bool].normalize_bool
1428
-
1429
1482
  wwid.tag_last(options)
1430
1483
  end
1431
1484
  end
@@ -1470,14 +1523,14 @@ command %i[mark flag] do |c|
1470
1523
  # c.switch [:fuzzy], default_value: false, negatable: false
1471
1524
 
1472
1525
  c.desc 'Force exact search string matching (case sensitive)'
1473
- c.switch %i[x exact], default_value: false, negatable: false
1526
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
1474
1527
 
1475
1528
  c.desc 'Flag items that *don\'t* match search/tag/date filters'
1476
1529
  c.switch [:not], default_value: false, negatable: false
1477
1530
 
1478
1531
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
1479
1532
  c.arg_name 'TYPE'
1480
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
1533
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
1481
1534
 
1482
1535
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans.'
1483
1536
  c.arg_name 'BOOLEAN'
@@ -1581,7 +1634,7 @@ command :show do |c|
1581
1634
 
1582
1635
  c.desc 'Max count to show'
1583
1636
  c.arg_name 'MAX'
1584
- c.flag %i[c count], default_value: 0
1637
+ c.flag %i[c count], default_value: 0, must_match: /^\d+$/, type: Integer
1585
1638
 
1586
1639
  c.desc 'Age (oldest|newest)'
1587
1640
  c.arg_name 'AGE'
@@ -1615,14 +1668,14 @@ command :show do |c|
1615
1668
  # c.switch [:fuzzy], default_value: false, negatable: false
1616
1669
 
1617
1670
  c.desc 'Force exact search string matching (case sensitive)'
1618
- c.switch %i[x exact], default_value: false, negatable: false
1671
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
1619
1672
 
1620
1673
  c.desc 'Show items that *don\'t* match search/tag/date filters'
1621
1674
  c.switch [:not], default_value: false, negatable: false
1622
1675
 
1623
1676
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
1624
1677
  c.arg_name 'TYPE'
1625
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
1678
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
1626
1679
 
1627
1680
  c.desc 'Sort order (asc/desc)'
1628
1681
  c.arg_name 'ORDER'
@@ -1699,24 +1752,11 @@ command :show do |c|
1699
1752
  end
1700
1753
  else
1701
1754
  section = options[:menu] ? wwid.choose_section(include_all: true) : settings['current_section']
1702
- end
1703
-
1704
- if options[:menu]
1705
- tag = wwid.choose_tag(section, include_all: true)
1706
- raise UserCancelled unless tag
1707
-
1708
- tags.concat(tag.split(/ +/).map { |t| t.strip.sub(/^@/, '') }) if tag =~ /^@/
1755
+ section ||= 'All'
1709
1756
  end
1710
1757
 
1711
1758
  tags.concat(options[:tag].to_tags) if options[:tag]
1712
1759
 
1713
- unless tags.empty?
1714
- tag_filter = {
1715
- 'tags' => tags,
1716
- 'bool' => options[:bool].normalize_bool
1717
- }
1718
- end
1719
-
1720
1760
  options[:times] = true if options[:totals]
1721
1761
 
1722
1762
  template = settings['templates']['default'].deep_merge({
@@ -1734,18 +1774,46 @@ command :show do |c|
1734
1774
  options[:search] = search
1735
1775
  end
1736
1776
 
1777
+ options[:section] = section
1778
+
1779
+ unless tags.empty?
1780
+ tag_filter = {
1781
+ 'tags' => tags,
1782
+ 'bool' => options[:bool].normalize_bool
1783
+ }
1784
+ end
1785
+
1786
+ options[:tag_filter] = tag_filter
1787
+ options[:tag] = nil
1788
+
1789
+ items = wwid.filter_items([], opt: options)
1790
+
1791
+ if options[:menu]
1792
+ tag = wwid.choose_tag(section, items: items, include_all: true)
1793
+ raise UserCancelled unless tag
1794
+
1795
+ # options[:bool] = :and unless tags.empty?
1796
+
1797
+ tags = tag.split(/ +/).map { |t| t.strip.sub(/^@?/, '') } if tag =~ /^@/
1798
+ unless tags.empty?
1799
+ tag_filter = {
1800
+ 'tags' => tags,
1801
+ 'bool' => options[:bool].normalize_bool
1802
+ }
1803
+ options[:tag_filter] = tag_filter
1804
+ end
1805
+ end
1806
+
1737
1807
  opt = options.dup
1738
1808
  opt[:sort_tags] = options[:tag_sort] =~ /^n/i
1739
1809
  opt[:count] = options[:count].to_i
1740
1810
  opt[:highlight] = true
1741
1811
  opt[:order] = options[:sort].normalize_order
1742
- opt[:section] = section
1743
1812
  opt[:tag] = nil
1744
- opt[:tag_filter] = tag_filter
1745
1813
  opt[:tag_order] = options[:tag_order].normalize_order
1746
1814
  opt[:tags_color] = template['tags_color']
1747
1815
 
1748
- Doing::Pager.page wwid.list_section(opt)
1816
+ Doing::Pager.page wwid.list_section(opt, items: items)
1749
1817
  end
1750
1818
  end
1751
1819
 
@@ -1812,14 +1880,14 @@ command %i[grep search] do |c|
1812
1880
  # c.switch [:fuzzy], default_value: false, negatable: false
1813
1881
 
1814
1882
  c.desc 'Force exact string matching (case sensitive)'
1815
- c.switch %i[x exact], default_value: false, negatable: false
1883
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
1816
1884
 
1817
1885
  c.desc 'Show items that *don\'t* match search string'
1818
1886
  c.switch [:not], default_value: false, negatable: false
1819
1887
 
1820
1888
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
1821
1889
  c.arg_name 'TYPE'
1822
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
1890
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
1823
1891
 
1824
1892
  c.desc 'Display an interactive menu of results to perform further operations'
1825
1893
  c.switch %i[i interactive], default_value: false, negatable: false
@@ -2094,7 +2162,7 @@ command :since do |c|
2094
2162
  end
2095
2163
 
2096
2164
  desc 'List entries from yesterday'
2097
- desc 'Show only entries with start times within the previous 24 hour period. Use --before, --after, and --from to limit to
2165
+ long_desc 'Show only entries with start times within the previous 24 hour period. Use --before, --after, and --from to limit to
2098
2166
  time spans within the day.'
2099
2167
  command :yesterday do |c|
2100
2168
  c.example 'doing yesterday', desc: 'List all entries from the previous day'
@@ -2204,14 +2272,14 @@ command :last do |c|
2204
2272
  # c.switch [:fuzzy], default_value: false, negatable: false
2205
2273
 
2206
2274
  c.desc 'Force exact search string matching (case sensitive)'
2207
- c.switch %i[x exact], default_value: false, negatable: false
2275
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
2208
2276
 
2209
2277
  c.desc 'Show items that *don\'t* match search string or tag filter'
2210
2278
  c.switch [:not], default_value: false, negatable: false
2211
2279
 
2212
2280
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
2213
2281
  c.arg_name 'TYPE'
2214
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
2282
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
2215
2283
 
2216
2284
  c.action do |global_options, options, _args|
2217
2285
  options[:fuzzy] = false
@@ -2221,17 +2289,7 @@ command :last do |c|
2221
2289
  tags = []
2222
2290
  else
2223
2291
  tags = options[:tag].to_tags
2224
- options[:bool] = case options[:bool]
2225
- when /^p/i
2226
- :pattern
2227
- when /(any|or)/i
2228
- :or
2229
- when /(not|none)/i
2230
- :not
2231
- else
2232
- :and
2233
- end
2234
-
2292
+ options[:bool] = options[:bool].normalize_bool
2235
2293
  end
2236
2294
 
2237
2295
  options[:case] = options[:case].normalize_case
@@ -2336,7 +2394,7 @@ command :plugins do |c|
2336
2394
  end
2337
2395
 
2338
2396
  desc 'Generate shell completion scripts'
2339
- desc 'Generates the necessary scripts to add command line completion to various shells, so typing \'doing\' and hitting
2397
+ long_desc 'Generates the necessary scripts to add command line completion to various shells, so typing \'doing\' and hitting
2340
2398
  tab will offer completions of subcommands and their options.'
2341
2399
  command :completion do |c|
2342
2400
  c.example 'doing completion', desc: 'Output zsh (default) to STDOUT'
@@ -2407,14 +2465,14 @@ command :view do |c|
2407
2465
  # c.switch [:fuzzy], default_value: false, negatable: false
2408
2466
 
2409
2467
  c.desc 'Force exact search string matching (case sensitive)'
2410
- c.switch %i[x exact], default_value: false, negatable: false
2468
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
2411
2469
 
2412
2470
  c.desc 'Show items that *don\'t* match search string'
2413
2471
  c.switch [:not], default_value: false, negatable: false
2414
2472
 
2415
2473
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
2416
2474
  c.arg_name 'TYPE'
2417
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
2475
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
2418
2476
 
2419
2477
  c.desc 'Sort tags by (name|time)'
2420
2478
  c.arg_name 'KEY'
@@ -2476,6 +2534,7 @@ command :view do |c|
2476
2534
  end
2477
2535
 
2478
2536
  view = wwid.get_view(title)
2537
+
2479
2538
  if view
2480
2539
  page_title = view.key?('title') ? view['title'] : title.cap_first
2481
2540
  only_timed = if (view.key?('only_timed') && view['only_timed']) || options[:only_timed]
@@ -2555,6 +2614,7 @@ command :view do |c|
2555
2614
  end
2556
2615
 
2557
2616
  opts = options.dup
2617
+ opts[:view_template] = title
2558
2618
  opts[:count] = count
2559
2619
  opts[:format] = date_format
2560
2620
  opts[:highlight] = options[:color]
@@ -2631,14 +2691,14 @@ command %i[archive move] do |c|
2631
2691
  # c.switch [:fuzzy], default_value: false, negatable: false
2632
2692
 
2633
2693
  c.desc 'Force exact search string matching (case sensitive)'
2634
- c.switch %i[x exact], default_value: false, negatable: false
2694
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
2635
2695
 
2636
2696
  c.desc 'Show items that *don\'t* match search string'
2637
2697
  c.switch [:not], default_value: false, negatable: false
2638
2698
 
2639
2699
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
2640
2700
  c.arg_name 'TYPE'
2641
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
2701
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
2642
2702
 
2643
2703
  c.desc 'Archive entries older than date
2644
2704
  (Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
@@ -2716,14 +2776,14 @@ command :rotate do |c|
2716
2776
  # c.switch [:fuzzy], default_value: false, negatable: false
2717
2777
 
2718
2778
  c.desc 'Force exact search string matching (case sensitive)'
2719
- c.switch %i[x exact], default_value: false, negatable: false
2779
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
2720
2780
 
2721
2781
  c.desc 'Rotate items that *don\'t* match search string or tag filter'
2722
2782
  c.switch [:not], default_value: false, negatable: false
2723
2783
 
2724
2784
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
2725
2785
  c.arg_name 'TYPE'
2726
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
2786
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
2727
2787
 
2728
2788
  c.desc 'Rotate entries older than date
2729
2789
  (Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
@@ -2930,7 +2990,7 @@ command :config do |c|
2930
2990
  c.command :undo do |undo|
2931
2991
  undo.action do |_global, options, args|
2932
2992
  config_file = config.choose_config
2933
- wwid.restore_backup(config_file)
2993
+ Doing::Util::Backup.restore_last_backup(config_file, count: 1)
2934
2994
  end
2935
2995
  end
2936
2996
 
@@ -3078,7 +3138,11 @@ command :undo do |c|
3078
3138
  if options[:prune]
3079
3139
  Doing::Util::Backup.prune_backups(file, options[:prune])
3080
3140
  elsif options[:redo]
3081
- Doing::Util::Backup.redo_backup(file, count: count)
3141
+ if options[:interactive]
3142
+ Doing::Util::Backup.select_redo(file)
3143
+ else
3144
+ Doing::Util::Backup.redo_backup(file, count: count)
3145
+ end
3082
3146
  else
3083
3147
  if options[:interactive]
3084
3148
  Doing::Util::Backup.select_backup(file)
@@ -3096,12 +3160,18 @@ command :redo do |c|
3096
3160
  c.arg_name 'PATH'
3097
3161
  c.flag %i[f file], default_value: wwid.doing_file
3098
3162
 
3163
+ c.desc 'Select from an interactive menu'
3164
+ c.switch %i[i interactive]
3165
+
3099
3166
  c.action do |_global, options, args|
3100
3167
  file = options[:file] || wwid.doing_file
3101
3168
  count = args.empty? ? 1 : args[0].to_i
3102
3169
  raise InvalidArgument, "Invalid count specified for redo" unless count&.positive?
3103
-
3104
- Doing::Util::Backup.redo_backup(file, count: count)
3170
+ if options[:interactive]
3171
+ Doing::Util::Backup.select_redo(file)
3172
+ else
3173
+ Doing::Util::Backup.redo_backup(file, count: count)
3174
+ end
3105
3175
  end
3106
3176
  end
3107
3177
 
@@ -3120,6 +3190,32 @@ command %i[changelog changes] do |c|
3120
3190
  end
3121
3191
  end
3122
3192
 
3193
+ arg_name 'OPTION'
3194
+ command :commands_accepting do |c|
3195
+ c.desc 'Output in single column for completion'
3196
+ c.switch %i[c column]
3197
+
3198
+ c.action do |g, o, a|
3199
+ a.each do |option|
3200
+ cmds = []
3201
+ commands.each do |cmd, v|
3202
+ v.flags.merge(v.switches).each do |n, flag|
3203
+ if flag.name == option.to_sym || flag.aliases&.include?(option.to_sym)
3204
+ cmds.push(cmd)
3205
+ end
3206
+ end
3207
+ end
3208
+
3209
+ if o[:column]
3210
+ puts cmds
3211
+ else
3212
+ puts "Commands accepting --#{option}: #{cmds.join(', ')}"
3213
+ end
3214
+ end
3215
+ end
3216
+ end
3217
+
3218
+
3123
3219
  desc 'Import entries from an external source'
3124
3220
  long_desc "Imports entries from other sources. Available plugins: #{Doing::Plugins.plugin_names(type: :import, separator: ', ')}"
3125
3221
  arg_name 'PATH'
@@ -3140,14 +3236,14 @@ command :import do |c|
3140
3236
  # c.switch [:fuzzy], default_value: false, negatable: false
3141
3237
 
3142
3238
  c.desc 'Force exact search string matching (case sensitive)'
3143
- c.switch %i[x exact], default_value: false, negatable: false
3239
+ c.switch %i[x exact], default_value: config.exact_match?, negatable: config.exact_match?
3144
3240
 
3145
3241
  c.desc 'Import items that *don\'t* match search/tag/date filters'
3146
3242
  c.switch [:not], default_value: false, negatable: false
3147
3243
 
3148
3244
  c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
3149
3245
  c.arg_name 'TYPE'
3150
- c.flag [:case], must_match: /^[csi]/, default_value: 'smart'
3246
+ c.flag [:case], must_match: /^[csi]/, default_value: settings.dig('search', 'case')
3151
3247
 
3152
3248
  c.desc 'Only import items with recorded time intervals'
3153
3249
  c.switch [:only_timed], default_value: false, negatable: false
@@ -3222,7 +3318,6 @@ end
3222
3318
 
3223
3319
  pre do |global, _command, _options, _args|
3224
3320
  # global[:pager] ||= settings['paginate']
3225
-
3226
3321
  Doing::Pager.paginate = global[:pager]
3227
3322
 
3228
3323
  $stdout.puts "doing v#{Doing::VERSION}" if global[:version]
@@ -3253,6 +3348,8 @@ post do |global, _command, _options, _args|
3253
3348
  # Use skips_post before a command to skip this
3254
3349
  # block on that command only
3255
3350
  Doing.logger.output_results
3351
+ Doing.logger.benchmark(:total, :finish)
3352
+ Doing.logger.log_benchmarks
3256
3353
  end
3257
3354
 
3258
3355
  around do |global, command, options, arguments, code|
@@ -3286,13 +3383,13 @@ around do |global, command, options, arguments, code|
3286
3383
  config.config_file = cf
3287
3384
  settings = config.configure({ ignore_local: true })
3288
3385
  end
3289
-
3386
+ Doing.logger.benchmark(:init, :start)
3290
3387
  if global[:doing_file]
3291
3388
  wwid.init_doing_file(global[:doing_file])
3292
3389
  else
3293
3390
  wwid.init_doing_file
3294
3391
  end
3295
-
3392
+ Doing.logger.benchmark(:init, :finish)
3296
3393
  wwid.auto_tag = !global[:noauto]
3297
3394
 
3298
3395
  settings[:include_notes] = false unless global[:notes]
data/docs/_config.yml ADDED
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-slate