doing 2.1.23 → 2.1.27

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +17 -21
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +329 -102
  6. data/Dockerfile +5 -5
  7. data/Dockerfile-2.6 +5 -5
  8. data/Dockerfile-2.7 +5 -4
  9. data/Dockerfile-3.0 +5 -4
  10. data/Gemfile.lock +1 -1
  11. data/README.md +1 -1
  12. data/Rakefile +3 -3
  13. data/bin/commands/add_section.rb +15 -0
  14. data/bin/commands/again.rb +57 -0
  15. data/bin/commands/archive.rb +55 -0
  16. data/bin/commands/cancel.rb +60 -0
  17. data/bin/commands/changes.rb +69 -0
  18. data/bin/commands/choose.rb +9 -0
  19. data/bin/commands/colors.rb +21 -0
  20. data/bin/commands/commands.rb +89 -0
  21. data/bin/commands/commands_accepting.rb +76 -0
  22. data/bin/commands/completion.rb +27 -0
  23. data/bin/commands/config.rb +245 -0
  24. data/bin/commands/done.rb +235 -0
  25. data/bin/commands/finish.rb +126 -0
  26. data/bin/commands/flag.rb +90 -0
  27. data/bin/commands/grep.rb +108 -0
  28. data/bin/commands/import.rb +71 -0
  29. data/bin/commands/install_fzf.rb +17 -0
  30. data/bin/commands/last.rb +81 -0
  31. data/bin/commands/meanwhile.rb +76 -0
  32. data/bin/commands/note.rb +91 -0
  33. data/bin/commands/now.rb +145 -0
  34. data/bin/commands/on.rb +65 -0
  35. data/bin/commands/open.rb +53 -0
  36. data/bin/commands/plugins.rb +23 -0
  37. data/bin/commands/recent.rb +77 -0
  38. data/bin/commands/redo.rb +26 -0
  39. data/bin/commands/reset.rb +73 -0
  40. data/bin/commands/rotate.rb +42 -0
  41. data/bin/commands/sections.rb +11 -0
  42. data/bin/commands/select.rb +105 -0
  43. data/bin/commands/show.rb +185 -0
  44. data/bin/commands/since.rb +63 -0
  45. data/bin/commands/tag.rb +149 -0
  46. data/bin/commands/tag_dir.rb +29 -0
  47. data/bin/commands/tags.rb +66 -0
  48. data/bin/commands/template.rb +61 -0
  49. data/bin/commands/today.rb +64 -0
  50. data/bin/commands/undo.rb +49 -0
  51. data/bin/commands/view.rb +201 -0
  52. data/bin/commands/views.rb +11 -0
  53. data/bin/commands/yesterday.rb +72 -0
  54. data/bin/doing +241 -3662
  55. data/docs/doc/Array.html +13 -449
  56. data/docs/doc/BooleanTermParser/Clause.html +5 -5
  57. data/docs/doc/BooleanTermParser/Operator.html +4 -4
  58. data/docs/doc/BooleanTermParser/Query.html +8 -8
  59. data/docs/doc/BooleanTermParser/QueryParser.html +2 -2
  60. data/docs/doc/BooleanTermParser/QueryTransformer.html +2 -2
  61. data/docs/doc/BooleanTermParser.html +1 -1
  62. data/docs/doc/Doing/Color.html +65 -59
  63. data/docs/doc/Doing/Completion.html +2 -2
  64. data/docs/doc/Doing/Configuration.html +49 -16
  65. data/docs/doc/Doing/Errors/DoingNoTraceError.html +2 -2
  66. data/docs/doc/Doing/Errors/DoingRuntimeError.html +2 -2
  67. data/docs/doc/Doing/Errors/DoingStandardError.html +2 -2
  68. data/docs/doc/Doing/Errors/EmptyInput.html +2 -2
  69. data/docs/doc/Doing/Errors/NoResults.html +2 -2
  70. data/docs/doc/Doing/Errors/PluginException.html +3 -3
  71. data/docs/doc/Doing/Errors/UserCancelled.html +2 -2
  72. data/docs/doc/Doing/Errors/WrongCommand.html +2 -2
  73. data/docs/doc/Doing/Errors.html +1 -1
  74. data/docs/doc/Doing/Hooks.html +6 -6
  75. data/docs/doc/Doing/Item.html +50 -16
  76. data/docs/doc/Doing/Items.html +10 -10
  77. data/docs/doc/Doing/LogAdapter.html +24 -24
  78. data/docs/doc/Doing/Note.html +7 -7
  79. data/docs/doc/Doing/Pager.html +4 -4
  80. data/docs/doc/Doing/Plugins.html +7 -7
  81. data/docs/doc/Doing/Prompt.html +59 -14
  82. data/docs/doc/Doing/Section.html +6 -6
  83. data/docs/doc/Doing/TemplateString.html +8 -8
  84. data/docs/doc/Doing/Types.html +46 -1
  85. data/docs/doc/Doing/Util/Backup.html +10 -10
  86. data/docs/doc/Doing/Util.html +15 -15
  87. data/docs/doc/Doing/WWID.html +73 -61
  88. data/docs/doc/Doing.html +3 -3
  89. data/docs/doc/FalseClass.html +235 -0
  90. data/docs/doc/GLI/Commands/Help.html +3 -3
  91. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +17 -17
  92. data/docs/doc/GLI/Commands.html +1 -1
  93. data/docs/doc/GLI.html +1 -1
  94. data/docs/doc/Hash.html +45 -11
  95. data/docs/doc/Numeric.html +5 -5
  96. data/docs/doc/Object.html +203 -0
  97. data/docs/doc/PhraseParser/Operator.html +4 -4
  98. data/docs/doc/PhraseParser/PhraseClause.html +5 -5
  99. data/docs/doc/PhraseParser/Query.html +10 -10
  100. data/docs/doc/PhraseParser/QueryParser.html +2 -2
  101. data/docs/doc/PhraseParser/QueryTransformer.html +2 -2
  102. data/docs/doc/PhraseParser/TermClause.html +5 -5
  103. data/docs/doc/PhraseParser.html +1 -1
  104. data/docs/doc/Status.html +7 -7
  105. data/docs/doc/String.html +306 -3111
  106. data/docs/doc/Symbol.html +45 -11
  107. data/docs/doc/Time.html +6 -6
  108. data/docs/doc/TrueClass.html +235 -0
  109. data/docs/doc/_index.html +37 -19
  110. data/docs/doc/class_list.html +1 -1
  111. data/docs/doc/file.README.html +2 -2
  112. data/docs/doc/index.html +2 -2
  113. data/docs/doc/method_list.html +240 -576
  114. data/docs/doc/top-level-namespace.html +2 -2
  115. data/doing.rdoc +289 -169
  116. data/example_plugin.rb +2 -2
  117. data/lib/completion/_doing.zsh +35 -31
  118. data/lib/completion/doing.bash +30 -19
  119. data/lib/completion/doing.fish +81 -67
  120. data/lib/doing/array/array.rb +4 -0
  121. data/lib/doing/array/nested_hash.rb +17 -0
  122. data/lib/doing/{array.rb → array/tags.rb} +7 -25
  123. data/lib/doing/changelog/change.rb +26 -11
  124. data/lib/doing/changelog/changes.rb +14 -4
  125. data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
  126. data/lib/doing/chronify/chronify.rb +5 -0
  127. data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
  128. data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
  129. data/lib/doing/colors.rb +115 -54
  130. data/lib/doing/completion/fish_completion.rb +2 -1
  131. data/lib/doing/configuration.rb +9 -6
  132. data/lib/doing/good.rb +72 -0
  133. data/lib/doing/hash.rb +4 -0
  134. data/lib/doing/help_monkey_patch.rb +6 -5
  135. data/lib/doing/hooks.rb +3 -3
  136. data/lib/doing/item.rb +19 -15
  137. data/lib/doing/items.rb +2 -2
  138. data/lib/doing/log_adapter.rb +35 -2
  139. data/lib/doing/normalize.rb +188 -0
  140. data/lib/doing/pager.rb +1 -0
  141. data/lib/doing/plugins/export/dayone_export.rb +1 -1
  142. data/lib/doing/plugins/export/html_export.rb +1 -1
  143. data/lib/doing/plugins/export/json_export.rb +1 -1
  144. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  145. data/lib/doing/plugins/export/template_export.rb +3 -1
  146. data/lib/doing/plugins/import/calendar_import.rb +1 -1
  147. data/lib/doing/plugins/import/doing_import.rb +1 -1
  148. data/lib/doing/plugins/import/timing_import.rb +1 -1
  149. data/lib/doing/prompt.rb +9 -3
  150. data/lib/doing/string/highlight.rb +95 -0
  151. data/lib/doing/string/query.rb +129 -0
  152. data/lib/doing/string/string.rb +12 -0
  153. data/lib/doing/string/tags.rb +164 -0
  154. data/lib/doing/string/transform.rb +168 -0
  155. data/lib/doing/string/truncate.rb +75 -0
  156. data/lib/doing/string/url.rb +82 -0
  157. data/lib/doing/template_string.rb +2 -24
  158. data/lib/doing/types.rb +9 -0
  159. data/lib/doing/util.rb +20 -16
  160. data/lib/doing/version.rb +1 -1
  161. data/lib/doing/wwid.rb +91 -51
  162. data/lib/doing.rb +5 -6
  163. data/lib/examples/commands/wiki.rb +6 -7
  164. data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
  165. data/lib/helpers/threaded_tests.rb +69 -79
  166. data/lib/helpers/threaded_tests_string.rb +50 -0
  167. data/scripts/deploy.rb +107 -0
  168. data/scripts/runtests.sh +4 -0
  169. metadata +65 -8
  170. data/lib/doing/string.rb +0 -765
  171. data/lib/doing/symbol.rb +0 -28
data/lib/doing/wwid.rb CHANGED
@@ -338,6 +338,7 @@ module Doing
338
338
  ## @option opt :note [Array] item note (will be converted if value is String)
339
339
  ## @option opt :back [Date] backdate
340
340
  ## @option opt :timed [Boolean] new item is timed entry, marks previous entry as @done
341
+ ## @option opt :done [Date] If set, adds a @done tag to new entry
341
342
  ##
342
343
  def add_item(title, section = nil, opt)
343
344
  opt ||= {}
@@ -360,9 +361,18 @@ module Doing
360
361
 
361
362
  title.compress!
362
363
  entry = Item.new(opt[:back], title.strip, section)
364
+
365
+ if opt[:done] && entry.should_finish?
366
+ if entry.should_time?
367
+ entry.tag('done', value: opt[:done])
368
+ else
369
+ entry.tag('done')
370
+ end
371
+ end
372
+
363
373
  entry.note = note
364
374
 
365
- items = @content.dup
375
+ items = @content.clone
366
376
  if opt[:timed]
367
377
  items.reverse!
368
378
  items.each_with_index do |i, x|
@@ -380,7 +390,8 @@ module Doing
380
390
  # logger.count(:added, level: :debug)
381
391
  logger.info('New entry:', %(added "#{entry.date.relative_date}: #{entry.title}" to #{section}))
382
392
 
383
- Hooks.trigger :post_entry_added, self, entry.dup
393
+ Hooks.trigger :post_entry_added, self, entry
394
+ entry
384
395
  end
385
396
 
386
397
  ##
@@ -474,6 +485,7 @@ module Doing
474
485
  #
475
486
  def repeat_item(item, opt)
476
487
  opt ||= {}
488
+ old_item = item.clone
477
489
  if item.should_finish?
478
490
  if item.should_time?
479
491
  finish_date = verify_duration(item.date, Time.now, title: item.title)
@@ -481,7 +493,7 @@ module Doing
481
493
  else
482
494
  item.title.tag!('done')
483
495
  end
484
- Hooks.trigger :post_entry_updated, self, item
496
+ Hooks.trigger :post_entry_updated, self, item, old_item
485
497
  end
486
498
 
487
499
  # Remove @done tag
@@ -647,11 +659,12 @@ module Doing
647
659
  ## @option opt [Array] :val (nil) Array of tag value queries
648
660
  ##
649
661
  def filter_items(items = Items.new, opt: {})
662
+ logger.benchmark(:filter_items, :start)
650
663
  time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/
651
664
 
652
665
  if items.nil? || items.empty?
653
666
  section = opt[:section] ? guess_section(opt[:section]) : 'All'
654
- items = section =~ /^all$/i ? @content.dup : @content.in_section(section)
667
+ items = section =~ /^all$/i ? @content.clone : @content.in_section(section)
655
668
  end
656
669
 
657
670
  opt[:time_filter] = [nil, nil]
@@ -798,6 +811,8 @@ module Doing
798
811
  output.concat(filtered_items.reverse.slice(0, count))
799
812
  end
800
813
 
814
+ logger.benchmark(:filter_items, :finish)
815
+
801
816
  output
802
817
  end
803
818
 
@@ -847,7 +862,7 @@ module Doing
847
862
  note.map!(&:strip)
848
863
  note.delete_if(&:ignore?)
849
864
  item = items[i]
850
- old_item = item.dup
865
+ old_item = item.clone
851
866
  item.date = date || items[i].date
852
867
  item.title = title
853
868
  item.note = note
@@ -855,7 +870,7 @@ module Doing
855
870
  Doing.logger.count(:skipped, level: :debug)
856
871
  else
857
872
  Doing.logger.count(:updated)
858
- Hooks.trigger :post_entry_updated, self, item
873
+ Hooks.trigger :post_entry_updated, self, item, old_item
859
874
  end
860
875
  end
861
876
  end
@@ -1045,9 +1060,10 @@ module Doing
1045
1060
  else
1046
1061
  opt[:resume]
1047
1062
  end
1063
+ old_item = item.clone
1048
1064
  new_entry = reset_item(item, date: date, resume: res)
1049
1065
  @content.update_item(item, new_entry)
1050
- Hooks.trigger :post_entry_updated, self, new_entry
1066
+ Hooks.trigger :post_entry_updated, self, new_entry, old_item
1051
1067
  end
1052
1068
  write(@doing_file)
1053
1069
 
@@ -1062,8 +1078,9 @@ module Doing
1062
1078
  if opt[:flag]
1063
1079
  tag = @config['marker_tag'] || 'flagged'
1064
1080
  items.map! do |i|
1081
+ old_item = i.clone
1065
1082
  i.tag(tag, date: false, remove: opt[:remove], single: single)
1066
- Hooks.trigger :post_entry_updated, self, i
1083
+ Hooks.trigger :post_entry_updated, self, i, old_item
1067
1084
  end
1068
1085
  end
1069
1086
 
@@ -1071,9 +1088,10 @@ module Doing
1071
1088
  tag = 'done'
1072
1089
  items.map! do |i|
1073
1090
  if i.should_finish?
1091
+ old_item = i.clone
1074
1092
  should_date = !opt[:cancel] && i.should_time?
1075
1093
  i.tag(tag, date: should_date, remove: opt[:remove], single: single)
1076
- Hooks.trigger :post_entry_updated, self, i
1094
+ Hooks.trigger :post_entry_updated, self, i, old_item
1077
1095
  end
1078
1096
  end
1079
1097
  end
@@ -1087,8 +1105,9 @@ module Doing
1087
1105
  else
1088
1106
  logger.count(:added_tags)
1089
1107
  logger.write(items.count == 1 ? :info : :debug, 'Tagged:', new_title)
1108
+ old_item = i.clone
1090
1109
  i.title = new_title
1091
- Hooks.trigger :post_entry_updated, self, i
1110
+ Hooks.trigger :post_entry_updated, self, i, old_item
1092
1111
  end
1093
1112
  end
1094
1113
  end
@@ -1096,17 +1115,19 @@ module Doing
1096
1115
  if opt[:tag]
1097
1116
  tag = opt[:tag]
1098
1117
  items.map! do |i|
1118
+ old_item = i.clone
1099
1119
  i.tag(tag, date: false, remove: opt[:remove], single: single)
1100
1120
  i.expand_date_tags(@config['date_tags'])
1101
- Hooks.trigger :post_entry_updated, self, i
1121
+ Hooks.trigger :post_entry_updated, self, i, old_item
1102
1122
  end
1103
1123
  end
1104
1124
 
1105
1125
  if opt[:archive] || opt[:move]
1106
1126
  section = opt[:archive] ? 'Archive' : guess_section(opt[:move])
1107
1127
  items.map! do |i|
1128
+ old_item = i.clone
1108
1129
  i.move_to(section, label: true)
1109
- Hooks.trigger :post_entry_updated, self, i
1130
+ Hooks.trigger :post_entry_updated, self, i, old_item
1110
1131
  end
1111
1132
  end
1112
1133
 
@@ -1193,6 +1214,7 @@ module Doing
1193
1214
  opt[:sequential] ||= false
1194
1215
  opt[:date] ||= false
1195
1216
  opt[:remove] ||= false
1217
+ opt[:update] ||= false
1196
1218
  opt[:autotag] ||= false
1197
1219
  opt[:back] ||= false
1198
1220
  opt[:unfinished] ||= false
@@ -1228,6 +1250,7 @@ module Doing
1228
1250
  end
1229
1251
 
1230
1252
  items.each do |item|
1253
+ old_item = item.clone
1231
1254
  added = []
1232
1255
  removed = []
1233
1256
 
@@ -1304,7 +1327,7 @@ module Doing
1304
1327
  else
1305
1328
  old_title = item.title.dup
1306
1329
  should_date = opt[:date] && item.should_time?
1307
- item.title.tag!('done', remove: true) if tag =~ /done/ && !should_date
1330
+ item.title.tag!('done', remove: true) if tag =~ /done/ && (!should_date || opt[:update])
1308
1331
  item.title.tag!(tag, value: should_date ? done_date.strftime('%F %R') : nil)
1309
1332
  added << tag if old_title != item.title
1310
1333
  end
@@ -1322,7 +1345,7 @@ module Doing
1322
1345
  end
1323
1346
 
1324
1347
  item.expand_date_tags(@config['date_tags'])
1325
- Hooks.trigger :post_entry_updated, self, item
1348
+ Hooks.trigger :post_entry_updated, self, item, old_item
1326
1349
  end
1327
1350
 
1328
1351
  write(@doing_file)
@@ -1361,6 +1384,7 @@ module Doing
1361
1384
  return
1362
1385
  end
1363
1386
 
1387
+ old_item = item.clone
1364
1388
  content = ["#{item.date.strftime('%F %R')} | #{item.title.dup}"]
1365
1389
  content << item.note.strip_lines.join("\n") unless item.note.empty?
1366
1390
  new_item = fork_editor(content.join("\n"))
@@ -1376,7 +1400,7 @@ module Doing
1376
1400
  item.title = title
1377
1401
  item.note.add(note, replace: true)
1378
1402
  logger.info('Edited:', item.title)
1379
- Hooks.trigger :post_entry_updated, self, item.dup
1403
+ Hooks.trigger :post_entry_updated, self, item, old_item
1380
1404
 
1381
1405
  write(@doing_file)
1382
1406
  end
@@ -1413,6 +1437,7 @@ module Doing
1413
1437
  found_items = 0
1414
1438
 
1415
1439
  @content.each_with_index do |item, i|
1440
+ old_item = i.clone
1416
1441
  next unless item.section == opt[:section] || opt[:section] =~ /all/i
1417
1442
 
1418
1443
  next unless item.title =~ /@#{tag}/
@@ -1431,7 +1456,7 @@ module Doing
1431
1456
  logger.count(:completed)
1432
1457
  logger.info('Completed:', item.title)
1433
1458
  end
1434
- Hooks.trigger :post_entry_updated, self, item
1459
+ Hooks.trigger :post_entry_updated, self, item, old_item
1435
1460
  end
1436
1461
 
1437
1462
 
@@ -1492,7 +1517,7 @@ module Doing
1492
1517
 
1493
1518
  unless ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
1494
1519
  new_item = @content.delete(item)
1495
- Hooks.trigger :post_entry_removed, self, item.dup
1520
+ Hooks.trigger :post_entry_removed, self, item.clone
1496
1521
  raise DoingRuntimeError, "Error deleting item: #{item}" if new_item.nil?
1497
1522
 
1498
1523
  new_content.add_section(new_item.section, log: false)
@@ -1598,6 +1623,7 @@ module Doing
1598
1623
  ## @param opt [Hash] Additional Options
1599
1624
  ##
1600
1625
  def list_section(opt, items: Items.new)
1626
+ logger.benchmark(:list_section, :start)
1601
1627
  opt[:config_template] ||= 'default'
1602
1628
 
1603
1629
  tpl_cfg = @config.dig('templates', opt[:config_template])
@@ -1611,7 +1637,7 @@ module Doing
1611
1637
  cfg.deep_merge({
1612
1638
  'wrap_width' => @config['wrap_width'] || 0,
1613
1639
  'date_format' => @config['default_date_format'],
1614
- 'order' => @config['order'] || 'asc',
1640
+ 'order' => @config['order'] || :asc,
1615
1641
  'tags_color' => @config['tags_color'],
1616
1642
  'duration' => @config['duration'],
1617
1643
  'interval_format' => @config['interval_format']
@@ -1623,8 +1649,8 @@ module Doing
1623
1649
  opt[:age] ||= :newest
1624
1650
  opt[:age] = opt[:age].normalize_age
1625
1651
  opt[:format] ||= cfg['date_format']
1626
- opt[:order] ||= cfg['order'] || 'asc'
1627
- opt[:tag_order] ||= 'asc'
1652
+ opt[:order] ||= cfg['order'] || :asc
1653
+ opt[:tag_order] ||= :asc
1628
1654
  opt[:tags_color] = cfg['tags_color'] || false if opt[:tags_color].nil?
1629
1655
  opt[:template] ||= cfg['template']
1630
1656
 
@@ -1650,7 +1676,7 @@ module Doing
1650
1676
 
1651
1677
  items = filter_items(items, opt: opt)
1652
1678
 
1653
- items.reverse! unless opt[:order] =~ /^d/i
1679
+ items.reverse! unless opt[:order].normalize_order == :desc
1654
1680
 
1655
1681
  if opt[:delete]
1656
1682
  delete_items(items, force: opt[:force])
@@ -1673,6 +1699,7 @@ module Doing
1673
1699
  opt[:output] ||= 'template'
1674
1700
  opt[:wrap_width] ||= @config['templates']['default']['wrap_width'] || 0
1675
1701
 
1702
+ logger.benchmark(:list_section, :finish)
1676
1703
  output(items, title, is_single, opt)
1677
1704
  end
1678
1705
 
@@ -1700,7 +1727,7 @@ module Doing
1700
1727
  destination = guess_section(destination)
1701
1728
 
1702
1729
  if @content.section?(destination) && (@content.section?(section) || archive_all)
1703
- do_archive(section, destination, { count: count, tags: tags, bool: bool, search: options[:search], label: options[:label], before: options[:before] })
1730
+ do_archive(section, destination, { count: count, tags: tags, bool: bool, search: options[:search], label: options[:label], before: options[:before], after: options[:after], from: options[:from] })
1704
1731
  write(doing_file)
1705
1732
  else
1706
1733
  raise InvalidArgument, 'Either source or destination does not exist'
@@ -1722,7 +1749,7 @@ module Doing
1722
1749
  cfg = @config['templates'][opt[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
1723
1750
  'wrap_width' => @config['wrap_width'] || 0,
1724
1751
  'date_format' => @config['default_date_format'],
1725
- 'order' => @config['order'] || 'asc',
1752
+ 'order' => @config['order'] || :asc,
1726
1753
  'tags_color' => @config['tags_color'],
1727
1754
  'duration' => @config['duration'],
1728
1755
  'interval_format' => @config['interval_format']
@@ -1741,7 +1768,7 @@ module Doing
1741
1768
  from: opt[:from],
1742
1769
  format: cfg['date_format'],
1743
1770
  interval_format: opt[:interval_format],
1744
- order: cfg['order'] || 'asc',
1771
+ order: cfg['order'] || :asc,
1745
1772
  output: output,
1746
1773
  section: opt[:section],
1747
1774
  sort_tags: opt[:sort_tags],
@@ -1776,7 +1803,7 @@ module Doing
1776
1803
  list_section({
1777
1804
  section: section,
1778
1805
  count: 0,
1779
- order: 'asc',
1806
+ order: :asc,
1780
1807
  date_filter: dates,
1781
1808
  times: times,
1782
1809
  output: output,
@@ -1842,7 +1869,7 @@ module Doing
1842
1869
  cfg = @config['templates'][opt[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
1843
1870
  'wrap_width' => @config['wrap_width'] || 0,
1844
1871
  'date_format' => @config['default_date_format'],
1845
- 'order' => @config['order'] || 'asc',
1872
+ 'order' => @config['order'] || :asc,
1846
1873
  'tags_color' => @config['tags_color'],
1847
1874
  'duration' => @config['duration'],
1848
1875
  'interval_format' => @config['interval_format']
@@ -1858,7 +1885,7 @@ module Doing
1858
1885
  opt[:count] = count
1859
1886
  opt[:format] = cfg['date_format']
1860
1887
  opt[:template] = opt[:template] || cfg['template']
1861
- opt[:order] = 'asc'
1888
+ opt[:order] = :asc
1862
1889
  opt[:times] = times
1863
1890
 
1864
1891
  list_section(opt)
@@ -1875,7 +1902,7 @@ module Doing
1875
1902
  cfg = @config['templates'][options[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
1876
1903
  'wrap_width' => @config['wrap_width'] || 0,
1877
1904
  'date_format' => @config['default_date_format'],
1878
- 'order' => @config['order'] || 'asc',
1905
+ 'order' => @config['order'] || :asc,
1879
1906
  'tags_color' => @config['tags_color'],
1880
1907
  'duration' => @config['duration'],
1881
1908
  'interval_format' => @config['interval_format']
@@ -2022,10 +2049,10 @@ module Doing
2022
2049
  ##
2023
2050
  ## @param format [String] return format (html,
2024
2051
  ## json, or text)
2025
- ## @param sort_by_name [Boolean] Sort by name if true, otherwise by time
2026
- ## @param sort_order [String] The sort order (asc or desc)
2052
+ ## @param sort_by [Symbol] Sort by :name or :time
2053
+ ## @param sort_order [Symbol] The sort order (:asc or :desc)
2027
2054
  ##
2028
- def tag_times(format: :text, sort_by_name: false, sort_order: 'asc')
2055
+ def tag_times(format: :text, sort_by: :time, sort_order: :asc)
2029
2056
  return '' if @timers.empty?
2030
2057
 
2031
2058
  max = @timers.keys.sort_by { |k| k.length }.reverse[0].length + 1
@@ -2033,13 +2060,13 @@ module Doing
2033
2060
  total = @timers.delete('All')
2034
2061
 
2035
2062
  tags_data = @timers.delete_if { |_k, v| v == 0 }
2036
- sorted_tags_data = if sort_by_name
2063
+ sorted_tags_data = if sort_by.normalize_tag_sort == :name
2037
2064
  tags_data.sort_by { |k, _v| k }
2038
2065
  else
2039
2066
  tags_data.sort_by { |_k, v| v }
2040
2067
  end
2041
2068
 
2042
- sorted_tags_data.reverse! if sort_order =~ /^asc/i
2069
+ sorted_tags_data.reverse! if sort_order.normalize_order == :asc
2043
2070
  case format
2044
2071
  when :html
2045
2072
 
@@ -2189,9 +2216,9 @@ EOS
2189
2216
  Doing.config_with(ENV['DOING_CONFIG'], { ignore_local: true })
2190
2217
  end
2191
2218
 
2192
- Doing.logger.benchmark(:configure, :start)
2219
+ logger.benchmark(:configure, :start)
2193
2220
  config = Doing.config
2194
- Doing.logger.benchmark(:configure, :finish)
2221
+ logger.benchmark(:configure, :finish)
2195
2222
 
2196
2223
  config.settings['backup_dir'] = ENV['DOING_BACKUP_DIR'] if ENV['DOING_BACKUP_DIR']
2197
2224
  @config = config.settings
@@ -2202,7 +2229,7 @@ EOS
2202
2229
 
2203
2230
  filename ||= @config['doing_file']
2204
2231
  init_doing_file(filename)
2205
- current_content = @content.dup
2232
+ current_content = @content.clone
2206
2233
  backup_file = Util::Backup.last_backup(filename, count: 1)
2207
2234
  raise DoingRuntimeError, 'No undo history to diff' if backup_file.nil?
2208
2235
 
@@ -2245,6 +2272,7 @@ EOS
2245
2272
  ## template trigger
2246
2273
  ##
2247
2274
  def output(items, title, is_single, opt)
2275
+ logger.benchmark(:output, :start)
2248
2276
  opt ||= {}
2249
2277
  out = nil
2250
2278
 
@@ -2262,7 +2290,7 @@ EOS
2262
2290
  end
2263
2291
 
2264
2292
  logger.debug('Output:', "#{items.count} #{items.count == 1 ? 'item' : 'items'} shown")
2265
-
2293
+ logger.benchmark(:output, :finish)
2266
2294
  out
2267
2295
  end
2268
2296
 
@@ -2306,25 +2334,37 @@ EOS
2306
2334
  section_items = @content.in_section(section)
2307
2335
  max = section_items.count - count.to_i
2308
2336
 
2337
+ opt[:after] = opt[:from][0] if opt[:from]
2338
+ opt[:before] = opt[:from][1] if opt[:from]
2339
+
2340
+ time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/
2341
+
2342
+ if opt[:before].is_a?(String) && opt[:before] =~ time_rx
2343
+ opt[:before] = opt[:before].chronify(guess: :end, future: false)
2344
+ end
2345
+
2346
+ if opt[:after].is_a?(String) && opt[:after] =~ time_rx
2347
+ opt[:after] = opt[:after].chronify(guess: :begin, future: false)
2348
+ end
2349
+
2309
2350
  counter = 0
2310
2351
 
2311
2352
  @content.map do |item|
2312
2353
  break if counter >= max
2313
- if opt[:before]
2314
- time_string = opt[:before]
2315
- cutoff = time_string.chronify(guess: :begin)
2316
- end
2317
2354
 
2318
- if (item.section.downcase != section.downcase && section != /^all$/i) || item.section.downcase == destination.downcase
2319
- item
2320
- elsif ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
2321
- item
2322
- else
2323
- counter += 1
2324
- item.move_to(destination, label: label, log: false)
2325
- Hooks.trigger :post_entry_updated, self, item.dup
2326
- item
2327
- end
2355
+ next if item.section.downcase == destination.downcase
2356
+
2357
+ next if item.section.downcase != section.downcase && section != /^all$/i
2358
+
2359
+ next if (opt[:before] && item.date > opt[:before]) || (opt[:after] && item.date < opt[:after])
2360
+
2361
+ next if (!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s))
2362
+
2363
+ counter += 1
2364
+ old_item = item.clone
2365
+ item.move_to(destination, label: label, log: false)
2366
+ Hooks.trigger :post_entry_updated, self, item, old_item
2367
+ item
2328
2368
  end
2329
2369
 
2330
2370
  if counter.positive?
data/lib/doing.rb CHANGED
@@ -28,10 +28,11 @@ require_relative 'doing/hash'
28
28
  require_relative 'doing/types'
29
29
  require_relative 'doing/colors'
30
30
  require_relative 'doing/template_string'
31
- require_relative 'doing/string'
31
+ require_relative 'doing/string/string'
32
32
  require_relative 'doing/time'
33
- require_relative 'doing/array'
34
- require_relative 'doing/symbol'
33
+ require_relative 'doing/array/array'
34
+ require_relative 'doing/good'
35
+ require_relative 'doing/normalize'
35
36
  require_relative 'doing/util'
36
37
  require_relative 'doing/util_backup'
37
38
  require_relative 'doing/configuration'
@@ -49,9 +50,7 @@ require_relative 'doing/pager'
49
50
  require_relative 'doing/completion'
50
51
  require_relative 'doing/boolean_term_parser'
51
52
  require_relative 'doing/phrase_parser'
52
- require_relative 'doing/array_chronify'
53
- require_relative 'doing/numeric_chronify'
54
- require_relative 'doing/string_chronify'
53
+ require_relative 'doing/chronify/chronify'
55
54
  # require 'doing/markdown_document_listener'
56
55
 
57
56
  # Main doing module
@@ -36,8 +36,7 @@ command :wiki do |c|
36
36
  c.switch [:only_timed], default_value: false, negatable: false
37
37
 
38
38
  c.action do |global, options, args|
39
- wwid = global[:wwid]
40
- tags = wwid.tag_groups([], opt: options)
39
+ tags = @wwid.tag_groups([], opt: options)
41
40
 
42
41
  wiki = Doing::Plugins.plugins.dig(:export, 'wiki', :class)
43
42
 
@@ -46,7 +45,7 @@ command :wiki do |c|
46
45
 
47
46
  raise RuntimeError, 'Missing plugin "wiki"' unless wiki
48
47
 
49
- out = wiki.render(wwid, items, variables: export_options)
48
+ out = wiki.render(@wwid, items, variables: export_options)
50
49
 
51
50
  if out
52
51
  FileUtils.mkdir_p('doing_wiki')
@@ -56,13 +55,13 @@ command :wiki do |c|
56
55
  end
57
56
  end
58
57
 
59
- template = if wwid.config['export_templates']['wiki_index'] && File.exist?(File.expand_path(wwid.config['export_templates']['wiki_index']))
60
- IO.read(File.expand_path(wwid.config['export_templates']['wiki_index']))
58
+ template = if @settings['export_templates']['wiki_index'] && File.exist?(File.expand_path(@settings['export_templates']['wiki_index']))
59
+ IO.read(File.expand_path(@settings['export_templates']['wiki_index']))
61
60
  else
62
61
  wiki.template('wiki_index')
63
62
  end
64
- style = if wwid.config['export_templates']['wiki_css'] && File.exist?(File.expand_path(wwid.config['export_templates']['wiki_css']))
65
- IO.read(File.expand_path(wwid.config['export_templates']['wiki_css']))
63
+ style = if @settings['export_templates']['wiki_css'] && File.exist?(File.expand_path(@settings['export_templates']['wiki_css']))
64
+ IO.read(File.expand_path(@settings['export_templates']['wiki_css']))
66
65
  else
67
66
  wiki.template('wiki_css')
68
67
  end
@@ -74,7 +74,7 @@ module Doing
74
74
  self.template('wiki_css')
75
75
  end
76
76
 
77
- totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
77
+ totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
78
78
  engine = Haml::Engine.new(template)
79
79
  Doing.logger.debug('Wiki Export:', "#{items_out.count} items output to #{variables[:page_title]} wiki page")
80
80
  @out = engine.render(Object.new,