doing 2.1.19 → 2.1.21

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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +9 -8
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +49 -0
  6. data/Gemfile.lock +11 -11
  7. data/README.md +1 -1
  8. data/Rakefile +10 -4
  9. data/bin/doing +123 -167
  10. data/docs/doc/Array.html +3 -3
  11. data/docs/doc/BooleanTermParser/Clause.html +3 -3
  12. data/docs/doc/BooleanTermParser/Operator.html +3 -3
  13. data/docs/doc/BooleanTermParser/Query.html +3 -3
  14. data/docs/doc/BooleanTermParser/QueryParser.html +3 -3
  15. data/docs/doc/BooleanTermParser/QueryTransformer.html +3 -3
  16. data/docs/doc/BooleanTermParser.html +3 -3
  17. data/docs/doc/Doing/Color.html +3 -3
  18. data/docs/doc/Doing/Completion.html +3 -3
  19. data/docs/doc/Doing/Configuration.html +4 -3
  20. data/docs/doc/Doing/Errors/DoingNoTraceError.html +3 -3
  21. data/docs/doc/Doing/Errors/DoingRuntimeError.html +3 -3
  22. data/docs/doc/Doing/Errors/DoingStandardError.html +3 -3
  23. data/docs/doc/Doing/Errors/EmptyInput.html +3 -3
  24. data/docs/doc/Doing/Errors/NoResults.html +3 -3
  25. data/docs/doc/Doing/Errors/PluginException.html +3 -3
  26. data/docs/doc/Doing/Errors/UserCancelled.html +3 -3
  27. data/docs/doc/Doing/Errors/WrongCommand.html +3 -3
  28. data/docs/doc/Doing/Errors.html +3 -3
  29. data/docs/doc/Doing/Hooks.html +3 -3
  30. data/docs/doc/Doing/Item.html +3 -3
  31. data/docs/doc/Doing/Items.html +3 -3
  32. data/docs/doc/Doing/LogAdapter.html +3 -3
  33. data/docs/doc/Doing/Note.html +3 -3
  34. data/docs/doc/Doing/Pager.html +3 -3
  35. data/docs/doc/Doing/Plugins.html +3 -3
  36. data/docs/doc/Doing/Prompt.html +3 -3
  37. data/docs/doc/Doing/Section.html +3 -3
  38. data/docs/doc/Doing/TemplateString.html +4 -4
  39. data/docs/doc/Doing/Util/Backup.html +3 -3
  40. data/docs/doc/Doing/Util.html +3 -3
  41. data/docs/doc/Doing/WWID.html +66 -8
  42. data/docs/doc/Doing.html +4 -4
  43. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +3 -3
  44. data/docs/doc/GLI/Commands.html +3 -3
  45. data/docs/doc/GLI.html +3 -3
  46. data/docs/doc/Hash.html +3 -3
  47. data/docs/doc/Numeric.html +3 -3
  48. data/docs/doc/PhraseParser/Operator.html +3 -3
  49. data/docs/doc/PhraseParser/PhraseClause.html +3 -3
  50. data/docs/doc/PhraseParser/Query.html +3 -3
  51. data/docs/doc/PhraseParser/QueryParser.html +3 -3
  52. data/docs/doc/PhraseParser/QueryTransformer.html +3 -3
  53. data/docs/doc/PhraseParser/TermClause.html +3 -3
  54. data/docs/doc/PhraseParser.html +3 -3
  55. data/docs/doc/Status.html +3 -3
  56. data/docs/doc/String.html +94 -17
  57. data/docs/doc/Symbol.html +3 -3
  58. data/docs/doc/Time.html +3 -3
  59. data/docs/doc/_index.html +4 -4
  60. data/docs/doc/file.README.html +4 -4
  61. data/docs/doc/frames.html +1 -1
  62. data/docs/doc/index.html +4 -4
  63. data/docs/doc/method_list.html +300 -276
  64. data/docs/doc/top-level-namespace.html +94 -3
  65. data/doing.gemspec +1 -1
  66. data/doing.rdoc +15 -6
  67. data/lib/completion/_doing.zsh +5 -5
  68. data/lib/completion/doing.bash +8 -8
  69. data/lib/completion/doing.fish +93 -15
  70. data/lib/doing/completion/fish_completion.rb +80 -11
  71. data/lib/doing/configuration.rb +1 -0
  72. data/lib/doing/hash.rb +1 -1
  73. data/lib/doing/items.rb +3 -1
  74. data/lib/doing/pager.rb +1 -1
  75. data/lib/doing/plugins/export/dayone_export.rb +1 -1
  76. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  77. data/lib/doing/string.rb +11 -0
  78. data/lib/doing/string_chronify.rb +55 -17
  79. data/lib/doing/types.rb +19 -0
  80. data/lib/doing/version.rb +1 -1
  81. data/lib/doing/wwid.rb +69 -42
  82. data/lib/examples/commands/later.rb +32 -0
  83. data/lib/helpers/threaded_tests.rb +250 -0
  84. metadata +9 -6
data/lib/doing/items.rb CHANGED
@@ -131,7 +131,9 @@ module Doing
131
131
  out = []
132
132
  @sections.each do |section|
133
133
  out.push(section.original)
134
- in_section(section.title).each { |item| out.push(item.to_s)}
134
+ items = in_section(section.title).sort_by { |i| i.date }
135
+ items.reverse! if Doing.config.settings['doing_file_sort'].normalize_order == 'desc'
136
+ items.each { |item| out.push(item.to_s)}
135
137
  end
136
138
 
137
139
  out.join("\n")
data/lib/doing/pager.rb CHANGED
@@ -55,7 +55,7 @@ module Doing
55
55
  write_io.write(text)
56
56
  write_io.close
57
57
  rescue SystemCallError => e
58
- raise Errors::DoingStandardError, "Pager error, #{e}"
58
+ # raise Errors::DoingStandardError, "Pager error, #{e}"
59
59
  end
60
60
 
61
61
  _, status = Process.waitpid2(pid)
@@ -75,7 +75,7 @@ module Doing
75
75
  note = i.note.map { |line| line.strip.link_urls(format: :markdown) } if i.note
76
76
  end
77
77
 
78
- title = "#{title} @project(#{i.section})" unless variables[:is_single]
78
+ title = "#{title} @section(#{i.section})" unless variables[:is_single]
79
79
 
80
80
  tags.concat(i.tag_array).sort!.uniq!
81
81
  flagged = day_flagged = true if i.tags?(wwid.config['marker_tag'])
@@ -48,7 +48,7 @@ module Doing
48
48
  note = i.note.map { |line| line.strip.link_urls(format: :markdown) } if i.note
49
49
  end
50
50
 
51
- title = "#{title} @project(#{i.section})" unless variables[:is_single]
51
+ title = "#{title} @section(#{i.section})" unless variables[:is_single]
52
52
 
53
53
  interval = wwid.get_interval(i, record: true) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
54
54
  interval ||= false
data/lib/doing/string.rb CHANGED
@@ -685,6 +685,15 @@ module Doing
685
685
  end
686
686
  end
687
687
 
688
+ def to_bool
689
+ case self
690
+ when /^[yt1]/i
691
+ true
692
+ else
693
+ false
694
+ end
695
+ end
696
+
688
697
  ##
689
698
  ## Convert a string value to an appropriate type. If
690
699
  ## kind is not specified, '[one, two]' becomes an Array,
@@ -704,6 +713,8 @@ module Doing
704
713
  gsub(/^\[ *| *\]$/, '').split(/ *, */)
705
714
  when /^i/i
706
715
  to_i
716
+ when /^(fa|tr)/i
717
+ to_bool
707
718
  when /^f/i
708
719
  to_f
709
720
  when /^sy/i
@@ -26,12 +26,12 @@ module Doing
26
26
  ##
27
27
  def chronify(**options)
28
28
  now = Time.now
29
- raise InvalidTimeExpression, "Invalid time expression #{inspect}" if to_s.strip == ''
29
+ raise Errors::InvalidTimeExpression, "Invalid time expression #{inspect}" if to_s.strip == ''
30
30
 
31
31
  secs_ago = if match(/^(\d+)$/)
32
32
  # plain number, assume minutes
33
33
  Regexp.last_match(1).to_i * 60
34
- elsif (m = match(/^(?:(?<day>\d+)d)?(?:(?<hour>\d+)h)?(?:(?<min>\d+)m)?$/i))
34
+ elsif (m = match(/^(?:(?<day>\d+)d)? *(?:(?<hour>\d+)h)? *(?:(?<min>\d+)m)?$/i))
35
35
  # day/hour/minute format e.g. 1d2h30m
36
36
  [[m['day'], 24 * 3600],
37
37
  [m['hour'], 3600],
@@ -39,14 +39,23 @@ module Doing
39
39
  end
40
40
 
41
41
  if secs_ago
42
- now - secs_ago
42
+ res = now - secs_ago
43
+ Doing.logger.debug('Parser:', %(date/time string "#{self}" interpreted as #{res} (#{secs_ago} seconds ago)))
43
44
  else
44
- Chronic.parse(self, {
45
- guess: options.fetch(:guess, :begin),
46
- context: options.fetch(:future, false) ? :future : :past,
47
- ambiguous_time_range: 8
48
- })
45
+ date_string = dup
46
+ date_string = 'today' if date_string.match(REGEX_DAY) && now.strftime('%a') =~ /^#{Regexp.last_match(1)}/i
47
+ date_string = "#{options[:context].to_s} #{date_string}" if date_string =~ REGEX_TIME && options[:context]
48
+
49
+ res = Chronic.parse(date_string, {
50
+ guess: options.fetch(:guess, :begin),
51
+ context: options.fetch(:future, false) ? :future : :past,
52
+ ambiguous_time_range: 8
53
+ })
54
+
55
+ Doing.logger.debug('Parser:', %(date/time string "#{self}" interpreted as #{res}))
49
56
  end
57
+
58
+ res
50
59
  end
51
60
 
52
61
  ##
@@ -152,6 +161,10 @@ module Doing
152
161
  end
153
162
  end
154
163
 
164
+ def is_range?
165
+ self =~ / (to|through|thru|(un)?til|-+) /
166
+ end
167
+
155
168
  ##
156
169
  ## Splits a range string and returns an array of
157
170
  ## DateTime objects as [start, end]. If only one date is
@@ -163,20 +176,45 @@ module Doing
163
176
  ## "mon 3pm to mon 5pm".split_date_range
164
177
  ##
165
178
  def split_date_range
179
+ time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/
180
+ range_rx = / (to|through|thru|(?:un)?til|-+) /
181
+
166
182
  date_string = dup
167
- case date_string
168
- when / (to|through|thru|(un)?til|-+) /
169
- dates = date_string.split(/ (?:to|through|thru|(?:un)?til|-+) /)
170
- start = dates[0].chronify(guess: :begin)
171
- finish = dates[-1].chronify(guess: :end)
183
+
184
+ if date_string.is_range?
185
+ # Do we want to differentiate between "to" and "through"?
186
+ # inclusive = date_string =~ / (through|thru|-+) / ? true : false
187
+ inclusive = true
188
+
189
+ dates = date_string.split(range_rx)
190
+ if dates[0].strip =~ time_rx && dates[-1].strip =~ time_rx
191
+ start = dates[0].strip
192
+ finish = dates[-1].strip
193
+ else
194
+ start = dates[0].chronify(guess: :begin, future: false)
195
+ finish = dates[-1].chronify(guess: inclusive ? :end : :begin, future: false)
196
+ end
197
+
198
+ raise Errors::InvalidTimeExpression, 'Unrecognized date string' if start.nil? || finish.nil?
199
+
172
200
  else
173
- start = date_string.chronify(guess: :begin)
174
- finish = nil
201
+ if date_string.strip =~ time_rx
202
+ start = date_string.strip
203
+ finish = nil
204
+ else
205
+ start = date_string.strip.chronify(guess: :begin, future: false)
206
+ finish = date_string.strip.chronify(guess: :end)
207
+ end
208
+ raise Errors::InvalidTimeExpression, 'Unrecognized date string' unless start
209
+
175
210
  end
176
211
 
177
- raise InvalidTimeExpression, 'Unrecognized date string' unless start
178
212
 
179
- Doing.logger.debug('Parser:', "date range interpreted as #{start.strftime('%F %R')} -- #{finish ? finish.strftime('%F %R') : 'now'}")
213
+ if start.is_a? String
214
+ Doing.logger.debug('Parser:', "--from string interpreted as time span, from #{start || '12am'} to #{finish || '11:59pm'}")
215
+ else
216
+ Doing.logger.debug('Parser:', "date range interpreted as #{start.strftime('%F %R')} -- #{finish ? finish.strftime('%F %R') : 'now'}")
217
+ end
180
218
  [start, finish]
181
219
  end
182
220
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ REGEX_BOOL = /^(?:and|all|any|or|not|none|p(?:at(?:tern)?)?)$/i
4
+ REGEX_SORT_ORDER = /^(?:a(?:sc)?|d(?:esc)?)$/i
5
+ REGEX_VALUE_QUERY = /^(?:!)?@?(?:\S+) +(?:!?[<>=][=*]?|[$*^]=) +(?:.*?)$/
6
+ REGEX_CLOCK = '(?:\d{1,2}+(?::\d{1,2}+)?(?: *(?:am|pm))?|midnight|noon)'
7
+ REGEX_TIME = /^#{REGEX_CLOCK}$/i
8
+ REGEX_DAY = /^(mon|tue|wed|thur?|fri|sat|sun)(\w+(day)?)?$/i
9
+ REGEX_RANGE_INDICATOR = ' +(?:to|through|thru|(?:un)?til|-+) +'
10
+ REGEX_RANGE = /^\S+#{REGEX_RANGE_INDICATOR}+\S+/i
11
+ REGEX_TIME_RANGE = /^#{REGEX_CLOCK}#{REGEX_RANGE_INDICATOR}#{REGEX_CLOCK}$/i
12
+
13
+ InvalidExportType = Class.new(RuntimeError)
14
+ MissingConfigFile = Class.new(RuntimeError)
15
+ TagArray = Class.new(Array)
16
+ DateBeginString = Class.new(DateTime)
17
+ DateEndString = Class.new(DateTime)
18
+ DateRangeString = Class.new(Array)
19
+ DateIntervalString = Class.new(DateTime)
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.1.19'
2
+ VERSION = '2.1.21'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -305,6 +305,28 @@ module Doing
305
305
  view
306
306
  end
307
307
 
308
+ def add_with_editor(**options)
309
+ raise MissingEditor, 'No EDITOR variable defined in environment' if Util.default_editor.nil?
310
+
311
+ input = options[:date].strftime('%F %R | ')
312
+ input += options[:title]
313
+ input += "\n#{options[:note]}" if options[:note]
314
+ input = fork_editor(input).strip
315
+
316
+ d, title, note = format_input(input)
317
+ raise EmptyInput, 'No content' if title.empty?
318
+
319
+ if options[:ask]
320
+ ask_note = Doing::Prompt.read_lines(prompt: 'Add a note')
321
+ note.add(ask_note) unless ask_note.empty?
322
+ end
323
+
324
+ date = d.nil? ? options[:date] : d
325
+ finish = options[:finish_last] || false
326
+ add_item(title.cap_first, options[:section], { note: note, back: date, timed: finish })
327
+ write(@doing_file)
328
+ end
329
+
308
330
  ##
309
331
  ## Adds an entry
310
332
  ##
@@ -356,7 +378,7 @@ module Doing
356
378
 
357
379
  @content.push(entry)
358
380
  # logger.count(:added, level: :debug)
359
- logger.info('New entry:', %(added "#{entry.title}" to #{section}))
381
+ logger.info('New entry:', %(added "#{entry.date.relative_date}: #{entry.title}" to #{section}))
360
382
 
361
383
  Hooks.trigger :post_entry_added, self, entry.dup
362
384
  end
@@ -454,7 +476,8 @@ module Doing
454
476
  opt ||= {}
455
477
  if item.should_finish?
456
478
  if item.should_time?
457
- item.title.tag!('done', value: Time.now.strftime('%F %R'))
479
+ finish_date = verify_duration(item.date, Time.now, title: item.title)
480
+ item.title.tag!('done', value: finish_date.strftime('%F %R'))
458
481
  else
459
482
  item.title.tag!('done')
460
483
  end
@@ -484,7 +507,7 @@ module Doing
484
507
  end
485
508
 
486
509
  # @content.update_item(original, item)
487
- add_item(title, section, { note: note, back: opt[:date], timed: true })
510
+ add_item(title, section, { note: note, back: opt[:date], timed: false })
488
511
  end
489
512
 
490
513
  ##
@@ -633,42 +656,25 @@ module Doing
633
656
 
634
657
  opt[:time_filter] = [nil, nil]
635
658
  if opt[:from] && !opt[:date_filter]
636
- date_string = opt[:from]
637
- case date_string
638
- when / (to|through|thru|(un)?til|-+) /
639
- dates = date_string.split(/ (?:to|through|thru|(?:un)?til|-+) /)
640
- if dates[0].strip =~ time_rx && dates[-1].strip =~ time_rx
641
- time_start = dates[0].strip
642
- time_end = dates[-1].strip
643
- else
644
- start = dates[0].chronify(guess: :begin)
645
- finish = dates[-1].chronify(guess: :end)
646
- end
647
- when time_rx
648
- time_start = date_string
649
- time_end = nil
650
- else
651
- start = date_string.chronify(guess: :begin)
652
- finish = false
659
+ if opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx
660
+ time_start, time_end = opt[:from]
661
+ elsif opt[:from].is_a?(Time)
662
+ start, finish = opt[:from]
653
663
  end
654
664
 
655
665
  if time_start
656
666
  opt[:time_filter] = [time_start, time_end]
657
- Doing.logger.debug('Parser:', "--from string interpreted as time span, from #{time_start ? time_start : '12am'} to #{time_end ? time_end : '11:59pm'}")
658
667
  else
659
- raise InvalidTimeExpression, 'Unrecognized date string' unless start
660
-
661
- opt[:date_filter] = [start, finish]
662
- Doing.logger.debug('Parser:', "--from string interpreted as #{start.strftime('%F %R')} -- #{finish ? finish.strftime('%F %R') : 'now'}")
668
+ opt[:date_filter] = opt[:from]
663
669
  end
664
670
  end
665
671
 
666
- if opt[:before] =~ time_rx
672
+ if opt[:before].is_a?(String) && opt[:before] =~ time_rx
667
673
  opt[:time_filter][1] = opt[:before]
668
674
  opt[:before] = nil
669
675
  end
670
676
 
671
- if opt[:after] =~ time_rx
677
+ if opt[:after].is_a?(String) && opt[:after] =~ time_rx
672
678
  opt[:time_filter][0] = opt[:after]
673
679
  opt[:after] = nil
674
680
  end
@@ -734,7 +740,7 @@ module Doing
734
740
  start_time = start_string.chronify(guess: :begin)
735
741
 
736
742
  end_string = if opt[:time_filter][1].nil?
737
- "#{item.date.next_day.strftime('%Y-%m-%d')} 12am"
743
+ "#{item.date.to_datetime.next_day.strftime('%Y-%m-%d')} 12am"
738
744
  else
739
745
  "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][1]}"
740
746
  end
@@ -753,22 +759,26 @@ module Doing
753
759
  end
754
760
 
755
761
  if keep && opt[:before]
756
- time_string = opt[:before]
757
- if time_string =~ time_rx
758
- cutoff = "#{item.date.strftime('%Y-%m-%d')} #{time_string}".chronify(guess: :begin)
762
+ before = opt[:before]
763
+ if before =~ time_rx
764
+ cutoff = "#{item.date.strftime('%Y-%m-%d')} #{before}".chronify(guess: :begin)
765
+ elsif before.is_a?(String)
766
+ cutoff = before.chronify(guess: :begin)
759
767
  else
760
- cutoff = time_string.chronify(guess: :begin)
768
+ cutoff = before
761
769
  end
762
770
  keep = cutoff && item.date <= cutoff
763
771
  keep = opt[:not] ? !keep : keep
764
772
  end
765
773
 
766
774
  if keep && opt[:after]
767
- time_string = opt[:after]
768
- if time_string =~ time_rx
769
- cutoff = "#{item.date.strftime('%Y-%m-%d')} #{time_string}".chronify(guess: :end)
775
+ after = opt[:after]
776
+ if after =~ time_rx
777
+ cutoff = "#{item.date.strftime('%Y-%m-%d')} #{after}".chronify(guess: :end)
778
+ elsif after.is_a?(String)
779
+ cutoff = after.chronify(guess: :end)
770
780
  else
771
- cutoff = time_string.chronify(guess: :end)
781
+ cutoff = after
772
782
  end
773
783
  keep = cutoff && item.date >= cutoff
774
784
  keep = opt[:not] ? !keep : keep
@@ -934,6 +944,7 @@ module Doing
934
944
  actions = [
935
945
  'add tag',
936
946
  'remove tag',
947
+ 'autotag',
937
948
  'cancel',
938
949
  'delete',
939
950
  'finish',
@@ -960,6 +971,8 @@ module Doing
960
971
  opt[:resume] = true
961
972
  when /reset/
962
973
  opt[:reset] = true
974
+ when /autotag/
975
+ opt[:autotag] = true
963
976
  when /(add|remove) tag/
964
977
  type = action =~ /^add/ ? 'add' : 'remove'
965
978
  raise InvalidArgument, "'add tag' and 'remove tag' can not be used together" if opt[:tag]
@@ -1071,6 +1084,21 @@ module Doing
1071
1084
  end
1072
1085
  end
1073
1086
 
1087
+ if opt[:autotag]
1088
+ items.map! do |i|
1089
+ new_title = autotag(i.title)
1090
+ if new_title == i.title
1091
+ logger.count(:skipped, level: :debug, message: '%count unchaged %items')
1092
+ # logger.debug('Autotag:', 'No changes')
1093
+ else
1094
+ logger.count(:added_tags)
1095
+ logger.write(items.count == 1 ? :info : :debug, 'Tagged:', new_title)
1096
+ i.title = new_title
1097
+ Hooks.trigger :post_entry_updated, self, i
1098
+ end
1099
+ end
1100
+ end
1101
+
1074
1102
  if opt[:tag]
1075
1103
  tag = opt[:tag]
1076
1104
  items.map! do |i|
@@ -1098,10 +1126,7 @@ module Doing
1098
1126
 
1099
1127
  return unless opt[:output]
1100
1128
 
1101
- items.map! do |i|
1102
- i.title = "#{i.title} @project(#{i.section})"
1103
- i
1104
- end
1129
+ items.each { |i| i.title = "#{i.title} @section(#{i.section})" }
1105
1130
 
1106
1131
  export_items = Items.new
1107
1132
  export_items.concat(items)
@@ -1138,6 +1163,8 @@ module Doing
1138
1163
  def verify_duration(date, finish_date, title: nil)
1139
1164
  max_elapsed = @config.dig('interaction', 'confirm_longer_than') || 0
1140
1165
  max_elapsed = max_elapsed.chronify_qty if max_elapsed.is_a?(String)
1166
+ date = date.chronify(guess: :end, context: :today) if finish_date.is_a?(String)
1167
+
1141
1168
  elapsed = finish_date - date
1142
1169
 
1143
1170
  if max_elapsed.positive? && (elapsed > max_elapsed)
@@ -1316,7 +1343,7 @@ module Doing
1316
1343
  ##
1317
1344
  ## @return [Item] the next chronological item in the index
1318
1345
  ##
1319
- def next_item(item, options)
1346
+ def next_item(item, options = {})
1320
1347
  options ||= {}
1321
1348
  items = filter_items(Items.new, opt: options)
1322
1349
 
@@ -1747,7 +1774,7 @@ module Doing
1747
1774
  opt[:sort_tags] ||= false
1748
1775
  section = guess_section(section)
1749
1776
  # :date_filter expects an array with start and end date
1750
- dates = [dates, dates] if dates.instance_of?(String)
1777
+ dates = dates.split_date_range if dates.instance_of?(String)
1751
1778
 
1752
1779
  list_section({
1753
1780
  section: section,
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example command that calls an existing command (tag) with
4
+ # preset options
5
+ desc 'Add an item to the Later section'
6
+ arg_name 'ENTRY'
7
+ command :later do |c|
8
+ c.example 'doing later "Something I\'ll think about tomorrow"', desc: 'Add an entry to the Later section'
9
+ c.example 'doing later -e', desc: 'Open $EDITOR to create an entry and optional note'
10
+
11
+ c.desc "Edit entry with #{Doing::Util.default_editor}"
12
+ c.switch %i[e editor], negatable: false, default_value: false
13
+
14
+ c.desc 'Backdate start time to date string [4pm|20m|2h|yesterday noon]'
15
+ c.arg_name 'DATE_STRING'
16
+ c.flag %i[b back started], type: DateBeginString
17
+
18
+ c.desc 'Note'
19
+ c.arg_name 'TEXT'
20
+ c.flag %i[n note]
21
+
22
+ c.desc 'Prompt for note via multi-line input'
23
+ c.switch %i[ask], negatable: false, default_value: false
24
+
25
+ c.action do |global_options, options, args|
26
+ cmd = commands[:now]
27
+ options[:section] = 'Later'
28
+ options[:finish_last] = false
29
+ action = cmd.send(:get_action, nil)
30
+ action.call(global_options, options, args)
31
+ end
32
+ end