doing 1.0.76 → 1.0.81

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: 7b4ff1cca2f519037f002070068ed1267cd0083ae04e3b2b14e431bf8ee47baf
4
- data.tar.gz: da59f5f23f01b82a37e2ca56b552c26f638854597cc0f0beac21f30b2c1dd066
3
+ metadata.gz: 860b1b1c3f8f1ffd33fc344cabb37a7a060cdcb80d54f65a8f3598450b4c22cf
4
+ data.tar.gz: 83767946e503d9bd2515aa99a7e11de87c2910f48a1b2c6e5a745c9e7dada0be
5
5
  SHA512:
6
- metadata.gz: 550a05a4c6b0a432df01904d0cb2b78d7bdf9b41fa0ac8660e694f23b8f8384389395966cbb35cfbb6feb55ad248c39ec0010b8ca4e2626a892e029f553fb4dd
7
- data.tar.gz: e3b3d0789d9fba0f4cd63fcc1da2910d32a14142d25e367ede83fbee3956476991cfd74fc73f99642b1cf47c744c4e78765f6df75f3933ee2d1e619de8f2a733
6
+ metadata.gz: d78baf84b0bef668b7c877e908b6f1ee4ea05ce3914735f8d2e069abed215dfe5c9fc1fc3a9eb0dc5e4f5e251420d2454fb14a1b7465a5df8cf688e4b16b24ef
7
+ data.tar.gz: b38cf4b873baeb35ecab15ef10b3c1b8be4a7d2e28f9b34511d30f50def0b47ef127829bc5755bb0b2ba424183959dd009a222958df03ee8e54680e2ce267ab6
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.75<!--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
 
@@ -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 (`$1`) indicates that the first matched group (in parenthesis) should be used to generate the new tag, resulting in `@projecttag` being added to the entry.
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
 
@@ -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')
@@ -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 'Requires that `fzf` be installed and available in your path. <https://github.com/junegunn/fzf>
260
+ long_desc 'List all entries and select with typeahead fuzzy matching.
261
261
 
262
- List all entries and select with typeahead fuzzy matching. Multiple selections are allowed, hit tab to add the highlighted entry to the selection. Return processes the selected entries.'
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,18 @@ 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 'Perform action without confirmation'
305
+ c.switch %i[force], negatable: false, default_value: false
306
+
307
+ c.desc 'Save selected entries to file using --output format'
308
+ c.arg_name 'FILE'
309
+ c.flag %i[save_to]
310
+
311
+ c.desc 'Output format for export (doing|taskpaper|csv|html|json|template|timeline)'
312
+ c.arg_name 'FORMAT'
313
+ c.flag %i[o output], must_match: /^(?:doing|taskpaper|html|csv|json|template|timeline)$/i
314
+
297
315
  c.action do |_global_options, options, args|
298
- section = options[:section] || 'All'
299
- edit = options[:editor]
300
316
  wwid.interactive(options)
301
317
  end
302
318
  end
@@ -1293,7 +1309,8 @@ desc 'Select a section to display from a menu'
1293
1309
  command :choose do |c|
1294
1310
  c.action do |_global_options, _options, _args|
1295
1311
  section = wwid.choose_section
1296
- puts wwid.list_section({ section: section.cap_first, count: 0 })
1312
+
1313
+ puts wwid.list_section({ section: section.cap_first, count: 0 }) if section
1297
1314
  end
1298
1315
  end
1299
1316
 
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)://)?([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&amp;:/~+#]*[\w\-@^=%&amp;/~+#])?}) do |_match|
95
+ gsub(%r{(?mi)((http|https)://)([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&amp;:/~+#]*[\w\-@^=%&amp;/~+#])?}) 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
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '1.0.76'
2
+ VERSION = '1.0.81'
3
3
  end
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 ? 'y' : 'n'
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
@@ -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]) }
@@ -766,6 +766,23 @@ 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: '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
785
+ end
769
786
 
770
787
  ##
771
788
  ## @brief Display an interactive menu of entries
@@ -773,7 +790,7 @@ class WWID
773
790
  ## @param opt (Hash) Additional options
774
791
  ##
775
792
  def interactive(opt = {})
776
- exit_now! "Select command requires that fzf be installed" unless exec_available('fzf')
793
+ fzf = File.join(File.dirname(__FILE__), '../helpers/fuzzyfilefinder')
777
794
 
778
795
  section = opt[:section] ? guess_section(opt[:section]) : 'All'
779
796
 
@@ -795,7 +812,7 @@ class WWID
795
812
  ') ',
796
813
  item['date'],
797
814
  ' | ',
798
- item['title'],
815
+ item['title']
799
816
  ]
800
817
  if opt[:section] =~ /^all/i
801
818
  out.concat([
@@ -806,8 +823,15 @@ class WWID
806
823
  end
807
824
  out.join('')
808
825
  end
809
-
810
- res = `echo #{Shellwords.escape(options.join("\n"))}|fzf -m --bind ctrl-a:select-all`
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(' ')}`
811
835
  selected = []
812
836
  res.split(/\n/).each do |item|
813
837
  idx = item.match(/^(\d+)\)/)[1].to_i
@@ -819,10 +843,81 @@ class WWID
819
843
  return
820
844
  end
821
845
 
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
852
+ end
853
+ end
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
916
+
822
917
  if opt[:delete]
823
- res = yn("Delete #{selected.size} items?", default_response: 'y')
918
+ res = opt[:force] ? true : yn("Delete #{selected.size} items?", default_response: 'y')
824
919
  if res
825
- selected.each {|item| delete_item(item) }
920
+ selected.each { |item| delete_item(item) }
826
921
  write(@doing_file)
827
922
  end
828
923
  return
@@ -830,17 +925,35 @@ class WWID
830
925
 
831
926
  if opt[:flag]
832
927
  tag = @config['marker_tag'] || 'flagged'
833
- selected.map! {|item| tag_item(item, tag, date: false) }
928
+ selected.map! do |item|
929
+ if opt[:remove]
930
+ untag_item(item, tag)
931
+ else
932
+ tag_item(item, tag, date: false)
933
+ end
934
+ end
834
935
  end
835
936
 
836
937
  if opt[:finish] || opt[:cancel]
837
938
  tag = 'done'
838
- selected.map! {|item| tag_item(item, tag, date: !opt[:cancel])}
939
+ selected.map! do |item|
940
+ if opt[:remove]
941
+ untag_item(item, tag)
942
+ else
943
+ tag_item(item, tag, date: !opt[:cancel])
944
+ end
945
+ end
839
946
  end
840
947
 
841
948
  if opt[:tag]
842
949
  tag = opt[:tag]
843
- selected.map! {|item| tag_item(item, tag, date: false)}
950
+ selected.map! do |item|
951
+ if opt[:remove]
952
+ untag_item(item, tag)
953
+ else
954
+ tag_item(item, tag, date: false)
955
+ end
956
+ end
844
957
  end
845
958
 
846
959
  if opt[:archive] || opt[:move]
@@ -891,6 +1004,43 @@ class WWID
891
1004
 
892
1005
  write(@doing_file)
893
1006
  end
1007
+
1008
+ if opt[:output]
1009
+ selected.map! do |item|
1010
+ item['title'] = "#{item['title']} @project(#{item['section']})"
1011
+ item
1012
+ end
1013
+
1014
+ @content = { 'Export' => { 'original' => 'Export:', 'items' => selected } }
1015
+ options = { section: 'Export' }
1016
+
1017
+ case opt[:output]
1018
+ when /doing/
1019
+ options[:template] = '- %date | %title%note'
1020
+ when /taskpaper/
1021
+ options[:template] = '- %title @date(%date)%note'
1022
+ else
1023
+ options[:output] = opt[:output]
1024
+ end
1025
+
1026
+ output = list_section(options)
1027
+
1028
+ if opt[:save_to]
1029
+ file = File.expand_path(opt[:save_to])
1030
+ if File.exist?(file)
1031
+ # Create a backup copy for the undo command
1032
+ FileUtils.cp(file, "#{file}~")
1033
+ end
1034
+
1035
+ File.open(file, 'w+') do |f|
1036
+ f.puts output
1037
+ end
1038
+
1039
+ @results.push("Export saved to #{file}")
1040
+ else
1041
+ puts output
1042
+ end
1043
+ end
894
1044
  end
895
1045
 
896
1046
  ##
@@ -952,7 +1102,6 @@ class WWID
952
1102
  count = (opt[:count]).zero? ? items.length : opt[:count]
953
1103
  items.map! do |item|
954
1104
  break if idx == count
955
-
956
1105
  finished = opt[:unfinished] && item.has_tags?('done', :and)
957
1106
  tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.has_tags?(opt[:tag], opt[:tag_bool])
958
1107
  search_match = opt[:search].nil? || opt[:search].empty? ? true : item.matches_search?(opt[:search])
@@ -1072,6 +1221,33 @@ class WWID
1072
1221
  @content[section]['items'] = section_items
1073
1222
  end
1074
1223
 
1224
+ ##
1225
+ ## @brief Remove a tag on an item from the index
1226
+ ##
1227
+ ## @param old_item (Item) The item to tag
1228
+ ## @param tag (string) The tag to remove
1229
+ ##
1230
+ def untag_item(old_item, tags)
1231
+ title = old_item['title'].dup
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
1248
+ end
1249
+ end
1250
+
1075
1251
  ##
1076
1252
  ## @brief Tag an item from the index
1077
1253
  ##
@@ -1079,23 +1255,29 @@ class WWID
1079
1255
  ## @param tag (string) The tag to apply
1080
1256
  ## @param date (Boolean) Include timestamp?
1081
1257
  ##
1082
- def tag_item(old_item, tag, date: false)
1258
+ def tag_item(old_item, tags, remove: false, date: false)
1083
1259
  title = old_item['title'].dup
1260
+ if tags.is_a? ::String
1261
+ tags = tags.split(/ *, */).map(&:strip)
1262
+ end
1263
+
1084
1264
  done_date = Time.now
1085
- if title !~ /@#{tag}/
1086
- title.chomp!
1087
- if date
1088
- 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
1089
1277
  else
1090
- title += " @#{tag}"
1278
+ @results.push(%(Item already @#{tag}: "#{title}" in #{old_item['section']}))
1279
+ return old_item
1091
1280
  end
1092
- new_item = old_item.dup
1093
- new_item['title'] = title
1094
- update_item(old_item, new_item)
1095
- return new_item
1096
- else
1097
- @results.push(%(Item already @#{tag}: "#{title}" in #{old_item['section']}))
1098
- return old_item
1099
1281
  end
1100
1282
  end
1101
1283
 
@@ -1309,10 +1491,10 @@ class WWID
1309
1491
  if File.exist?(file)
1310
1492
  # Create a backup copy for the undo command
1311
1493
  FileUtils.cp(file, "#{file}~")
1494
+ end
1312
1495
 
1313
- File.open(file, 'w+') do |f|
1314
- f.puts output
1315
- end
1496
+ File.open(file, 'w+') do |f|
1497
+ f.puts output
1316
1498
  end
1317
1499
 
1318
1500
  if @config.key?('run_after')
@@ -1344,14 +1526,8 @@ class WWID
1344
1526
  ## @return (String) The selected section name
1345
1527
  ##
1346
1528
  def choose_section
1347
- sections.each_with_index do |section, i|
1348
- puts format('% 3d: %s', i + 1, section)
1349
- end
1350
- print "#{colors['green']}> #{colors['default']}"
1351
- num = STDIN.gets
1352
- return false if num =~ /^[a-z ]*$/i
1353
-
1354
- sections[num.to_i - 1]
1529
+ choice = choose_from(sections.sort, prompt: 'Choose a section > ', fzf_args: ['--height=60%'])
1530
+ choice ? choice.strip : choice
1355
1531
  end
1356
1532
 
1357
1533
  ##
@@ -1369,14 +1545,8 @@ class WWID
1369
1545
  ## @return (String) The selected view name
1370
1546
  ##
1371
1547
  def choose_view
1372
- views.each_with_index do |view, i|
1373
- puts format('% 3d: %s', i + 1, view)
1374
- end
1375
- print '> '
1376
- num = STDIN.gets
1377
- return false if num =~ /^[a-z ]*$/i
1378
-
1379
- views[num.to_i - 1]
1548
+ choice = choose_from(views.sort, prompt: 'Choose a view > ', fzf_args: ['--height=60%'])
1549
+ choice ? choice.strip : choice
1380
1550
  end
1381
1551
 
1382
1552
  ##
@@ -1526,13 +1696,16 @@ class WWID
1526
1696
  note ||= ''
1527
1697
 
1528
1698
  tags = []
1699
+ attributes = {}
1529
1700
  skip_tags = %w[meanwhile done cancelled flagged]
1530
1701
  i['title'].scan(/@([^(\s]+)(?:\((.*?)\))?/).each do |tag|
1531
1702
  tags.push(tag[0]) unless skip_tags.include?(tag[0])
1703
+ attributes[tag[0]] = tag[1] if tag[1]
1532
1704
  end
1705
+
1533
1706
  if opt[:output] == 'json'
1534
1707
 
1535
- items_out << {
1708
+ i = {
1536
1709
  date: i['date'],
1537
1710
  end_date: end_date,
1538
1711
  title: title.strip, #+ " #{note}"
@@ -1541,6 +1714,10 @@ class WWID
1541
1714
  tags: tags
1542
1715
  }
1543
1716
 
1717
+ attributes.each { |attr, val| i[attr.to_sym] = val }
1718
+
1719
+ items_out << i
1720
+
1544
1721
  elsif opt[:output] == 'timeline'
1545
1722
  new_item = {
1546
1723
  'id' => index + 1,
@@ -1644,7 +1821,7 @@ class WWID
1644
1821
 
1645
1822
  totals = opt[:totals] ? tag_times('html', opt[:sort_tags]) : ''
1646
1823
  engine = Haml::Engine.new(template)
1647
- puts engine.render(Object.new,
1824
+ out = engine.render(Object.new,
1648
1825
  { :@items => items_out, :@page_title => page_title, :@style => style, :@totals => totals })
1649
1826
  else
1650
1827
  items.each do |item|
@@ -1656,11 +1833,12 @@ class WWID
1656
1833
  reset = ''
1657
1834
  end
1658
1835
 
1659
- if (item.has_key?('note') && !item['note'].empty?) && @config[:include_notes]
1836
+ if (item.key?('note') && !item['note'].empty?) && @config[:include_notes]
1660
1837
  note_lines = item['note'].delete_if do |line|
1661
- line =~ /^\s*$/
1662
- end.map { |line| "\t\t" + line.sub(/^\t*/, '').sub(/^-/, '—') + ' ' }
1663
- if opt[:wrap_width] && opt[:wrap_width] > 0
1838
+ line =~ /^\s*$/
1839
+ end
1840
+ note_lines.map! { |line| "\t\t#{line.sub(/^\t*/, '').sub(/^-/, '—')} " }
1841
+ if opt[:wrap_width]&.positive?
1664
1842
  width = opt[:wrap_width]
1665
1843
  note_lines.map! do |line|
1666
1844
  line.strip.gsub(/(.{1,#{width}})(\s+|\Z)/, "\t\\1\n")
@@ -1673,7 +1851,7 @@ class WWID
1673
1851
  output = opt[:template].dup
1674
1852
 
1675
1853
  output.gsub!(/%[a-z]+/) do |m|
1676
- if colors.has_key?(m.sub(/^%/, ''))
1854
+ if colors.key?(m.sub(/^%/, ''))
1677
1855
  colors[m.sub(/^%/, '')]
1678
1856
  else
1679
1857
  m
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.76
4
+ version: 1.0.81
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-16 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/