doing 1.0.78 → 1.0.82
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -3
- data/bin/doing +6 -2
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +139 -112
- data/lib/doing.rb +3 -2
- data/lib/helpers/fuzzyfilefinder +0 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 146bc31206860d02e7f7e3d3d04b12cd84ecf794171af8f25ca679f49c6a98d0
|
4
|
+
data.tar.gz: 994568054f7f905bbe6a1982b00be636a68bac2719b4f6740ce04bf5cbcb208a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3c606a1bf94fc65241841ca4e9899488f8271569492fb33d4ecf49c46007bb1f678c5d6995656a17fbd003d387942bf43b0575b09d9a78bfc64852504224586
|
7
|
+
data.tar.gz: 5032b06cc50cc867e29f99973b6f03e4256f746f5643db7275a17621252db5e825fb1aa1b07439c0a3f087a2cfe96f6bc3a9966c98693f8a3a994880c81b6857
|
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.79<!--END VER-->.
|
31
31
|
|
32
32
|
$ [sudo] gem install doing
|
33
33
|
|
@@ -619,12 +619,15 @@ Now you can run `doing import --type timing -s SECTION PATH`, where SECTION is t
|
|
619
619
|
|
620
620
|
#### Interactive Usage
|
621
621
|
|
622
|
-
|
622
|
+
FuzzyFileFinder (`fzf`) is included with doing (<https://github.com/junegunn/fzf>), and you can use `doing select` to get a menu of all your items (or items in a given section) which can be searched with fuzzy matching. The menu allows multiple selections to be acted on directly.
|
623
623
|
|
624
|
-
To use the menu, type a search string or use the arrow keys to navigate up and down. Press tab on an entry you'd like to perform an action on. A marker will show up on the left indicating the entry is selected. Repeat the process and select as many entries as needed. When you hit Return, the selection will be passed back to doing. Use Control-A to select all visible entries.
|
624
|
+
To use the menu, type a search string or use the arrow keys to navigate up and down. Tip: searching is "fuzzy" by default, but you can start your search with a single quote (`'`) to force an exact match. Press tab on an entry you'd like to perform an action on. A marker will show up on the left indicating the entry is selected. Repeat the process and select as many entries as needed. When you hit Return, the selection will be passed back to doing. Use Control-A to select all visible entries.
|
625
625
|
|
626
626
|
Doing can perform several functions with this menu. Not all of doing's features are available, but the core functionality you'd need is there, plus you can open the selected entries on one page in your text editor, make changes to them, and when you save and close the entries are updated accordingly. This allows editing of everything from timestamps to tags to notes.
|
627
627
|
|
628
|
+
If no actions are specified, an interactive menu of options will be
|
629
|
+
presented.
|
630
|
+
|
628
631
|
Run `doing help select` for a list of options:
|
629
632
|
|
630
633
|
-a, --archive - Archive selected items
|
@@ -634,7 +637,11 @@ Run `doing help select` for a list of options:
|
|
634
637
|
-f, --finish - Add @done with current time to selected item(s)
|
635
638
|
--flag - Add flag to selected item(s)
|
636
639
|
-m, --move=SECTION - Move selected items to section (default: none)
|
640
|
+
-o, --output=FORMAT - Output format for export (doing|taskpaper|csv|html|json|template|timeline) (default: none)
|
641
|
+
-q, --query=QUERY - Initial search query for filtering (default: none)
|
642
|
+
-r, --remove - Reverse -c, -f, --flag, and -t (remove instead of adding)
|
637
643
|
-s, --section=SECTION - Select from a specific section (default: none)
|
644
|
+
--save_to=FILE - Save selected entries to file using --output format (default: none)
|
638
645
|
-t, --tag=TAG - Tag selected entries (default: none)
|
639
646
|
|
640
647
|
For example, `doing select -d -a` would present the menu, and then mark selected entries as @done (with timestamp) and move them to the Archive section.
|
data/bin/doing
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env ruby -W1
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
@@ -301,6 +301,9 @@ command :select do |c|
|
|
301
301
|
c.desc 'Add flag to selected item(s)'
|
302
302
|
c.switch %i[flag], negatable: false, default_value: false
|
303
303
|
|
304
|
+
c.desc 'Perform action without confirmation'
|
305
|
+
c.switch %i[force], negatable: false, default_value: false
|
306
|
+
|
304
307
|
c.desc 'Save selected entries to file using --output format'
|
305
308
|
c.arg_name 'FILE'
|
306
309
|
c.flag %i[save_to]
|
@@ -1306,7 +1309,8 @@ desc 'Select a section to display from a menu'
|
|
1306
1309
|
command :choose do |c|
|
1307
1310
|
c.action do |_global_options, _options, _args|
|
1308
1311
|
section = wwid.choose_section
|
1309
|
-
|
1312
|
+
|
1313
|
+
puts wwid.list_section({ section: section.cap_first, count: 0 }) if section
|
1310
1314
|
end
|
1311
1315
|
end
|
1312
1316
|
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -757,7 +757,7 @@ class WWID
|
|
757
757
|
all_items.concat(@content[section]['items'].dup) if @content.key?(section)
|
758
758
|
end
|
759
759
|
|
760
|
-
if opt[:tag]
|
760
|
+
if opt[:tag]&.length
|
761
761
|
all_items.select! { |item| item.has_tags?(opt[:tag], opt[:tag_bool]) }
|
762
762
|
elsif opt[:search]&.length
|
763
763
|
all_items.select! { |item| item.matches_search?(opt[:search]) }
|
@@ -771,16 +771,17 @@ class WWID
|
|
771
771
|
##
|
772
772
|
## @return (String) The selected option
|
773
773
|
##
|
774
|
-
def choose_from(options, prompt)
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
774
|
+
def choose_from(options, prompt: 'Make a selection: ', multiple: false, fzf_args: [])
|
775
|
+
fzf = File.join(File.dirname(__FILE__), '../helpers/fuzzyfilefinder')
|
776
|
+
fzf_args << '-1'
|
777
|
+
fzf_args << %(--prompt "#{prompt}")
|
778
|
+
fzf_args << '--multi' if multiple
|
779
|
+
header = "esc: cancel,#{multiple ? ' tab: multi-select, ctrl-a: select all,' : ''} return: confirm"
|
780
|
+
fzf_args << %(--header "#{header}")
|
781
|
+
res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} #{fzf_args.join(' ')}`
|
782
|
+
return false if res.strip.size.zero?
|
783
|
+
|
784
|
+
res
|
784
785
|
end
|
785
786
|
|
786
787
|
##
|
@@ -811,7 +812,7 @@ class WWID
|
|
811
812
|
') ',
|
812
813
|
item['date'],
|
813
814
|
' | ',
|
814
|
-
item['title']
|
815
|
+
item['title']
|
815
816
|
]
|
816
817
|
if opt[:section] =~ /^all/i
|
817
818
|
out.concat([
|
@@ -822,8 +823,15 @@ class WWID
|
|
822
823
|
end
|
823
824
|
out.join('')
|
824
825
|
end
|
825
|
-
|
826
|
-
|
826
|
+
fzf_args = [
|
827
|
+
%(--header="Arrows: navigate, tab: mark for selection, ctrl-a: select all, enter: commit"),
|
828
|
+
%(--prompt="Select entries to act on > "),
|
829
|
+
'-1',
|
830
|
+
'-m',
|
831
|
+
'--bind ctrl-a:select-all',
|
832
|
+
%(-q "#{opt[:query]}")
|
833
|
+
]
|
834
|
+
res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} #{fzf_args.join(' ')}`
|
827
835
|
selected = []
|
828
836
|
res.split(/\n/).each do |item|
|
829
837
|
idx = item.match(/^(\d+)\)/)[1].to_i
|
@@ -835,63 +843,81 @@ class WWID
|
|
835
843
|
return
|
836
844
|
end
|
837
845
|
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
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
|
846
|
+
actions = %i[editor delete tag flag finish cancel tag archive output save_to]
|
847
|
+
has_action = false
|
848
|
+
actions.each do |a|
|
849
|
+
if opt[a]
|
850
|
+
has_action = true
|
851
|
+
break
|
887
852
|
end
|
888
853
|
end
|
889
854
|
|
855
|
+
unless has_action
|
856
|
+
choice = choose_from([
|
857
|
+
'add tag',
|
858
|
+
'remove tag',
|
859
|
+
'cancel',
|
860
|
+
'delete',
|
861
|
+
'finish',
|
862
|
+
'flag',
|
863
|
+
'archive',
|
864
|
+
'move',
|
865
|
+
'edit',
|
866
|
+
'output formatted'
|
867
|
+
],
|
868
|
+
prompt: 'What do you want to do with the selected items? > ',
|
869
|
+
multiple: true,
|
870
|
+
fzf_args: ['--height=60%', '--tac', '--no-sort'])
|
871
|
+
return unless choice
|
872
|
+
|
873
|
+
to_do = choice.strip.split(/\n/)
|
874
|
+
to_do.each do |action|
|
875
|
+
case action
|
876
|
+
when /(add|remove) tag/
|
877
|
+
type = action =~ /^add/ ? 'add' : 'remove'
|
878
|
+
if opt[:tag]
|
879
|
+
warn "'add tag' and 'remove tag' can not be used together"
|
880
|
+
Process.exit 1
|
881
|
+
end
|
882
|
+
print "#{colors['yellow']}Tag to #{type}: #{colors['reset']}"
|
883
|
+
tag = STDIN.gets
|
884
|
+
return if tag =~ /^ *$/
|
885
|
+
opt[:tag] = tag.strip.sub(/^@/, '')
|
886
|
+
opt[:remove] = true if type == 'remove'
|
887
|
+
when /output formatted/
|
888
|
+
output_format = choose_from(%w[doing taskpaper json timeline html csv].sort, prompt: 'Which output format? > ', fzf_args: ['--height=60%', '--tac', '--no-sort'])
|
889
|
+
return if tag =~ /^ *$/
|
890
|
+
opt[:output] = output_format.strip
|
891
|
+
res = opt[:force] ? false : yn('Save to file?', default_response: 'n')
|
892
|
+
if res
|
893
|
+
print "#{colors['yellow']}File path/name: #{colors['reset']}"
|
894
|
+
filename = STDIN.gets.strip
|
895
|
+
return if filename.empty?
|
896
|
+
opt[:save_to] = filename
|
897
|
+
end
|
898
|
+
when /archive/
|
899
|
+
opt[:archive] = true
|
900
|
+
when /delete/
|
901
|
+
opt[:delete] = true
|
902
|
+
when /edit/
|
903
|
+
opt[:editor] = true
|
904
|
+
when /finish/
|
905
|
+
opt[:finish] = true
|
906
|
+
when /cancel/
|
907
|
+
opt[:cancel] = true
|
908
|
+
when /move/
|
909
|
+
section = choose_section.strip
|
910
|
+
opt[:move] = section.strip unless section =~ /^ *$/
|
911
|
+
when /flag/
|
912
|
+
opt[:flag] = true
|
913
|
+
end
|
914
|
+
end
|
915
|
+
end
|
890
916
|
|
891
917
|
if opt[:delete]
|
892
|
-
res = yn("Delete #{selected.size} items?", default_response: 'y')
|
918
|
+
res = opt[:force] ? true : yn("Delete #{selected.size} items?", default_response: 'y')
|
893
919
|
if res
|
894
|
-
selected.each {|item| delete_item(item) }
|
920
|
+
selected.each { |item| delete_item(item) }
|
895
921
|
write(@doing_file)
|
896
922
|
end
|
897
923
|
return
|
@@ -985,13 +1011,16 @@ class WWID
|
|
985
1011
|
item
|
986
1012
|
end
|
987
1013
|
|
988
|
-
@content = {'Export' => {'original' => 'Export:', 'items' => selected}}
|
989
|
-
options = {section: 'Export'}
|
1014
|
+
@content = { 'Export' => { 'original' => 'Export:', 'items' => selected } }
|
1015
|
+
options = { section: 'Export' }
|
990
1016
|
|
991
|
-
|
992
|
-
|
993
|
-
else
|
1017
|
+
case opt[:output]
|
1018
|
+
when /doing/
|
994
1019
|
options[:template] = '- %date | %title%note'
|
1020
|
+
when /taskpaper/
|
1021
|
+
options[:template] = '- %title @date(%date)%note'
|
1022
|
+
else
|
1023
|
+
options[:output] = opt[:output]
|
995
1024
|
end
|
996
1025
|
|
997
1026
|
output = list_section(options)
|
@@ -1073,7 +1102,6 @@ class WWID
|
|
1073
1102
|
count = (opt[:count]).zero? ? items.length : opt[:count]
|
1074
1103
|
items.map! do |item|
|
1075
1104
|
break if idx == count
|
1076
|
-
|
1077
1105
|
finished = opt[:unfinished] && item.has_tags?('done', :and)
|
1078
1106
|
tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.has_tags?(opt[:tag], opt[:tag_bool])
|
1079
1107
|
search_match = opt[:search].nil? || opt[:search].empty? ? true : item.matches_search?(opt[:search])
|
@@ -1199,19 +1227,24 @@ class WWID
|
|
1199
1227
|
## @param old_item (Item) The item to tag
|
1200
1228
|
## @param tag (string) The tag to remove
|
1201
1229
|
##
|
1202
|
-
def untag_item(old_item,
|
1230
|
+
def untag_item(old_item, tags)
|
1203
1231
|
title = old_item['title'].dup
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1232
|
+
if tags.is_a? ::String
|
1233
|
+
tags = tags.split(/ *, */).map {|t| t.strip.gsub(/\*/,'[^ (]*') }
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
tags.each do |tag|
|
1237
|
+
if title =~ /@#{tag}/
|
1238
|
+
title.chomp!
|
1239
|
+
title.gsub!(/ +@#{tag}(\(.*?\))?/, '')
|
1240
|
+
new_item = old_item.dup
|
1241
|
+
new_item['title'] = title
|
1242
|
+
update_item(old_item, new_item)
|
1243
|
+
return new_item
|
1244
|
+
else
|
1245
|
+
@results.push(%(Item isn't tagged @#{tag}: "#{title}" in #{old_item['section']}))
|
1246
|
+
return old_item
|
1247
|
+
end
|
1215
1248
|
end
|
1216
1249
|
end
|
1217
1250
|
|
@@ -1222,23 +1255,29 @@ class WWID
|
|
1222
1255
|
## @param tag (string) The tag to apply
|
1223
1256
|
## @param date (Boolean) Include timestamp?
|
1224
1257
|
##
|
1225
|
-
def tag_item(old_item,
|
1258
|
+
def tag_item(old_item, tags, remove: false, date: false)
|
1226
1259
|
title = old_item['title'].dup
|
1260
|
+
if tags.is_a? ::String
|
1261
|
+
tags = tags.split(/ *, */).map(&:strip)
|
1262
|
+
end
|
1263
|
+
|
1227
1264
|
done_date = Time.now
|
1228
|
-
|
1229
|
-
title
|
1230
|
-
|
1231
|
-
|
1265
|
+
tags.each do |tag|
|
1266
|
+
if title !~ /@#{tag}/
|
1267
|
+
title.chomp!
|
1268
|
+
if date
|
1269
|
+
title += " @#{tag}(#{done_date.strftime('%F %R')})"
|
1270
|
+
else
|
1271
|
+
title += " @#{tag}"
|
1272
|
+
end
|
1273
|
+
new_item = old_item.dup
|
1274
|
+
new_item['title'] = title
|
1275
|
+
update_item(old_item, new_item)
|
1276
|
+
return new_item
|
1232
1277
|
else
|
1233
|
-
|
1278
|
+
@results.push(%(Item already @#{tag}: "#{title}" in #{old_item['section']}))
|
1279
|
+
return old_item
|
1234
1280
|
end
|
1235
|
-
new_item = old_item.dup
|
1236
|
-
new_item['title'] = title
|
1237
|
-
update_item(old_item, new_item)
|
1238
|
-
return new_item
|
1239
|
-
else
|
1240
|
-
@results.push(%(Item already @#{tag}: "#{title}" in #{old_item['section']}))
|
1241
|
-
return old_item
|
1242
1281
|
end
|
1243
1282
|
end
|
1244
1283
|
|
@@ -1487,14 +1526,8 @@ class WWID
|
|
1487
1526
|
## @return (String) The selected section name
|
1488
1527
|
##
|
1489
1528
|
def choose_section
|
1490
|
-
sections.
|
1491
|
-
|
1492
|
-
end
|
1493
|
-
print "#{colors['green']}> #{colors['default']}"
|
1494
|
-
num = STDIN.gets
|
1495
|
-
return false if num =~ /^[a-z ]*$/i
|
1496
|
-
|
1497
|
-
sections[num.to_i - 1]
|
1529
|
+
choice = choose_from(sections.sort, prompt: 'Choose a section > ', fzf_args: ['--height=60%'])
|
1530
|
+
choice ? choice.strip : choice
|
1498
1531
|
end
|
1499
1532
|
|
1500
1533
|
##
|
@@ -1512,14 +1545,8 @@ class WWID
|
|
1512
1545
|
## @return (String) The selected view name
|
1513
1546
|
##
|
1514
1547
|
def choose_view
|
1515
|
-
views.
|
1516
|
-
|
1517
|
-
end
|
1518
|
-
print '> '
|
1519
|
-
num = STDIN.gets
|
1520
|
-
return false if num =~ /^[a-z ]*$/i
|
1521
|
-
|
1522
|
-
views[num.to_i - 1]
|
1548
|
+
choice = choose_from(views.sort, prompt: 'Choose a view > ', fzf_args: ['--height=60%'])
|
1549
|
+
choice ? choice.strip : choice
|
1523
1550
|
end
|
1524
1551
|
|
1525
1552
|
##
|
data/lib/doing.rb
CHANGED
Binary file
|
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.82
|
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-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -168,6 +168,7 @@ files:
|
|
168
168
|
- lib/doing/helpers.rb
|
169
169
|
- lib/doing/version.rb
|
170
170
|
- lib/doing/wwid.rb
|
171
|
+
- lib/helpers/fuzzyfilefinder
|
171
172
|
- lib/templates/doing.css
|
172
173
|
- lib/templates/doing.haml
|
173
174
|
homepage: http://brettterpstra.com/project/doing/
|