doing 2.1.22 → 2.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardoc/checksums +17 -14
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +323 -111
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +2 -1
- data/bin/commands/add_section.rb +13 -0
- data/bin/commands/again.rb +99 -0
- data/bin/commands/archive.rb +96 -0
- data/bin/commands/cancel.rb +102 -0
- data/bin/commands/changes.rb +42 -0
- data/bin/commands/choose.rb +9 -0
- data/bin/commands/colors.rb +19 -0
- data/bin/commands/commands.rb +87 -0
- data/bin/commands/commands_accepting.rb +25 -0
- data/bin/commands/completion.rb +24 -0
- data/bin/commands/config.rb +245 -0
- data/bin/commands/done.rb +249 -0
- data/bin/commands/finish.rb +149 -0
- data/bin/commands/flag.rb +126 -0
- data/bin/commands/grep.rb +124 -0
- data/bin/commands/import.rb +101 -0
- data/bin/commands/install_fzf.rb +17 -0
- data/bin/commands/last.rb +114 -0
- data/bin/commands/meanwhile.rb +86 -0
- data/bin/commands/note.rb +130 -0
- data/bin/commands/now.rb +151 -0
- data/bin/commands/on.rb +66 -0
- data/bin/commands/open.rb +53 -0
- data/bin/commands/plugins.rb +23 -0
- data/bin/commands/recent.rb +78 -0
- data/bin/commands/redo.rb +22 -0
- data/bin/commands/reset.rb +106 -0
- data/bin/commands/rotate.rb +73 -0
- data/bin/commands/sections.rb +11 -0
- data/bin/commands/select.rb +123 -0
- data/bin/commands/show.rb +231 -0
- data/bin/commands/since.rb +64 -0
- data/bin/commands/tag.rb +179 -0
- data/bin/commands/tag_dir.rb +29 -0
- data/bin/commands/tags.rb +93 -0
- data/bin/commands/template.rb +61 -0
- data/bin/commands/today.rb +65 -0
- data/bin/commands/undo.rb +49 -0
- data/bin/commands/view.rb +238 -0
- data/bin/commands/views.rb +11 -0
- data/bin/commands/yesterday.rb +73 -0
- data/bin/doing +54 -3505
- data/docs/doc/Array.html +79 -11
- data/docs/doc/BooleanTermParser/Clause.html +5 -5
- data/docs/doc/BooleanTermParser/Operator.html +4 -4
- data/docs/doc/BooleanTermParser/Query.html +8 -8
- data/docs/doc/BooleanTermParser/QueryParser.html +2 -2
- data/docs/doc/BooleanTermParser/QueryTransformer.html +2 -2
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/Color.html +4 -4
- data/docs/doc/Doing/Completion.html +2 -2
- data/docs/doc/Doing/Configuration.html +17 -18
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +2 -2
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +2 -2
- data/docs/doc/Doing/Errors/DoingStandardError.html +2 -2
- data/docs/doc/Doing/Errors/EmptyInput.html +2 -2
- data/docs/doc/Doing/Errors/NoResults.html +2 -2
- data/docs/doc/Doing/Errors/PluginException.html +3 -3
- data/docs/doc/Doing/Errors/UserCancelled.html +2 -2
- data/docs/doc/Doing/Errors/WrongCommand.html +2 -2
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/Hooks.html +6 -6
- data/docs/doc/Doing/Item.html +50 -16
- data/docs/doc/Doing/Items.html +10 -10
- data/docs/doc/Doing/LogAdapter.html +24 -24
- data/docs/doc/Doing/Note.html +7 -7
- data/docs/doc/Doing/Pager.html +4 -4
- data/docs/doc/Doing/Plugins.html +7 -7
- data/docs/doc/Doing/Prompt.html +59 -14
- data/docs/doc/Doing/Section.html +6 -6
- data/docs/doc/Doing/TemplateString.html +8 -8
- data/docs/doc/Doing/Types.html +206 -0
- data/docs/doc/Doing/Util/Backup.html +10 -10
- data/docs/doc/Doing/Util.html +16 -19
- data/docs/doc/Doing/WWID.html +65 -53
- data/docs/doc/Doing.html +3 -3
- data/docs/doc/FalseClass.html +201 -0
- data/docs/doc/GLI/Commands/Help.html +185 -0
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +17 -17
- data/docs/doc/GLI/Commands.html +5 -3
- data/docs/doc/GLI.html +4 -2
- data/docs/doc/Hash.html +47 -21
- data/docs/doc/Numeric.html +5 -5
- data/docs/doc/Object.html +203 -0
- data/docs/doc/PhraseParser/Operator.html +4 -4
- data/docs/doc/PhraseParser/PhraseClause.html +5 -5
- data/docs/doc/PhraseParser/Query.html +10 -10
- data/docs/doc/PhraseParser/QueryParser.html +2 -2
- data/docs/doc/PhraseParser/QueryTransformer.html +2 -2
- data/docs/doc/PhraseParser/TermClause.html +5 -5
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +7 -7
- data/docs/doc/String.html +144 -51
- data/docs/doc/Symbol.html +8 -8
- data/docs/doc/Time.html +6 -6
- data/docs/doc/TrueClass.html +201 -0
- data/docs/doc/_index.html +46 -16
- data/docs/doc/class_list.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +292 -212
- data/docs/doc/top-level-namespace.html +2 -2
- data/docs/index.md +1 -1
- data/doing.rdoc +178 -16
- data/example_plugin.rb +2 -2
- data/lib/completion/_doing.zsh +27 -27
- data/lib/completion/doing.bash +31 -20
- data/lib/completion/doing.fish +33 -11
- data/lib/doing/array.rb +2 -2
- data/lib/doing/changelog/change.rb +115 -0
- data/lib/doing/changelog/changes.rb +73 -0
- data/lib/doing/changelog/entry.rb +21 -0
- data/lib/doing/changelog/version.rb +97 -0
- data/lib/doing/changelog.rb +6 -0
- data/lib/doing/completion/fish_completion.rb +2 -1
- data/lib/doing/configuration.rb +20 -13
- data/lib/doing/good.rb +64 -0
- data/lib/doing/hash.rb +7 -2
- data/lib/doing/help_monkey_patch.rb +31 -0
- data/lib/doing/hooks.rb +8 -4
- data/lib/doing/item.rb +24 -35
- data/lib/doing/pager.rb +1 -0
- data/lib/doing/plugins/export/template_export.rb +1 -1
- data/lib/doing/plugins/import/calendar_import.rb +1 -1
- data/lib/doing/plugins/import/doing_import.rb +1 -1
- data/lib/doing/plugins/import/timing_import.rb +1 -1
- data/lib/doing/prompt.rb +8 -0
- data/lib/doing/string.rb +20 -11
- data/lib/doing/string_chronify.rb +1 -1
- data/lib/doing/template_string.rb +2 -2
- data/lib/doing/types.rb +3 -0
- data/lib/doing/util.rb +12 -11
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +62 -37
- data/lib/doing.rb +2 -0
- data/lib/examples/commands/wiki.rb +6 -7
- data/lib/helpers/threaded_tests.rb +61 -71
- data/lib/helpers/threaded_tests_string.rb +50 -0
- metadata +56 -2
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.
|
|
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
|
|
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
|
|
@@ -651,7 +663,7 @@ module Doing
|
|
|
651
663
|
|
|
652
664
|
if items.nil? || items.empty?
|
|
653
665
|
section = opt[:section] ? guess_section(opt[:section]) : 'All'
|
|
654
|
-
items = section =~ /^all$/i ? @content.
|
|
666
|
+
items = section =~ /^all$/i ? @content.clone : @content.in_section(section)
|
|
655
667
|
end
|
|
656
668
|
|
|
657
669
|
opt[:time_filter] = [nil, nil]
|
|
@@ -847,7 +859,7 @@ module Doing
|
|
|
847
859
|
note.map!(&:strip)
|
|
848
860
|
note.delete_if(&:ignore?)
|
|
849
861
|
item = items[i]
|
|
850
|
-
old_item = item.
|
|
862
|
+
old_item = item.clone
|
|
851
863
|
item.date = date || items[i].date
|
|
852
864
|
item.title = title
|
|
853
865
|
item.note = note
|
|
@@ -855,7 +867,7 @@ module Doing
|
|
|
855
867
|
Doing.logger.count(:skipped, level: :debug)
|
|
856
868
|
else
|
|
857
869
|
Doing.logger.count(:updated)
|
|
858
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
870
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
859
871
|
end
|
|
860
872
|
end
|
|
861
873
|
end
|
|
@@ -1045,9 +1057,10 @@ module Doing
|
|
|
1045
1057
|
else
|
|
1046
1058
|
opt[:resume]
|
|
1047
1059
|
end
|
|
1060
|
+
old_item = item.clone
|
|
1048
1061
|
new_entry = reset_item(item, date: date, resume: res)
|
|
1049
1062
|
@content.update_item(item, new_entry)
|
|
1050
|
-
Hooks.trigger :post_entry_updated, self, new_entry
|
|
1063
|
+
Hooks.trigger :post_entry_updated, self, new_entry, old_item
|
|
1051
1064
|
end
|
|
1052
1065
|
write(@doing_file)
|
|
1053
1066
|
|
|
@@ -1062,8 +1075,9 @@ module Doing
|
|
|
1062
1075
|
if opt[:flag]
|
|
1063
1076
|
tag = @config['marker_tag'] || 'flagged'
|
|
1064
1077
|
items.map! do |i|
|
|
1078
|
+
old_item = i.clone
|
|
1065
1079
|
i.tag(tag, date: false, remove: opt[:remove], single: single)
|
|
1066
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1080
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1067
1081
|
end
|
|
1068
1082
|
end
|
|
1069
1083
|
|
|
@@ -1071,9 +1085,10 @@ module Doing
|
|
|
1071
1085
|
tag = 'done'
|
|
1072
1086
|
items.map! do |i|
|
|
1073
1087
|
if i.should_finish?
|
|
1088
|
+
old_item = i.clone
|
|
1074
1089
|
should_date = !opt[:cancel] && i.should_time?
|
|
1075
1090
|
i.tag(tag, date: should_date, remove: opt[:remove], single: single)
|
|
1076
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1091
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1077
1092
|
end
|
|
1078
1093
|
end
|
|
1079
1094
|
end
|
|
@@ -1087,8 +1102,9 @@ module Doing
|
|
|
1087
1102
|
else
|
|
1088
1103
|
logger.count(:added_tags)
|
|
1089
1104
|
logger.write(items.count == 1 ? :info : :debug, 'Tagged:', new_title)
|
|
1105
|
+
old_item = i.clone
|
|
1090
1106
|
i.title = new_title
|
|
1091
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1107
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1092
1108
|
end
|
|
1093
1109
|
end
|
|
1094
1110
|
end
|
|
@@ -1096,17 +1112,19 @@ module Doing
|
|
|
1096
1112
|
if opt[:tag]
|
|
1097
1113
|
tag = opt[:tag]
|
|
1098
1114
|
items.map! do |i|
|
|
1115
|
+
old_item = i.clone
|
|
1099
1116
|
i.tag(tag, date: false, remove: opt[:remove], single: single)
|
|
1100
1117
|
i.expand_date_tags(@config['date_tags'])
|
|
1101
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1118
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1102
1119
|
end
|
|
1103
1120
|
end
|
|
1104
1121
|
|
|
1105
1122
|
if opt[:archive] || opt[:move]
|
|
1106
1123
|
section = opt[:archive] ? 'Archive' : guess_section(opt[:move])
|
|
1107
1124
|
items.map! do |i|
|
|
1125
|
+
old_item = i.clone
|
|
1108
1126
|
i.move_to(section, label: true)
|
|
1109
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1127
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1110
1128
|
end
|
|
1111
1129
|
end
|
|
1112
1130
|
|
|
@@ -1228,6 +1246,7 @@ module Doing
|
|
|
1228
1246
|
end
|
|
1229
1247
|
|
|
1230
1248
|
items.each do |item|
|
|
1249
|
+
old_item = item.clone
|
|
1231
1250
|
added = []
|
|
1232
1251
|
removed = []
|
|
1233
1252
|
|
|
@@ -1265,7 +1284,7 @@ module Doing
|
|
|
1265
1284
|
|
|
1266
1285
|
tag = tag.strip
|
|
1267
1286
|
|
|
1268
|
-
if tag =~ /^done$/
|
|
1287
|
+
if tag =~ /^done$/ && opt[:date] && item.should_time?
|
|
1269
1288
|
max_elapsed = @config.dig('interaction', 'confirm_longer_than') || 0
|
|
1270
1289
|
max_elapsed = max_elapsed.chronify_qty if max_elapsed.is_a?(String)
|
|
1271
1290
|
elapsed = done_date - item.date
|
|
@@ -1322,7 +1341,7 @@ module Doing
|
|
|
1322
1341
|
end
|
|
1323
1342
|
|
|
1324
1343
|
item.expand_date_tags(@config['date_tags'])
|
|
1325
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
1344
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
1326
1345
|
end
|
|
1327
1346
|
|
|
1328
1347
|
write(@doing_file)
|
|
@@ -1361,6 +1380,7 @@ module Doing
|
|
|
1361
1380
|
return
|
|
1362
1381
|
end
|
|
1363
1382
|
|
|
1383
|
+
old_item = item.clone
|
|
1364
1384
|
content = ["#{item.date.strftime('%F %R')} | #{item.title.dup}"]
|
|
1365
1385
|
content << item.note.strip_lines.join("\n") unless item.note.empty?
|
|
1366
1386
|
new_item = fork_editor(content.join("\n"))
|
|
@@ -1376,7 +1396,7 @@ module Doing
|
|
|
1376
1396
|
item.title = title
|
|
1377
1397
|
item.note.add(note, replace: true)
|
|
1378
1398
|
logger.info('Edited:', item.title)
|
|
1379
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
1399
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
1380
1400
|
|
|
1381
1401
|
write(@doing_file)
|
|
1382
1402
|
end
|
|
@@ -1413,6 +1433,7 @@ module Doing
|
|
|
1413
1433
|
found_items = 0
|
|
1414
1434
|
|
|
1415
1435
|
@content.each_with_index do |item, i|
|
|
1436
|
+
old_item = i.clone
|
|
1416
1437
|
next unless item.section == opt[:section] || opt[:section] =~ /all/i
|
|
1417
1438
|
|
|
1418
1439
|
next unless item.title =~ /@#{tag}/
|
|
@@ -1431,7 +1452,7 @@ module Doing
|
|
|
1431
1452
|
logger.count(:completed)
|
|
1432
1453
|
logger.info('Completed:', item.title)
|
|
1433
1454
|
end
|
|
1434
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
1455
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
1435
1456
|
end
|
|
1436
1457
|
|
|
1437
1458
|
|
|
@@ -1492,7 +1513,7 @@ module Doing
|
|
|
1492
1513
|
|
|
1493
1514
|
unless ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
|
|
1494
1515
|
new_item = @content.delete(item)
|
|
1495
|
-
Hooks.trigger :post_entry_removed, self, item.
|
|
1516
|
+
Hooks.trigger :post_entry_removed, self, item.clone
|
|
1496
1517
|
raise DoingRuntimeError, "Error deleting item: #{item}" if new_item.nil?
|
|
1497
1518
|
|
|
1498
1519
|
new_content.add_section(new_item.section, log: false)
|
|
@@ -1719,7 +1740,7 @@ module Doing
|
|
|
1719
1740
|
opt[:totals] ||= false
|
|
1720
1741
|
opt[:sort_tags] ||= false
|
|
1721
1742
|
|
|
1722
|
-
cfg = @config['templates'][
|
|
1743
|
+
cfg = @config['templates'][opt[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
|
|
1723
1744
|
'wrap_width' => @config['wrap_width'] || 0,
|
|
1724
1745
|
'date_format' => @config['default_date_format'],
|
|
1725
1746
|
'order' => @config['order'] || 'asc',
|
|
@@ -1728,6 +1749,8 @@ module Doing
|
|
|
1728
1749
|
'interval_format' => @config['interval_format']
|
|
1729
1750
|
}, { extend_existing_arrays: true, sort_merged_arrays: true })
|
|
1730
1751
|
|
|
1752
|
+
template = opt[:template] || cfg['template']
|
|
1753
|
+
|
|
1731
1754
|
opt[:duration] ||= cfg['duration'] || false
|
|
1732
1755
|
opt[:interval_format] ||= cfg['interval_format'] || 'text'
|
|
1733
1756
|
|
|
@@ -1743,13 +1766,13 @@ module Doing
|
|
|
1743
1766
|
output: output,
|
|
1744
1767
|
section: opt[:section],
|
|
1745
1768
|
sort_tags: opt[:sort_tags],
|
|
1746
|
-
template:
|
|
1769
|
+
template: template,
|
|
1747
1770
|
times: times,
|
|
1748
1771
|
today: true,
|
|
1749
1772
|
totals: opt[:totals],
|
|
1750
1773
|
wrap_width: cfg['wrap_width'],
|
|
1751
1774
|
tags_color: cfg['tags_color'],
|
|
1752
|
-
config_template:
|
|
1775
|
+
config_template: opt[:config_template]
|
|
1753
1776
|
}
|
|
1754
1777
|
list_section(options)
|
|
1755
1778
|
end
|
|
@@ -1781,7 +1804,8 @@ module Doing
|
|
|
1781
1804
|
totals: opt[:totals],
|
|
1782
1805
|
duration: opt[:duration],
|
|
1783
1806
|
sort_tags: opt[:sort_tags],
|
|
1784
|
-
|
|
1807
|
+
template: opt[:template],
|
|
1808
|
+
config_template: opt[:config_template]
|
|
1785
1809
|
})
|
|
1786
1810
|
end
|
|
1787
1811
|
|
|
@@ -1816,7 +1840,8 @@ module Doing
|
|
|
1816
1840
|
times: times,
|
|
1817
1841
|
totals: opt[:totals],
|
|
1818
1842
|
yesterday: true,
|
|
1819
|
-
config_template: 'today'
|
|
1843
|
+
config_template: opt[:config_template] || 'today',
|
|
1844
|
+
template: opt[:template]
|
|
1820
1845
|
}
|
|
1821
1846
|
|
|
1822
1847
|
list_section(options)
|
|
@@ -1835,7 +1860,7 @@ module Doing
|
|
|
1835
1860
|
opt[:totals] ||= false
|
|
1836
1861
|
opt[:sort_tags] ||= false
|
|
1837
1862
|
|
|
1838
|
-
cfg = @config['templates'][
|
|
1863
|
+
cfg = @config['templates'][opt[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
|
|
1839
1864
|
'wrap_width' => @config['wrap_width'] || 0,
|
|
1840
1865
|
'date_format' => @config['default_date_format'],
|
|
1841
1866
|
'order' => @config['order'] || 'asc',
|
|
@@ -1853,10 +1878,9 @@ module Doing
|
|
|
1853
1878
|
opt[:wrap_width] = cfg['wrap_width']
|
|
1854
1879
|
opt[:count] = count
|
|
1855
1880
|
opt[:format] = cfg['date_format']
|
|
1856
|
-
opt[:template] = cfg['template']
|
|
1881
|
+
opt[:template] = opt[:template] || cfg['template']
|
|
1857
1882
|
opt[:order] = 'asc'
|
|
1858
1883
|
opt[:times] = times
|
|
1859
|
-
opt[:config_template] = 'recent'
|
|
1860
1884
|
|
|
1861
1885
|
list_section(opt)
|
|
1862
1886
|
end
|
|
@@ -1869,7 +1893,7 @@ module Doing
|
|
|
1869
1893
|
##
|
|
1870
1894
|
def last(times: true, section: nil, options: {})
|
|
1871
1895
|
section = section.nil? || section =~ /all/i ? 'All' : guess_section(section)
|
|
1872
|
-
cfg = @config['templates'][
|
|
1896
|
+
cfg = @config['templates'][options[:config_template]].deep_merge(@config['templates']['default'], { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({
|
|
1873
1897
|
'wrap_width' => @config['wrap_width'] || 0,
|
|
1874
1898
|
'date_format' => @config['default_date_format'],
|
|
1875
1899
|
'order' => @config['order'] || 'asc',
|
|
@@ -1881,19 +1905,19 @@ module Doing
|
|
|
1881
1905
|
options[:interval_format] ||= cfg['interval_format'] || 'text'
|
|
1882
1906
|
|
|
1883
1907
|
opts = {
|
|
1884
|
-
|
|
1885
|
-
|
|
1908
|
+
case: options[:case],
|
|
1909
|
+
config_template: 'last',
|
|
1886
1910
|
count: 1,
|
|
1887
|
-
|
|
1888
|
-
template: cfg['template'],
|
|
1889
|
-
times: times,
|
|
1911
|
+
delete: options[:delete],
|
|
1890
1912
|
duration: options[:duration],
|
|
1913
|
+
format: cfg['date_format'],
|
|
1891
1914
|
interval_format: options[:interval_format],
|
|
1892
|
-
case: options[:case],
|
|
1893
1915
|
not: options[:negate],
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1916
|
+
section: section,
|
|
1917
|
+
template: options[:template] || cfg['template'],
|
|
1918
|
+
times: times,
|
|
1919
|
+
val: options[:val],
|
|
1920
|
+
wrap_width: cfg['wrap_width']
|
|
1897
1921
|
}
|
|
1898
1922
|
|
|
1899
1923
|
if options[:tag]
|
|
@@ -2199,7 +2223,7 @@ EOS
|
|
|
2199
2223
|
|
|
2200
2224
|
filename ||= @config['doing_file']
|
|
2201
2225
|
init_doing_file(filename)
|
|
2202
|
-
current_content = @content.
|
|
2226
|
+
current_content = @content.clone
|
|
2203
2227
|
backup_file = Util::Backup.last_backup(filename, count: 1)
|
|
2204
2228
|
raise DoingRuntimeError, 'No undo history to diff' if backup_file.nil?
|
|
2205
2229
|
|
|
@@ -2318,8 +2342,9 @@ EOS
|
|
|
2318
2342
|
item
|
|
2319
2343
|
else
|
|
2320
2344
|
counter += 1
|
|
2345
|
+
old_item = item.clone
|
|
2321
2346
|
item.move_to(destination, label: label, log: false)
|
|
2322
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
2347
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
2323
2348
|
item
|
|
2324
2349
|
end
|
|
2325
2350
|
end
|
data/lib/doing.rb
CHANGED
|
@@ -23,6 +23,7 @@ require 'tty-markdown'
|
|
|
23
23
|
require 'tty-reader'
|
|
24
24
|
require 'tty-screen'
|
|
25
25
|
|
|
26
|
+
require_relative 'doing/changelog'
|
|
26
27
|
require_relative 'doing/hash'
|
|
27
28
|
require_relative 'doing/types'
|
|
28
29
|
require_relative 'doing/colors'
|
|
@@ -30,6 +31,7 @@ require_relative 'doing/template_string'
|
|
|
30
31
|
require_relative 'doing/string'
|
|
31
32
|
require_relative 'doing/time'
|
|
32
33
|
require_relative 'doing/array'
|
|
34
|
+
require_relative 'doing/good'
|
|
33
35
|
require_relative 'doing/symbol'
|
|
34
36
|
require_relative 'doing/util'
|
|
35
37
|
require_relative 'doing/util_backup'
|
|
@@ -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
|
-
|
|
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
|
|
60
|
-
IO.read(File.expand_path(
|
|
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
|
|
65
|
-
IO.read(File.expand_path(
|
|
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
|
|
@@ -2,66 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
require 'tty-spinner'
|
|
4
4
|
require 'tty-progressbar'
|
|
5
|
-
require './lib/doing'
|
|
6
5
|
require 'open3'
|
|
7
6
|
require 'shellwords'
|
|
7
|
+
require 'fileutils'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def highlight_errors
|
|
13
|
-
cols = `tput cols`.strip.to_i
|
|
14
|
-
|
|
15
|
-
string = dup
|
|
16
|
-
|
|
17
|
-
errs = string.scan(/(?<==\n)(?:Failure|Error):.*?(?=\n=+)/m)
|
|
18
|
-
|
|
19
|
-
errs.map! do |error|
|
|
20
|
-
err = error.dup
|
|
21
|
-
|
|
22
|
-
err.gsub!(%r{^(/.*?/)([^/:]+):(\d+):in (.*?)$}) do
|
|
23
|
-
m = Regexp.last_match
|
|
24
|
-
"#{m[1].white}#{m[2].bold.white}:#{m[3].yellow}:in #{m[4].cyan}"
|
|
25
|
-
end
|
|
26
|
-
err.gsub!(/(Failure|Error): (.*?)\((.*?)\):\n (.*?)(?=\n)/m) do
|
|
27
|
-
m = Regexp.last_match
|
|
28
|
-
[
|
|
29
|
-
m[1].bold.boldbgred.white,
|
|
30
|
-
m[3].bold.boldbgcyan.white,
|
|
31
|
-
m[2].bold.boldbgyellow.black,
|
|
32
|
-
" #{m[4]} ".bold.boldbgwhite.black.reset
|
|
33
|
-
].join(':'.boldblack.boldbgblack.reset)
|
|
34
|
-
end
|
|
35
|
-
err.gsub!(/(<.*?>) (was expected to) (.*?)\n( *<.*?>)./m) do
|
|
36
|
-
m = Regexp.last_match
|
|
37
|
-
"#{m[1].bold.green} #{m[2].white} #{m[3].boldwhite.boldbgred.reset}\n#{m[4].bold.white}"
|
|
38
|
-
end
|
|
39
|
-
err.gsub!(/(Finished in) ([\d.]+) (seconds)/) do
|
|
40
|
-
m = Regexp.last_match
|
|
41
|
-
"#{m[1].green} #{m[2].bold.white} #{m[3].green}"
|
|
42
|
-
end
|
|
43
|
-
err.gsub!(/(\d+) (failures)/) do
|
|
44
|
-
m = Regexp.last_match
|
|
45
|
-
"#{m[1].bold.red} #{m[2].red}"
|
|
46
|
-
end
|
|
47
|
-
err.gsub!(/100% passed/) do |m|
|
|
48
|
-
m.bold.green
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
err
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
errs.join("\n#{('=' * cols).blue}\n")
|
|
55
|
-
end
|
|
56
|
-
end
|
|
9
|
+
$LOAD_PATH.unshift File.join(__dir__, '..')
|
|
10
|
+
require 'doing'
|
|
11
|
+
require 'helpers/threaded_tests_string'
|
|
57
12
|
|
|
58
13
|
class ThreadedTests
|
|
59
14
|
include Doing::Color
|
|
15
|
+
include ThreadedTestString
|
|
60
16
|
|
|
61
17
|
def run(pattern: '*', max_threads: 8, max_tests: 0)
|
|
62
18
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
19
|
+
@results = File.expand_path('results.log')
|
|
63
20
|
|
|
64
|
-
max_threads = 1000 if max_threads == 0
|
|
21
|
+
max_threads = 1000 if max_threads.to_i == 0
|
|
65
22
|
|
|
66
23
|
c = Doing::Color
|
|
67
24
|
c.coloring = true
|
|
@@ -70,8 +27,8 @@ class ThreadedTests
|
|
|
70
27
|
|
|
71
28
|
tests = Dir.glob(pattern)
|
|
72
29
|
|
|
73
|
-
if max_tests > 0
|
|
74
|
-
tests = tests.slice(0, max_tests - 1)
|
|
30
|
+
if max_tests.to_i > 0
|
|
31
|
+
tests = tests.slice(0, max_tests.to_i - 1)
|
|
75
32
|
end
|
|
76
33
|
|
|
77
34
|
puts "#{tests.count} test files".boldcyan
|
|
@@ -90,13 +47,21 @@ class ThreadedTests
|
|
|
90
47
|
].join('')
|
|
91
48
|
progress = TTY::ProgressBar::Multi.new(banner,
|
|
92
49
|
width: 12,
|
|
50
|
+
clear: true,
|
|
93
51
|
hide_cursor: true)
|
|
94
52
|
@children = []
|
|
95
53
|
tests.each do |t|
|
|
96
54
|
test_name = File.basename(t, '.rb').sub(/doing_(.*?)_test/, '\1')
|
|
97
55
|
new_sp = progress.register("[#{':bar'.cyan}] #{test_name.bold.white}:status",
|
|
98
|
-
total:
|
|
99
|
-
|
|
56
|
+
total: tests.count + 8,
|
|
57
|
+
width: 1,
|
|
58
|
+
head: ' ',
|
|
59
|
+
unknown: ' ',
|
|
60
|
+
hide_cursor: true,
|
|
61
|
+
clear: true)
|
|
62
|
+
status = ': waiting'.dark.yellow.reset
|
|
63
|
+
@children.push([test_name, new_sp, status])
|
|
64
|
+
# new_sp.advance(status: ': waiting'.dark.yellow.reset)
|
|
100
65
|
end
|
|
101
66
|
|
|
102
67
|
@elapsed = 0.0
|
|
@@ -105,6 +70,7 @@ class ThreadedTests
|
|
|
105
70
|
@error_out = []
|
|
106
71
|
# progress.start
|
|
107
72
|
@threads = []
|
|
73
|
+
@running_tests = []
|
|
108
74
|
|
|
109
75
|
begin
|
|
110
76
|
while @children.count.positive?
|
|
@@ -123,34 +89,60 @@ class ThreadedTests
|
|
|
123
89
|
finish_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
124
90
|
|
|
125
91
|
progress.finish
|
|
92
|
+
rescue
|
|
93
|
+
progress.stop
|
|
94
|
+
ensure
|
|
95
|
+
msg = @running_tests.map { |t| t[1].format.uncolor.sub(/^\[:bar\] (.*?):status/, "#{c.bold}#{c.white}\\1#{c.reset}#{t[2]}") }.join("\n")
|
|
96
|
+
|
|
97
|
+
Doing::Prompt.clear_screen(msg)
|
|
126
98
|
|
|
127
99
|
output = []
|
|
128
|
-
if @error_out.count.positive?
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
100
|
+
output << if @error_out.count.positive?
|
|
101
|
+
c.boldred("#{@error_out.count} Issues")
|
|
102
|
+
else
|
|
103
|
+
c.green('Success')
|
|
104
|
+
end
|
|
133
105
|
output << c.green("#{@test_total} tests")
|
|
134
106
|
output << c.cyan("#{@assrt_total} assertions")
|
|
135
107
|
output << c.yellow("#{(finish_time - start_time).round(3)}s")
|
|
136
108
|
puts output.join(', ')
|
|
137
109
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
110
|
+
if @error_out.count.positive?
|
|
111
|
+
res = Doing::Prompt.yn('Display error report?', default_response: false)
|
|
112
|
+
Doing::Pager.paginate = true
|
|
113
|
+
Doing::Pager.page(@error_out.join("\n----\n".boldwhite)) if res
|
|
114
|
+
end
|
|
141
115
|
end
|
|
142
116
|
end
|
|
143
117
|
|
|
144
118
|
def run_test(s)
|
|
145
119
|
bar = s[1]
|
|
146
|
-
|
|
120
|
+
s[2] = ": #{'running'.green}"
|
|
121
|
+
bar.advance(status: s[2])
|
|
122
|
+
|
|
123
|
+
if @running_tests.count.positive?
|
|
124
|
+
@running_tests.each do |b|
|
|
125
|
+
prev_bar = b[1]
|
|
126
|
+
if prev_bar.complete?
|
|
127
|
+
prev_bar.reset
|
|
128
|
+
prev_bar.advance(status: b[2])
|
|
129
|
+
prev_bar.finish
|
|
130
|
+
else
|
|
131
|
+
prev_bar.update(head: ' ', unfinished: ' ')
|
|
132
|
+
prev_bar.advance(status: b[2])
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
147
136
|
|
|
137
|
+
@running_tests.push(s)
|
|
148
138
|
out, _err, status = Open3.capture3(ENV, 'rake', "test:#{s[0]}", stdin_data: nil)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
139
|
+
time = out.match(/^Finished in (?<time>\d+\.\d+) seconds\./)
|
|
140
|
+
count = out.match(/^(?<tests>\d+) tests, (?<assrt>\d+) assertions, (?<fails>\d+) failures, (?<errs>\d+) errors/)
|
|
141
|
+
|
|
142
|
+
unless status.success? && !count['fails'].to_i.positive? && !count['errs'].to_i.positive?
|
|
143
|
+
s[2] = ": #{count['fails'].bold.red} #{'failures'.red}, #{count['errs'].bold.red} #{'errors'.red}"
|
|
152
144
|
bar.update(head: '✖'.boldred)
|
|
153
|
-
bar.advance(head: '✖'.boldred, status:
|
|
145
|
+
bar.advance(head: '✖'.boldred, status: s[2])
|
|
154
146
|
|
|
155
147
|
# errs = out.scan(/(?:Failure|Error): [\w_]+\((?:.*?)\):(?:.*?)(?=\n=======)/m)
|
|
156
148
|
@error_out.push(out.highlight_errors)
|
|
@@ -160,9 +152,7 @@ class ThreadedTests
|
|
|
160
152
|
Thread.exit
|
|
161
153
|
end
|
|
162
154
|
|
|
163
|
-
|
|
164
|
-
count = out.match(/^(?<tests>\d+) tests, (?<assrt>\d+) assertions, (?<fails>\d+) failures, (?<errs>\d+) errors/)
|
|
165
|
-
status = [
|
|
155
|
+
s[2] = [
|
|
166
156
|
': ',
|
|
167
157
|
count['tests'].green,
|
|
168
158
|
'/',
|
|
@@ -177,7 +167,7 @@ class ThreadedTests
|
|
|
177
167
|
's'
|
|
178
168
|
].join('')
|
|
179
169
|
bar.update(head: '✔'.boldgreen)
|
|
180
|
-
bar.advance(head: '✔'.boldgreen, status:
|
|
170
|
+
bar.advance(head: '✔'.boldgreen, status: s[2])
|
|
181
171
|
@test_total += count['tests'].to_i
|
|
182
172
|
@assrt_total += count['assrt'].to_i
|
|
183
173
|
@elapsed += time['time'].to_f
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module ThreadedTestString
|
|
2
|
+
class ::String
|
|
3
|
+
include Doing::Color
|
|
4
|
+
|
|
5
|
+
def highlight_errors
|
|
6
|
+
cols = `tput cols`.strip.to_i
|
|
7
|
+
|
|
8
|
+
string = dup
|
|
9
|
+
|
|
10
|
+
errs = string.scan(/(?<==\n)(?:Failure|Error):.*?(?=\n=+)/m)
|
|
11
|
+
|
|
12
|
+
errs.map! do |error|
|
|
13
|
+
err = error.dup
|
|
14
|
+
|
|
15
|
+
err.gsub!(%r{^(/.*?/)([^/:]+):(\d+):in (.*?)$}) do
|
|
16
|
+
m = Regexp.last_match
|
|
17
|
+
"#{m[1].white}#{m[2].bold.white}:#{m[3].yellow}:in #{m[4].cyan}"
|
|
18
|
+
end
|
|
19
|
+
err.gsub!(/(Failure|Error): (.*?)\((.*?)\):\n (.*?)(?=\n)/m) do
|
|
20
|
+
m = Regexp.last_match
|
|
21
|
+
[
|
|
22
|
+
m[1].bold.boldbgred.white,
|
|
23
|
+
m[3].bold.boldbgcyan.white,
|
|
24
|
+
m[2].bold.boldbgyellow.black,
|
|
25
|
+
" #{m[4]} ".bold.boldbgwhite.black.reset
|
|
26
|
+
].join(':'.boldblack.boldbgblack.reset)
|
|
27
|
+
end
|
|
28
|
+
err.gsub!(/(<.*?>) (was expected to) (.*?)\n( *<.*?>)./m) do
|
|
29
|
+
m = Regexp.last_match
|
|
30
|
+
"#{m[1].bold.green} #{m[2].white} #{m[3].boldwhite.boldbgred.reset}\n#{m[4].bold.white}"
|
|
31
|
+
end
|
|
32
|
+
err.gsub!(/(Finished in) ([\d.]+) (seconds)/) do
|
|
33
|
+
m = Regexp.last_match
|
|
34
|
+
"#{m[1].green} #{m[2].bold.white} #{m[3].green}"
|
|
35
|
+
end
|
|
36
|
+
err.gsub!(/(\d+) (failures)/) do
|
|
37
|
+
m = Regexp.last_match
|
|
38
|
+
"#{m[1].bold.red} #{m[2].red}"
|
|
39
|
+
end
|
|
40
|
+
err.gsub!(/100% passed/) do |m|
|
|
41
|
+
m.bold.green
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
err
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
errs.join("\n#{('=' * cols).blue}\n")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|