doing 2.1.21 → 2.1.25
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 +19 -16
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +51 -13
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +6 -3
- data/bin/doing +254 -103
- data/docs/doc/Array.html +74 -34
- 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 +16 -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 +16 -16
- 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 +4 -4
- 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 +119 -21
- data/docs/doc/Numeric.html +5 -5
- 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 +206 -51
- data/docs/doc/Symbol.html +8 -8
- data/docs/doc/Time.html +6 -6
- data/docs/doc/_index.html +51 -14
- 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 +348 -252
- data/docs/doc/top-level-namespace.html +2 -93
- data/docs/index.md +1 -1
- data/doing.rdoc +177 -20
- data/example_plugin.rb +2 -2
- data/lib/completion/_doing.zsh +24 -24
- data/lib/completion/doing.bash +31 -20
- data/lib/completion/doing.fish +32 -10
- data/lib/doing/array.rb +5 -4
- data/lib/doing/array_chronify.rb +4 -3
- 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 -14
- data/lib/doing/good.rb +64 -0
- data/lib/doing/hash.rb +28 -5
- 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/log_adapter.rb +1 -1
- data/lib/doing/pager.rb +1 -1
- data/lib/doing/plugins/export/template_export.rb +9 -3
- 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 +4 -2
- data/lib/doing/string.rb +30 -12
- data/lib/doing/string_chronify.rb +1 -1
- data/lib/doing/template_string.rb +9 -2
- data/lib/doing/types.rb +23 -16
- data/lib/doing/util.rb +12 -11
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +65 -46
- data/lib/doing.rb +3 -0
- data/lib/helpers/threaded_tests.rb +106 -99
- data/lib/helpers/threaded_tests_string.rb +50 -0
- metadata +12 -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,20 +663,14 @@ 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]
|
|
658
670
|
if opt[:from] && !opt[:date_filter]
|
|
659
671
|
if opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx
|
|
660
|
-
|
|
661
|
-
elsif opt[:from].is_a?(Time)
|
|
662
|
-
start, finish = opt[:from]
|
|
663
|
-
end
|
|
664
|
-
|
|
665
|
-
if time_start
|
|
666
|
-
opt[:time_filter] = [time_start, time_end]
|
|
667
|
-
else
|
|
672
|
+
opt[:time_filter] = opt[:from]
|
|
673
|
+
elsif opt[:from][0].is_a?(Time)
|
|
668
674
|
opt[:date_filter] = opt[:from]
|
|
669
675
|
end
|
|
670
676
|
end
|
|
@@ -853,7 +859,7 @@ module Doing
|
|
|
853
859
|
note.map!(&:strip)
|
|
854
860
|
note.delete_if(&:ignore?)
|
|
855
861
|
item = items[i]
|
|
856
|
-
old_item = item.
|
|
862
|
+
old_item = item.clone
|
|
857
863
|
item.date = date || items[i].date
|
|
858
864
|
item.title = title
|
|
859
865
|
item.note = note
|
|
@@ -861,7 +867,7 @@ module Doing
|
|
|
861
867
|
Doing.logger.count(:skipped, level: :debug)
|
|
862
868
|
else
|
|
863
869
|
Doing.logger.count(:updated)
|
|
864
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
870
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
865
871
|
end
|
|
866
872
|
end
|
|
867
873
|
end
|
|
@@ -1051,9 +1057,10 @@ module Doing
|
|
|
1051
1057
|
else
|
|
1052
1058
|
opt[:resume]
|
|
1053
1059
|
end
|
|
1060
|
+
old_item = item.clone
|
|
1054
1061
|
new_entry = reset_item(item, date: date, resume: res)
|
|
1055
1062
|
@content.update_item(item, new_entry)
|
|
1056
|
-
Hooks.trigger :post_entry_updated, self, new_entry
|
|
1063
|
+
Hooks.trigger :post_entry_updated, self, new_entry, old_item
|
|
1057
1064
|
end
|
|
1058
1065
|
write(@doing_file)
|
|
1059
1066
|
|
|
@@ -1068,8 +1075,9 @@ module Doing
|
|
|
1068
1075
|
if opt[:flag]
|
|
1069
1076
|
tag = @config['marker_tag'] || 'flagged'
|
|
1070
1077
|
items.map! do |i|
|
|
1078
|
+
old_item = i.clone
|
|
1071
1079
|
i.tag(tag, date: false, remove: opt[:remove], single: single)
|
|
1072
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1080
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1073
1081
|
end
|
|
1074
1082
|
end
|
|
1075
1083
|
|
|
@@ -1077,9 +1085,10 @@ module Doing
|
|
|
1077
1085
|
tag = 'done'
|
|
1078
1086
|
items.map! do |i|
|
|
1079
1087
|
if i.should_finish?
|
|
1088
|
+
old_item = i.clone
|
|
1080
1089
|
should_date = !opt[:cancel] && i.should_time?
|
|
1081
1090
|
i.tag(tag, date: should_date, remove: opt[:remove], single: single)
|
|
1082
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1091
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1083
1092
|
end
|
|
1084
1093
|
end
|
|
1085
1094
|
end
|
|
@@ -1093,8 +1102,9 @@ module Doing
|
|
|
1093
1102
|
else
|
|
1094
1103
|
logger.count(:added_tags)
|
|
1095
1104
|
logger.write(items.count == 1 ? :info : :debug, 'Tagged:', new_title)
|
|
1105
|
+
old_item = i.clone
|
|
1096
1106
|
i.title = new_title
|
|
1097
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1107
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1098
1108
|
end
|
|
1099
1109
|
end
|
|
1100
1110
|
end
|
|
@@ -1102,17 +1112,19 @@ module Doing
|
|
|
1102
1112
|
if opt[:tag]
|
|
1103
1113
|
tag = opt[:tag]
|
|
1104
1114
|
items.map! do |i|
|
|
1115
|
+
old_item = i.clone
|
|
1105
1116
|
i.tag(tag, date: false, remove: opt[:remove], single: single)
|
|
1106
1117
|
i.expand_date_tags(@config['date_tags'])
|
|
1107
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1118
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1108
1119
|
end
|
|
1109
1120
|
end
|
|
1110
1121
|
|
|
1111
1122
|
if opt[:archive] || opt[:move]
|
|
1112
1123
|
section = opt[:archive] ? 'Archive' : guess_section(opt[:move])
|
|
1113
1124
|
items.map! do |i|
|
|
1125
|
+
old_item = i.clone
|
|
1114
1126
|
i.move_to(section, label: true)
|
|
1115
|
-
Hooks.trigger :post_entry_updated, self, i
|
|
1127
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
|
1116
1128
|
end
|
|
1117
1129
|
end
|
|
1118
1130
|
|
|
@@ -1234,6 +1246,7 @@ module Doing
|
|
|
1234
1246
|
end
|
|
1235
1247
|
|
|
1236
1248
|
items.each do |item|
|
|
1249
|
+
old_item = item.clone
|
|
1237
1250
|
added = []
|
|
1238
1251
|
removed = []
|
|
1239
1252
|
|
|
@@ -1271,7 +1284,7 @@ module Doing
|
|
|
1271
1284
|
|
|
1272
1285
|
tag = tag.strip
|
|
1273
1286
|
|
|
1274
|
-
if tag =~ /^done$/
|
|
1287
|
+
if tag =~ /^done$/ && opt[:date] && item.should_time?
|
|
1275
1288
|
max_elapsed = @config.dig('interaction', 'confirm_longer_than') || 0
|
|
1276
1289
|
max_elapsed = max_elapsed.chronify_qty if max_elapsed.is_a?(String)
|
|
1277
1290
|
elapsed = done_date - item.date
|
|
@@ -1328,7 +1341,7 @@ module Doing
|
|
|
1328
1341
|
end
|
|
1329
1342
|
|
|
1330
1343
|
item.expand_date_tags(@config['date_tags'])
|
|
1331
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
1344
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
1332
1345
|
end
|
|
1333
1346
|
|
|
1334
1347
|
write(@doing_file)
|
|
@@ -1367,6 +1380,7 @@ module Doing
|
|
|
1367
1380
|
return
|
|
1368
1381
|
end
|
|
1369
1382
|
|
|
1383
|
+
old_item = item.clone
|
|
1370
1384
|
content = ["#{item.date.strftime('%F %R')} | #{item.title.dup}"]
|
|
1371
1385
|
content << item.note.strip_lines.join("\n") unless item.note.empty?
|
|
1372
1386
|
new_item = fork_editor(content.join("\n"))
|
|
@@ -1382,7 +1396,7 @@ module Doing
|
|
|
1382
1396
|
item.title = title
|
|
1383
1397
|
item.note.add(note, replace: true)
|
|
1384
1398
|
logger.info('Edited:', item.title)
|
|
1385
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
1399
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
1386
1400
|
|
|
1387
1401
|
write(@doing_file)
|
|
1388
1402
|
end
|
|
@@ -1419,6 +1433,7 @@ module Doing
|
|
|
1419
1433
|
found_items = 0
|
|
1420
1434
|
|
|
1421
1435
|
@content.each_with_index do |item, i|
|
|
1436
|
+
old_item = i.clone
|
|
1422
1437
|
next unless item.section == opt[:section] || opt[:section] =~ /all/i
|
|
1423
1438
|
|
|
1424
1439
|
next unless item.title =~ /@#{tag}/
|
|
@@ -1437,7 +1452,7 @@ module Doing
|
|
|
1437
1452
|
logger.count(:completed)
|
|
1438
1453
|
logger.info('Completed:', item.title)
|
|
1439
1454
|
end
|
|
1440
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
1455
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
1441
1456
|
end
|
|
1442
1457
|
|
|
1443
1458
|
|
|
@@ -1498,7 +1513,7 @@ module Doing
|
|
|
1498
1513
|
|
|
1499
1514
|
unless ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
|
|
1500
1515
|
new_item = @content.delete(item)
|
|
1501
|
-
Hooks.trigger :post_entry_removed, self, item.
|
|
1516
|
+
Hooks.trigger :post_entry_removed, self, item.clone
|
|
1502
1517
|
raise DoingRuntimeError, "Error deleting item: #{item}" if new_item.nil?
|
|
1503
1518
|
|
|
1504
1519
|
new_content.add_section(new_item.section, log: false)
|
|
@@ -1622,6 +1637,7 @@ module Doing
|
|
|
1622
1637
|
'duration' => @config['duration'],
|
|
1623
1638
|
'interval_format' => @config['interval_format']
|
|
1624
1639
|
}, { extend_existing_arrays: true, sort_merged_arrays: true })
|
|
1640
|
+
|
|
1625
1641
|
opt[:duration] ||= cfg['duration'] || false
|
|
1626
1642
|
opt[:interval_format] ||= cfg['interval_format'] || 'text'
|
|
1627
1643
|
opt[:count] ||= 0
|
|
@@ -1724,7 +1740,7 @@ module Doing
|
|
|
1724
1740
|
opt[:totals] ||= false
|
|
1725
1741
|
opt[:sort_tags] ||= false
|
|
1726
1742
|
|
|
1727
|
-
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({
|
|
1728
1744
|
'wrap_width' => @config['wrap_width'] || 0,
|
|
1729
1745
|
'date_format' => @config['default_date_format'],
|
|
1730
1746
|
'order' => @config['order'] || 'asc',
|
|
@@ -1733,6 +1749,8 @@ module Doing
|
|
|
1733
1749
|
'interval_format' => @config['interval_format']
|
|
1734
1750
|
}, { extend_existing_arrays: true, sort_merged_arrays: true })
|
|
1735
1751
|
|
|
1752
|
+
template = opt[:template] || cfg['template']
|
|
1753
|
+
|
|
1736
1754
|
opt[:duration] ||= cfg['duration'] || false
|
|
1737
1755
|
opt[:interval_format] ||= cfg['interval_format'] || 'text'
|
|
1738
1756
|
|
|
@@ -1748,13 +1766,13 @@ module Doing
|
|
|
1748
1766
|
output: output,
|
|
1749
1767
|
section: opt[:section],
|
|
1750
1768
|
sort_tags: opt[:sort_tags],
|
|
1751
|
-
template:
|
|
1769
|
+
template: template,
|
|
1752
1770
|
times: times,
|
|
1753
1771
|
today: true,
|
|
1754
1772
|
totals: opt[:totals],
|
|
1755
1773
|
wrap_width: cfg['wrap_width'],
|
|
1756
1774
|
tags_color: cfg['tags_color'],
|
|
1757
|
-
config_template:
|
|
1775
|
+
config_template: opt[:config_template]
|
|
1758
1776
|
}
|
|
1759
1777
|
list_section(options)
|
|
1760
1778
|
end
|
|
@@ -1786,7 +1804,8 @@ module Doing
|
|
|
1786
1804
|
totals: opt[:totals],
|
|
1787
1805
|
duration: opt[:duration],
|
|
1788
1806
|
sort_tags: opt[:sort_tags],
|
|
1789
|
-
|
|
1807
|
+
template: opt[:template],
|
|
1808
|
+
config_template: opt[:config_template]
|
|
1790
1809
|
})
|
|
1791
1810
|
end
|
|
1792
1811
|
|
|
@@ -1821,7 +1840,8 @@ module Doing
|
|
|
1821
1840
|
times: times,
|
|
1822
1841
|
totals: opt[:totals],
|
|
1823
1842
|
yesterday: true,
|
|
1824
|
-
config_template: 'today'
|
|
1843
|
+
config_template: opt[:config_template] || 'today',
|
|
1844
|
+
template: opt[:template]
|
|
1825
1845
|
}
|
|
1826
1846
|
|
|
1827
1847
|
list_section(options)
|
|
@@ -1840,7 +1860,7 @@ module Doing
|
|
|
1840
1860
|
opt[:totals] ||= false
|
|
1841
1861
|
opt[:sort_tags] ||= false
|
|
1842
1862
|
|
|
1843
|
-
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({
|
|
1844
1864
|
'wrap_width' => @config['wrap_width'] || 0,
|
|
1845
1865
|
'date_format' => @config['default_date_format'],
|
|
1846
1866
|
'order' => @config['order'] || 'asc',
|
|
@@ -1858,10 +1878,9 @@ module Doing
|
|
|
1858
1878
|
opt[:wrap_width] = cfg['wrap_width']
|
|
1859
1879
|
opt[:count] = count
|
|
1860
1880
|
opt[:format] = cfg['date_format']
|
|
1861
|
-
opt[:template] = cfg['template']
|
|
1881
|
+
opt[:template] = opt[:template] || cfg['template']
|
|
1862
1882
|
opt[:order] = 'asc'
|
|
1863
1883
|
opt[:times] = times
|
|
1864
|
-
opt[:config_template] = 'recent'
|
|
1865
1884
|
|
|
1866
1885
|
list_section(opt)
|
|
1867
1886
|
end
|
|
@@ -1874,7 +1893,7 @@ module Doing
|
|
|
1874
1893
|
##
|
|
1875
1894
|
def last(times: true, section: nil, options: {})
|
|
1876
1895
|
section = section.nil? || section =~ /all/i ? 'All' : guess_section(section)
|
|
1877
|
-
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({
|
|
1878
1897
|
'wrap_width' => @config['wrap_width'] || 0,
|
|
1879
1898
|
'date_format' => @config['default_date_format'],
|
|
1880
1899
|
'order' => @config['order'] || 'asc',
|
|
@@ -1886,19 +1905,19 @@ module Doing
|
|
|
1886
1905
|
options[:interval_format] ||= cfg['interval_format'] || 'text'
|
|
1887
1906
|
|
|
1888
1907
|
opts = {
|
|
1889
|
-
|
|
1890
|
-
|
|
1908
|
+
case: options[:case],
|
|
1909
|
+
config_template: 'last',
|
|
1891
1910
|
count: 1,
|
|
1892
|
-
|
|
1893
|
-
template: cfg['template'],
|
|
1894
|
-
times: times,
|
|
1911
|
+
delete: options[:delete],
|
|
1895
1912
|
duration: options[:duration],
|
|
1913
|
+
format: cfg['date_format'],
|
|
1896
1914
|
interval_format: options[:interval_format],
|
|
1897
|
-
case: options[:case],
|
|
1898
1915
|
not: options[:negate],
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1916
|
+
section: section,
|
|
1917
|
+
template: options[:template] || cfg['template'],
|
|
1918
|
+
times: times,
|
|
1919
|
+
val: options[:val],
|
|
1920
|
+
wrap_width: cfg['wrap_width']
|
|
1902
1921
|
}
|
|
1903
1922
|
|
|
1904
1923
|
if options[:tag]
|
|
@@ -2204,7 +2223,7 @@ EOS
|
|
|
2204
2223
|
|
|
2205
2224
|
filename ||= @config['doing_file']
|
|
2206
2225
|
init_doing_file(filename)
|
|
2207
|
-
current_content = @content.
|
|
2226
|
+
current_content = @content.clone
|
|
2208
2227
|
backup_file = Util::Backup.last_backup(filename, count: 1)
|
|
2209
2228
|
raise DoingRuntimeError, 'No undo history to diff' if backup_file.nil?
|
|
2210
2229
|
|
|
@@ -2307,7 +2326,6 @@ EOS
|
|
|
2307
2326
|
|
|
2308
2327
|
section_items = @content.in_section(section)
|
|
2309
2328
|
max = section_items.count - count.to_i
|
|
2310
|
-
moved_items = []
|
|
2311
2329
|
|
|
2312
2330
|
counter = 0
|
|
2313
2331
|
|
|
@@ -2324,8 +2342,9 @@ EOS
|
|
|
2324
2342
|
item
|
|
2325
2343
|
else
|
|
2326
2344
|
counter += 1
|
|
2345
|
+
old_item = item.clone
|
|
2327
2346
|
item.move_to(destination, label: label, log: false)
|
|
2328
|
-
Hooks.trigger :post_entry_updated, self, item
|
|
2347
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
|
2329
2348
|
item
|
|
2330
2349
|
end
|
|
2331
2350
|
end
|
data/lib/doing.rb
CHANGED
|
@@ -23,12 +23,15 @@ 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'
|
|
28
|
+
require_relative 'doing/types'
|
|
27
29
|
require_relative 'doing/colors'
|
|
28
30
|
require_relative 'doing/template_string'
|
|
29
31
|
require_relative 'doing/string'
|
|
30
32
|
require_relative 'doing/time'
|
|
31
33
|
require_relative 'doing/array'
|
|
34
|
+
require_relative 'doing/good'
|
|
32
35
|
require_relative 'doing/symbol'
|
|
33
36
|
require_relative 'doing/util'
|
|
34
37
|
require_relative 'doing/util_backup'
|
|
@@ -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
|
-
def run(pattern: '*', max_threads:
|
|
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 =
|
|
21
|
+
max_threads = 1000 if max_threads == 0
|
|
65
22
|
|
|
66
23
|
c = Doing::Color
|
|
67
24
|
c.coloring = true
|
|
@@ -90,12 +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
|
-
children = []
|
|
52
|
+
@children = []
|
|
95
53
|
tests.each do |t|
|
|
96
54
|
test_name = File.basename(t, '.rb').sub(/doing_(.*?)_test/, '\1')
|
|
97
|
-
new_sp = progress.register("[#{':bar'.cyan}] #{test_name.bold.white}:status",
|
|
98
|
-
|
|
55
|
+
new_sp = progress.register("[#{':bar'.cyan}] #{test_name.bold.white}:status",
|
|
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)
|
|
99
65
|
end
|
|
100
66
|
|
|
101
67
|
@elapsed = 0.0
|
|
@@ -103,57 +69,21 @@ class ThreadedTests
|
|
|
103
69
|
@assrt_total = 0
|
|
104
70
|
@error_out = []
|
|
105
71
|
# progress.start
|
|
72
|
+
@threads = []
|
|
73
|
+
@running_tests = []
|
|
106
74
|
|
|
107
75
|
begin
|
|
108
|
-
while children.count.positive?
|
|
109
|
-
|
|
110
|
-
slices = children.slice!(0, max_threads)
|
|
76
|
+
while @children.count.positive?
|
|
77
|
+
|
|
78
|
+
slices = @children.slice!(0, max_threads)
|
|
111
79
|
slices.each { |c| c[1].start }
|
|
112
80
|
slices.each do |s|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
threads << Thread.new do
|
|
117
|
-
out, _err, status = Open3.capture3(ENV, 'rake', "test:#{s[0]}", stdin_data: nil)
|
|
118
|
-
unless status.success?
|
|
119
|
-
m = out.match(/(?<fail>\d+) failures, (?<err>\d+) errors/)
|
|
120
|
-
status = ": #{m['fail'].bold.red} #{'failures'.red}, #{m['err'].bold.red} #{'errors'.red}"
|
|
121
|
-
bar.update(head: '✖'.boldred)
|
|
122
|
-
bar.advance(head: '✖'.boldred, status: status)
|
|
123
|
-
|
|
124
|
-
# errs = out.scan(/(?:Failure|Error): [\w_]+\((?:.*?)\):(?:.*?)(?=\n=======)/m)
|
|
125
|
-
@error_out.push(out.highlight_errors)
|
|
126
|
-
bar.finish
|
|
127
|
-
|
|
128
|
-
Thread.exit
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
time = out.match(/^Finished in (?<time>\d+\.\d+) seconds\./)
|
|
132
|
-
count = out.match(/^(?<tests>\d+) tests, (?<assrt>\d+) assertions, (?<fails>\d+) failures, (?<errs>\d+) errors/)
|
|
133
|
-
status = [
|
|
134
|
-
': ',
|
|
135
|
-
count['tests'].green,
|
|
136
|
-
'/',
|
|
137
|
-
count['assrt'].cyan,
|
|
138
|
-
# ' (',
|
|
139
|
-
# count['fails'].to_i == 0 ? '-'.dark.white.reset : count['fails'].bold.red,
|
|
140
|
-
# '/',
|
|
141
|
-
# count['errs'].to_i == 0 ? '-'.dark.white.reset : count['errs'].bold.red,
|
|
142
|
-
# ') ',
|
|
143
|
-
' ',
|
|
144
|
-
time['time'].to_f.round(3).to_s.yellow,
|
|
145
|
-
's'
|
|
146
|
-
].join('')
|
|
147
|
-
bar.update(head: '✔'.boldgreen)
|
|
148
|
-
bar.advance(head: '✔'.boldgreen, status: status)
|
|
149
|
-
@test_total += count['tests'].to_i
|
|
150
|
-
@assrt_total += count['assrt'].to_i
|
|
151
|
-
@elapsed += time['time'].to_f
|
|
152
|
-
|
|
153
|
-
bar.finish
|
|
81
|
+
@threads << Thread.new do
|
|
82
|
+
run_test(s)
|
|
154
83
|
end
|
|
155
84
|
end
|
|
156
|
-
|
|
85
|
+
|
|
86
|
+
@threads.each { |t| t.join }
|
|
157
87
|
end
|
|
158
88
|
|
|
159
89
|
finish_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
@@ -175,6 +105,83 @@ class ThreadedTests
|
|
|
175
105
|
rescue
|
|
176
106
|
progress.stop
|
|
177
107
|
end
|
|
108
|
+
ensure
|
|
109
|
+
FileUtils.rm(@results)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def run_test(s)
|
|
113
|
+
bar = s[1]
|
|
114
|
+
s[2] = ": #{'running'.green}"
|
|
115
|
+
bar.advance(status: s[2])
|
|
116
|
+
|
|
117
|
+
if @running_tests.count.positive?
|
|
118
|
+
@running_tests.each do |b|
|
|
119
|
+
prev_bar = b[1]
|
|
120
|
+
if prev_bar.complete?
|
|
121
|
+
prev_bar.reset
|
|
122
|
+
prev_bar.advance(status: b[2])
|
|
123
|
+
prev_bar.finish
|
|
124
|
+
else
|
|
125
|
+
prev_bar.update(head: ' ', unfinished: ' ')
|
|
126
|
+
prev_bar.advance(status: b[2])
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
@running_tests.push(s)
|
|
132
|
+
out, _err, status = Open3.capture3(ENV, "rake test:#{s[0]} | tee #{@results}", stdin_data: nil)
|
|
133
|
+
unless status.success?
|
|
134
|
+
m = out.match(/(?<fail>\d+) failures, (?<err>\d+) errors/)
|
|
135
|
+
s[2] = ": #{m['fail'].bold.red} #{'failures'.red}, #{m['err'].bold.red} #{'errors'.red}"
|
|
136
|
+
bar.update(head: '✖'.boldred)
|
|
137
|
+
bar.advance(head: '✖'.boldred, status: s[2])
|
|
138
|
+
|
|
139
|
+
# errs = out.scan(/(?:Failure|Error): [\w_]+\((?:.*?)\):(?:.*?)(?=\n=======)/m)
|
|
140
|
+
@error_out.push(out.highlight_errors)
|
|
141
|
+
bar.finish
|
|
142
|
+
|
|
143
|
+
next_test
|
|
144
|
+
Thread.exit
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
time = out.match(/^Finished in (?<time>\d+\.\d+) seconds\./)
|
|
148
|
+
count = out.match(/^(?<tests>\d+) tests, (?<assrt>\d+) assertions, (?<fails>\d+) failures, (?<errs>\d+) errors/)
|
|
149
|
+
s[2] = [
|
|
150
|
+
': ',
|
|
151
|
+
count['tests'].green,
|
|
152
|
+
'/',
|
|
153
|
+
count['assrt'].cyan,
|
|
154
|
+
# ' (',
|
|
155
|
+
# count['fails'].to_i == 0 ? '-'.dark.white.reset : count['fails'].bold.red,
|
|
156
|
+
# '/',
|
|
157
|
+
# count['errs'].to_i == 0 ? '-'.dark.white.reset : count['errs'].bold.red,
|
|
158
|
+
# ') ',
|
|
159
|
+
' ',
|
|
160
|
+
time['time'].to_f.round(3).to_s.yellow,
|
|
161
|
+
's'
|
|
162
|
+
].join('')
|
|
163
|
+
bar.update(head: '✔'.boldgreen)
|
|
164
|
+
bar.advance(head: '✔'.boldgreen, status: s[2])
|
|
165
|
+
@test_total += count['tests'].to_i
|
|
166
|
+
@assrt_total += count['assrt'].to_i
|
|
167
|
+
@elapsed += time['time'].to_f
|
|
168
|
+
|
|
169
|
+
bar.finish
|
|
170
|
+
|
|
171
|
+
next_test
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def next_test
|
|
175
|
+
if @children.count.positive?
|
|
176
|
+
t = Thread.new do
|
|
177
|
+
s = @children.shift
|
|
178
|
+
# s[1].start
|
|
179
|
+
# s[1].advance(status: ": #{'running'.green}")
|
|
180
|
+
run_test(s)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
t.join
|
|
184
|
+
end
|
|
178
185
|
end
|
|
179
186
|
end
|
|
180
187
|
|
|
@@ -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
|