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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64076f13651c82428d05f490870a3a8a2507dc783cfaeebe7658538523aaef25
4
- data.tar.gz: 691df494543c3ff71911a4ca87135adba9504fb445d51180e9c5914372c5d35b
3
+ metadata.gz: 146bc31206860d02e7f7e3d3d04b12cd84ecf794171af8f25ca679f49c6a98d0
4
+ data.tar.gz: 994568054f7f905bbe6a1982b00be636a68bac2719b4f6740ce04bf5cbcb208a
5
5
  SHA512:
6
- metadata.gz: be005b1f54f3e170030f88a6df19567478985965b56bb50a842fe3d5c87d7afb750e165d4888da45466f3dba16205a94d1baf5cbfe723f0a18dffe960d944d00
7
- data.tar.gz: 48e5cbec09157db3c974b5d0c242c394884a77f47ce5cf4e009b7dedf47daccba581709aefb534266415884ab71fad03c0a4d22385e3abc9d3e33576f3aaad17
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.76<!--END VER-->.
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
- If you have `fzf` installed (<https://github.com/junegunn/fzf>), 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.
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
- puts wwid.list_section({ section: section.cap_first, count: 0 })
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
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '1.0.78'
2
+ VERSION = '1.0.82'
3
3
  end
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] && opt[:tag].length.positive?
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
- 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]
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
- res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} -m --bind ctrl-a:select-all -q "#{opt[:query]}"`
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
- 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
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
- if opt[:output] !~ /(doing|taskpaper)/
992
- options[:output] = opt[:output]
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, tag)
1230
+ def untag_item(old_item, tags)
1203
1231
  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
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, tag, remove: false, date: false)
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
- if title !~ /@#{tag}/
1229
- title.chomp!
1230
- if date
1231
- title += " @#{tag}(#{done_date.strftime('%F %R')})"
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
- title += " @#{tag}"
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.each_with_index do |section, i|
1491
- puts format('% 3d: %s', i + 1, section)
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.each_with_index do |view, i|
1516
- puts format('% 3d: %s', i + 1, view)
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
@@ -8,5 +8,6 @@ require 'tempfile'
8
8
  require 'chronic'
9
9
  require 'haml'
10
10
  require 'json'
11
- require 'doing/helpers.rb'
12
- require 'doing/wwid.rb'
11
+ require 'doing/helpers'
12
+ require 'doing/wwid'
13
+ # require 'doing/markdown_document_listener'
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.78
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-25 00:00:00.000000000 Z
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/