doing 2.1.22 → 2.1.26
Sign up to get free protection for your applications and to get access to all the features.
- 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
|