doing 1.0.76 → 1.0.78
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/README.md +3 -3
- data/bin/doing +17 -4
- data/lib/doing/helpers.rb +1 -1
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +168 -17
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64076f13651c82428d05f490870a3a8a2507dc783cfaeebe7658538523aaef25
|
4
|
+
data.tar.gz: 691df494543c3ff71911a4ca87135adba9504fb445d51180e9c5914372c5d35b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be005b1f54f3e170030f88a6df19567478985965b56bb50a842fe3d5c87d7afb750e165d4888da45466f3dba16205a94d1baf5cbfe723f0a18dffe960d944d00
|
7
|
+
data.tar.gz: 48e5cbec09157db3c974b5d0c242c394884a77f47ce5cf4e009b7dedf47daccba581709aefb534266415884ab71fad03c0a4d22385e3abc9d3e33576f3aaad17
|
data/README.md
CHANGED
@@ -27,7 +27,7 @@ If there's something I want to look at later but doesn't need to be added to a t
|
|
27
27
|
|
28
28
|
## Installation
|
29
29
|
|
30
|
-
The current version of `doing` is <!--VER-->1.0.
|
30
|
+
The current version of `doing` is <!--VER-->1.0.76<!--END VER-->.
|
31
31
|
|
32
32
|
$ [sudo] gem install doing
|
33
33
|
|
@@ -268,7 +268,7 @@ You can add additional custom views. Just nest them under the `views` key (inden
|
|
268
268
|
count: 5
|
269
269
|
wrap_width: 0
|
270
270
|
date_format: '%F %_I:%M%P'
|
271
|
-
template: '%date | %title%note'
|
271
|
+
template: '%date | %title%note'
|
272
272
|
|
273
273
|
The `section` key is the default section to pull entries from. Count and section can be overridden at runtime with the `-c` and `-s` flags. Setting `section` to `All` will combine all sections in the output.
|
274
274
|
|
@@ -505,7 +505,7 @@ You can include a `transform` section in the autotag config which contains pairs
|
|
505
505
|
transform:
|
506
506
|
- (\w+)-\d+:$1
|
507
507
|
|
508
|
-
This creates a search pattern looking for a string of word characters followed by a hyphen and one or more digits, e.g. `@projecttag-12`. Do not include the @ symbol in the pattern. The replacement (`$
|
508
|
+
This creates a search pattern looking for a string of word characters followed by a hyphen and one or more digits, e.g. `@projecttag-12`. Do not include the @ symbol in the pattern. The replacement (`$l`) indicates that the first matched group (in parenthesis) should be used to generate the new tag, resulting in `@projecttag` being added to the entry.
|
509
509
|
|
510
510
|
##### Annotating
|
511
511
|
|
data/bin/doing
CHANGED
@@ -257,9 +257,9 @@ command :template do |c|
|
|
257
257
|
end
|
258
258
|
|
259
259
|
desc 'Display an interactive menu to perform operations (requires fzf)'
|
260
|
-
long_desc '
|
260
|
+
long_desc 'List all entries and select with typeahead fuzzy matching.
|
261
261
|
|
262
|
-
|
262
|
+
Multiple selections are allowed, hit tab to add the highlighted entry to the selection. Return processes the selected entries.'
|
263
263
|
command :select do |c|
|
264
264
|
c.desc 'Select from a specific section'
|
265
265
|
c.arg_name 'SECTION'
|
@@ -269,6 +269,9 @@ command :select do |c|
|
|
269
269
|
c.arg_name 'TAG'
|
270
270
|
c.flag %i[t tag]
|
271
271
|
|
272
|
+
c.desc 'Reverse -c, -f, --flag, and -t (remove instead of adding)'
|
273
|
+
c.switch %i[r remove], negatable: false
|
274
|
+
|
272
275
|
# c.desc 'Add @done to selected item(s), using start time of next item as the finish time'
|
273
276
|
# c.switch %i[a auto], negatable: false, default_value: false
|
274
277
|
|
@@ -279,6 +282,10 @@ command :select do |c|
|
|
279
282
|
c.arg_name 'SECTION'
|
280
283
|
c.flag %i[m move]
|
281
284
|
|
285
|
+
c.desc 'Initial search query for filtering'
|
286
|
+
c.arg_name 'QUERY'
|
287
|
+
c.flag %i[q query]
|
288
|
+
|
282
289
|
c.desc 'Cancel selected items (add @done without timestamp)'
|
283
290
|
c.switch %i[c cancel], negatable: false, default_value: false
|
284
291
|
|
@@ -294,9 +301,15 @@ command :select do |c|
|
|
294
301
|
c.desc 'Add flag to selected item(s)'
|
295
302
|
c.switch %i[flag], negatable: false, default_value: false
|
296
303
|
|
304
|
+
c.desc 'Save selected entries to file using --output format'
|
305
|
+
c.arg_name 'FILE'
|
306
|
+
c.flag %i[save_to]
|
307
|
+
|
308
|
+
c.desc 'Output format for export (doing|taskpaper|csv|html|json|template|timeline)'
|
309
|
+
c.arg_name 'FORMAT'
|
310
|
+
c.flag %i[o output], must_match: /^(?:doing|taskpaper|html|csv|json|template|timeline)$/i
|
311
|
+
|
297
312
|
c.action do |_global_options, options, args|
|
298
|
-
section = options[:section] || 'All'
|
299
|
-
edit = options[:editor]
|
300
313
|
wwid.interactive(options)
|
301
314
|
end
|
302
315
|
end
|
data/lib/doing/helpers.rb
CHANGED
@@ -92,7 +92,7 @@ class ::String
|
|
92
92
|
def link_urls(opt = {})
|
93
93
|
opt[:format] ||= :html
|
94
94
|
if opt[:format] == :html
|
95
|
-
gsub(%r{(?mi)((http|https)://)
|
95
|
+
gsub(%r{(?mi)((http|https)://)([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&:/~+#]*[\w\-@^=%&/~+#])?}) do |_match|
|
96
96
|
m = Regexp.last_match
|
97
97
|
proto = m[1].nil? ? 'http://' : ''
|
98
98
|
%(<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>)
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -462,7 +462,7 @@ class WWID
|
|
462
462
|
## @return (Bool) yes or no
|
463
463
|
##
|
464
464
|
def yn(question, default_response: false)
|
465
|
-
default = default_response ?
|
465
|
+
default = default_response ? default_response : 'n'
|
466
466
|
|
467
467
|
# if this isn't an interactive shell, answer default
|
468
468
|
return default.downcase == 'y' unless $stdout.isatty
|
@@ -766,6 +766,22 @@ class WWID
|
|
766
766
|
all_items.max_by { |item| item['date'] }
|
767
767
|
end
|
768
768
|
|
769
|
+
##
|
770
|
+
## @brief Generate a menu of options and allow user selection
|
771
|
+
##
|
772
|
+
## @return (String) The selected option
|
773
|
+
##
|
774
|
+
def choose_from(options, prompt)
|
775
|
+
puts prompt
|
776
|
+
options.each_with_index do |section, i|
|
777
|
+
puts format('% 3d: %s', i + 1, section)
|
778
|
+
end
|
779
|
+
print "#{colors['green']}> #{colors['default']}"
|
780
|
+
num = STDIN.gets
|
781
|
+
return false if num =~ /^[a-z ]*$/i
|
782
|
+
|
783
|
+
options[num.to_i - 1]
|
784
|
+
end
|
769
785
|
|
770
786
|
##
|
771
787
|
## @brief Display an interactive menu of entries
|
@@ -773,7 +789,7 @@ class WWID
|
|
773
789
|
## @param opt (Hash) Additional options
|
774
790
|
##
|
775
791
|
def interactive(opt = {})
|
776
|
-
|
792
|
+
fzf = File.join(File.dirname(__FILE__), '../helpers/fuzzyfilefinder')
|
777
793
|
|
778
794
|
section = opt[:section] ? guess_section(opt[:section]) : 'All'
|
779
795
|
|
@@ -807,7 +823,7 @@ class WWID
|
|
807
823
|
out.join('')
|
808
824
|
end
|
809
825
|
|
810
|
-
res = `echo #{Shellwords.escape(options.join("\n"))}
|
826
|
+
res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} -m --bind ctrl-a:select-all -q "#{opt[:query]}"`
|
811
827
|
selected = []
|
812
828
|
res.split(/\n/).each do |item|
|
813
829
|
idx = item.match(/^(\d+)\)/)[1].to_i
|
@@ -819,6 +835,59 @@ class WWID
|
|
819
835
|
return
|
820
836
|
end
|
821
837
|
|
838
|
+
unless opt[:delete] || opt[:flag] || opt[:finish] || opt[:cancel] || opt[:tag] || opt[:archive] || opt[:output] || opt[:save_to]
|
839
|
+
action = choose_from(
|
840
|
+
[
|
841
|
+
"add tag",
|
842
|
+
"remove tag",
|
843
|
+
"archive",
|
844
|
+
"cancel",
|
845
|
+
"delete",
|
846
|
+
"edit",
|
847
|
+
"finish",
|
848
|
+
"flag",
|
849
|
+
"move",
|
850
|
+
"output format"
|
851
|
+
],
|
852
|
+
'What do you want to do with the selected items?')
|
853
|
+
case action
|
854
|
+
when /(add|remove) tag/
|
855
|
+
print "Enter tag: "
|
856
|
+
tag = STDIN.gets
|
857
|
+
return if tag =~ /^ *$/
|
858
|
+
opt[:tag] = tag.strip
|
859
|
+
opt[:remove] = true if action =~ /remove tag/
|
860
|
+
when /output format/
|
861
|
+
output_format = choose_from(%w[doing taskpaper json timeline html csv], 'Which output format?')
|
862
|
+
return if tag =~ /^ *$/
|
863
|
+
opt[:output] = output_format.strip
|
864
|
+
res = yn("Save to file?", default_response: 'n')
|
865
|
+
if res
|
866
|
+
print "File path/name: "
|
867
|
+
filename = STDIN.gets.strip
|
868
|
+
return if filename.empty?
|
869
|
+
opt[:save_to] = filename
|
870
|
+
end
|
871
|
+
when /archive/
|
872
|
+
opt[:archive] = true
|
873
|
+
when /delete/
|
874
|
+
opt[:delete] = true
|
875
|
+
when /edit/
|
876
|
+
opt[:editor] = true
|
877
|
+
when /finish/
|
878
|
+
opt[:finish] = true
|
879
|
+
when /cancel/
|
880
|
+
opt[:cancel] = true
|
881
|
+
when /move/
|
882
|
+
section = choose_section.strip
|
883
|
+
return if section =~ /^ *$/
|
884
|
+
opt[:move] = section.strip
|
885
|
+
when /flag/
|
886
|
+
opt[:flag] = true
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
|
822
891
|
if opt[:delete]
|
823
892
|
res = yn("Delete #{selected.size} items?", default_response: 'y')
|
824
893
|
if res
|
@@ -830,17 +899,35 @@ class WWID
|
|
830
899
|
|
831
900
|
if opt[:flag]
|
832
901
|
tag = @config['marker_tag'] || 'flagged'
|
833
|
-
selected.map!
|
902
|
+
selected.map! do |item|
|
903
|
+
if opt[:remove]
|
904
|
+
untag_item(item, tag)
|
905
|
+
else
|
906
|
+
tag_item(item, tag, date: false)
|
907
|
+
end
|
908
|
+
end
|
834
909
|
end
|
835
910
|
|
836
911
|
if opt[:finish] || opt[:cancel]
|
837
912
|
tag = 'done'
|
838
|
-
selected.map!
|
913
|
+
selected.map! do |item|
|
914
|
+
if opt[:remove]
|
915
|
+
untag_item(item, tag)
|
916
|
+
else
|
917
|
+
tag_item(item, tag, date: !opt[:cancel])
|
918
|
+
end
|
919
|
+
end
|
839
920
|
end
|
840
921
|
|
841
922
|
if opt[:tag]
|
842
923
|
tag = opt[:tag]
|
843
|
-
selected.map!
|
924
|
+
selected.map! do |item|
|
925
|
+
if opt[:remove]
|
926
|
+
untag_item(item, tag)
|
927
|
+
else
|
928
|
+
tag_item(item, tag, date: false)
|
929
|
+
end
|
930
|
+
end
|
844
931
|
end
|
845
932
|
|
846
933
|
if opt[:archive] || opt[:move]
|
@@ -891,6 +978,40 @@ class WWID
|
|
891
978
|
|
892
979
|
write(@doing_file)
|
893
980
|
end
|
981
|
+
|
982
|
+
if opt[:output]
|
983
|
+
selected.map! do |item|
|
984
|
+
item['title'] = "#{item['title']} @project(#{item['section']})"
|
985
|
+
item
|
986
|
+
end
|
987
|
+
|
988
|
+
@content = {'Export' => {'original' => 'Export:', 'items' => selected}}
|
989
|
+
options = {section: 'Export'}
|
990
|
+
|
991
|
+
if opt[:output] !~ /(doing|taskpaper)/
|
992
|
+
options[:output] = opt[:output]
|
993
|
+
else
|
994
|
+
options[:template] = '- %date | %title%note'
|
995
|
+
end
|
996
|
+
|
997
|
+
output = list_section(options)
|
998
|
+
|
999
|
+
if opt[:save_to]
|
1000
|
+
file = File.expand_path(opt[:save_to])
|
1001
|
+
if File.exist?(file)
|
1002
|
+
# Create a backup copy for the undo command
|
1003
|
+
FileUtils.cp(file, "#{file}~")
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
File.open(file, 'w+') do |f|
|
1007
|
+
f.puts output
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
@results.push("Export saved to #{file}")
|
1011
|
+
else
|
1012
|
+
puts output
|
1013
|
+
end
|
1014
|
+
end
|
894
1015
|
end
|
895
1016
|
|
896
1017
|
##
|
@@ -1072,6 +1193,28 @@ class WWID
|
|
1072
1193
|
@content[section]['items'] = section_items
|
1073
1194
|
end
|
1074
1195
|
|
1196
|
+
##
|
1197
|
+
## @brief Remove a tag on an item from the index
|
1198
|
+
##
|
1199
|
+
## @param old_item (Item) The item to tag
|
1200
|
+
## @param tag (string) The tag to remove
|
1201
|
+
##
|
1202
|
+
def untag_item(old_item, tag)
|
1203
|
+
title = old_item['title'].dup
|
1204
|
+
|
1205
|
+
if title =~ /@#{tag}/
|
1206
|
+
title.chomp!
|
1207
|
+
title.gsub!(/ +@#{tag}(\(.*?\))?/, '')
|
1208
|
+
new_item = old_item.dup
|
1209
|
+
new_item['title'] = title
|
1210
|
+
update_item(old_item, new_item)
|
1211
|
+
return new_item
|
1212
|
+
else
|
1213
|
+
@results.push(%(Item isn't tagged @#{tag}: "#{title}" in #{old_item['section']}))
|
1214
|
+
return old_item
|
1215
|
+
end
|
1216
|
+
end
|
1217
|
+
|
1075
1218
|
##
|
1076
1219
|
## @brief Tag an item from the index
|
1077
1220
|
##
|
@@ -1079,7 +1222,7 @@ class WWID
|
|
1079
1222
|
## @param tag (string) The tag to apply
|
1080
1223
|
## @param date (Boolean) Include timestamp?
|
1081
1224
|
##
|
1082
|
-
def tag_item(old_item, tag, date: false)
|
1225
|
+
def tag_item(old_item, tag, remove: false, date: false)
|
1083
1226
|
title = old_item['title'].dup
|
1084
1227
|
done_date = Time.now
|
1085
1228
|
if title !~ /@#{tag}/
|
@@ -1309,10 +1452,10 @@ class WWID
|
|
1309
1452
|
if File.exist?(file)
|
1310
1453
|
# Create a backup copy for the undo command
|
1311
1454
|
FileUtils.cp(file, "#{file}~")
|
1455
|
+
end
|
1312
1456
|
|
1313
|
-
|
1314
|
-
|
1315
|
-
end
|
1457
|
+
File.open(file, 'w+') do |f|
|
1458
|
+
f.puts output
|
1316
1459
|
end
|
1317
1460
|
|
1318
1461
|
if @config.key?('run_after')
|
@@ -1526,13 +1669,16 @@ class WWID
|
|
1526
1669
|
note ||= ''
|
1527
1670
|
|
1528
1671
|
tags = []
|
1672
|
+
attributes = {}
|
1529
1673
|
skip_tags = %w[meanwhile done cancelled flagged]
|
1530
1674
|
i['title'].scan(/@([^(\s]+)(?:\((.*?)\))?/).each do |tag|
|
1531
1675
|
tags.push(tag[0]) unless skip_tags.include?(tag[0])
|
1676
|
+
attributes[tag[0]] = tag[1] if tag[1]
|
1532
1677
|
end
|
1678
|
+
|
1533
1679
|
if opt[:output] == 'json'
|
1534
1680
|
|
1535
|
-
|
1681
|
+
i = {
|
1536
1682
|
date: i['date'],
|
1537
1683
|
end_date: end_date,
|
1538
1684
|
title: title.strip, #+ " #{note}"
|
@@ -1541,6 +1687,10 @@ class WWID
|
|
1541
1687
|
tags: tags
|
1542
1688
|
}
|
1543
1689
|
|
1690
|
+
attributes.each { |attr, val| i[attr.to_sym] = val }
|
1691
|
+
|
1692
|
+
items_out << i
|
1693
|
+
|
1544
1694
|
elsif opt[:output] == 'timeline'
|
1545
1695
|
new_item = {
|
1546
1696
|
'id' => index + 1,
|
@@ -1644,7 +1794,7 @@ class WWID
|
|
1644
1794
|
|
1645
1795
|
totals = opt[:totals] ? tag_times('html', opt[:sort_tags]) : ''
|
1646
1796
|
engine = Haml::Engine.new(template)
|
1647
|
-
|
1797
|
+
out = engine.render(Object.new,
|
1648
1798
|
{ :@items => items_out, :@page_title => page_title, :@style => style, :@totals => totals })
|
1649
1799
|
else
|
1650
1800
|
items.each do |item|
|
@@ -1656,11 +1806,12 @@ class WWID
|
|
1656
1806
|
reset = ''
|
1657
1807
|
end
|
1658
1808
|
|
1659
|
-
if (item.
|
1809
|
+
if (item.key?('note') && !item['note'].empty?) && @config[:include_notes]
|
1660
1810
|
note_lines = item['note'].delete_if do |line|
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1811
|
+
line =~ /^\s*$/
|
1812
|
+
end
|
1813
|
+
note_lines.map! { |line| "\t\t#{line.sub(/^\t*/, '').sub(/^-/, '—')} " }
|
1814
|
+
if opt[:wrap_width]&.positive?
|
1664
1815
|
width = opt[:wrap_width]
|
1665
1816
|
note_lines.map! do |line|
|
1666
1817
|
line.strip.gsub(/(.{1,#{width}})(\s+|\Z)/, "\t\\1\n")
|
@@ -1673,7 +1824,7 @@ class WWID
|
|
1673
1824
|
output = opt[:template].dup
|
1674
1825
|
|
1675
1826
|
output.gsub!(/%[a-z]+/) do |m|
|
1676
|
-
if colors.
|
1827
|
+
if colors.key?(m.sub(/^%/, ''))
|
1677
1828
|
colors[m.sub(/^%/, '')]
|
1678
1829
|
else
|
1679
1830
|
m
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.78
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|