doing 2.1.38 → 2.1.39

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -1
  5. data/Rakefile +7 -1
  6. data/bin/commands/now.rb +2 -4
  7. data/bin/commands/on.rb +4 -2
  8. data/bin/commands/show.rb +3 -0
  9. data/bin/doing +33 -35
  10. data/docs/doc/Array.html +1 -1
  11. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  12. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  13. data/docs/doc/BooleanTermParser/Query.html +1 -1
  14. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  15. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  16. data/docs/doc/BooleanTermParser.html +1 -1
  17. data/docs/doc/Doing/Color.html +1 -1
  18. data/docs/doc/Doing/Completion.html +1 -1
  19. data/docs/doc/Doing/Configuration.html +1 -1
  20. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  21. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  22. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  23. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  24. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  25. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  26. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  27. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  28. data/docs/doc/Doing/Errors.html +1 -1
  29. data/docs/doc/Doing/Hooks.html +1 -1
  30. data/docs/doc/Doing/Item.html +64 -1
  31. data/docs/doc/Doing/Items.html +1 -1
  32. data/docs/doc/Doing/Logger.html +1 -1
  33. data/docs/doc/Doing/Note.html +1 -1
  34. data/docs/doc/Doing/Pager.html +1 -1
  35. data/docs/doc/Doing/Plugins.html +1 -1
  36. data/docs/doc/Doing/Prompt.html +1 -1
  37. data/docs/doc/Doing/Section.html +1 -1
  38. data/docs/doc/Doing/TemplateString.html +1 -1
  39. data/docs/doc/Doing/Types.html +1 -1
  40. data/docs/doc/Doing/Util/Backup.html +1 -1
  41. data/docs/doc/Doing/Util.html +1 -1
  42. data/docs/doc/Doing/WWID.html +1 -1
  43. data/docs/doc/Doing.html +2 -2
  44. data/docs/doc/FalseClass.html +1 -1
  45. data/docs/doc/GLI/Commands/Help.html +1 -1
  46. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  47. data/docs/doc/GLI/Commands.html +1 -1
  48. data/docs/doc/GLI.html +1 -1
  49. data/docs/doc/Hash.html +1 -1
  50. data/docs/doc/Object.html +1 -1
  51. data/docs/doc/PhraseParser/Operator.html +1 -1
  52. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  53. data/docs/doc/PhraseParser/Query.html +1 -1
  54. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  55. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  56. data/docs/doc/PhraseParser/TermClause.html +1 -1
  57. data/docs/doc/PhraseParser.html +1 -1
  58. data/docs/doc/Status.html +1 -1
  59. data/docs/doc/String.html +1 -1
  60. data/docs/doc/Symbol.html +1 -1
  61. data/docs/doc/Time.html +1 -1
  62. data/docs/doc/TrueClass.html +1 -1
  63. data/docs/doc/_index.html +1 -1
  64. data/docs/doc/file.README.html +2 -2
  65. data/docs/doc/index.html +2 -2
  66. data/docs/doc/method_list.html +159 -151
  67. data/docs/doc/top-level-namespace.html +1 -1
  68. data/doing.rdoc +6 -1
  69. data/lib/completion/_doing.zsh +1 -1
  70. data/lib/completion/doing.bash +2 -2
  71. data/lib/completion/doing.fish +1 -0
  72. data/lib/doing/chronify/string.rb +3 -1
  73. data/lib/doing/item.rb +151 -53
  74. data/lib/doing/items.rb +1 -1
  75. data/lib/doing/prompt.rb +6 -8
  76. data/lib/doing/string/tags.rb +8 -2
  77. data/lib/doing/version.rb +1 -1
  78. data/lib/doing/wwid.rb +7 -2
  79. metadata +2 -2
@@ -206,7 +206,7 @@
206
206
  </div>
207
207
 
208
208
  <div id="footer">
209
- Generated on Sat Mar 12 16:18:00 2022 by
209
+ Generated on Sun Mar 13 05:08:08 2022 by
210
210
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
211
211
  0.9.27 (ruby-3.0.1).
212
212
  </div>
data/doing.rdoc CHANGED
@@ -5,7 +5,7 @@ record of what you've been doing, complete with tag-based time tracking. The
5
5
  command line tool allows you to add entries, annotate with tags and notes, and
6
6
  view your entries with myriad options, with a focus on a "natural" language syntax.
7
7
 
8
- v2.1.38
8
+ v2.1.39
9
9
 
10
10
  === Global Options
11
11
  === --config_file arg
@@ -2283,6 +2283,11 @@ Show elapsed time on entries without @done tag
2283
2283
 
2284
2284
 
2285
2285
 
2286
+ ===== -e|--editor
2287
+ Edit matching entries with vim
2288
+
2289
+
2290
+
2286
2291
  ===== -h|--[no-]hilite
2287
2292
  Highlight search matches in output. Only affects command line output
2288
2293
 
@@ -174,7 +174,7 @@ function _doing() {
174
174
  args=( {'(--archive)-a','(-a)--archive'}"[Archive selected items]" "--after[Select entries newer than date]:DATE_STRING:" "--resume[Copy selection as a new entry with current time and no @done tag]" "--before[Select entries older than date]:DATE_STRING:" {'(--cancel)-c','(-c)--cancel'}"[Cancel selected items]" "--case[Case sensitivity for search string matching ((c)ase-sensitive]:TYPE:" {'(--delete)-d','(-d)--delete'}"[Delete selected items]" {'(--editor)-e','(-e)--editor'}"[Edit selected item(s)]" {'(--finish)-f','(-f)--finish'}"[Add @done with current time to selected item(s)]" "--flag[Add flag to selected item(s)]" "--force[Perform action without confirmation]" "--from[Date range]:DATE_OR_RANGE:" {'(--move)-m','(-m)--move'}"[Move selected items to section]:SECTION:" "--menu[Use --no-menu to skip the interactive menu]" "--not[Select items that *dont* match search/tag filterst* match search/tag filters]" {'(--output)-o','(-o)--output'}"[Output entries to format]:FORMAT:" {'(--query)-q','(-q)--query'}"[Initial search query for filtering]:QUERY:" {'(--remove)-r','(-r)--remove'}"[Reverse -c]" {'(--section)-s','(-s)--section'}"[Select from a specific section]:SECTION:" "--save_to[Save selected entries to file using --output format]:FILE:" "--search[Filter entries using a search query]:QUERY:" {'(--tag)-t','(-t)--tag'}"[Tag selected entries]:TAG:" "--val[Perform a tag value query]:QUERY:" {'(--exact)-x','(-x)--exact'}"[Force exact search string matching]" )
175
175
  ;;
176
176
  show)
177
- args=( {'(--age)-a','(-a)--age'}"[Age]:AGE:" "--after[Show entries newer than date]:DATE_STRING:" "--before[Show entries older than date]:DATE_STRING:" "--bool[Boolean used to combine multiple tags]:BOOLEAN:" {'(--count)-c','(-c)--count'}"[Max count to show]:MAX:" "--case[Case sensitivity for search string matching ((c)ase-sensitive]:TYPE:" "--config_template[Output using a template from configuration]:TEMPLATE_KEY:" "--duration[Show elapsed time on entries without @done tag]" "--from[Date range]:DATE_OR_RANGE:" {'(--hilite)-h','(-h)--hilite'}"[Highlight search matches in output]" {'(--interactive)-i','(-i)--interactive'}"[Select from a menu of matching entries to perform additional operations]" {'(--menu)-m','(-m)--menu'}"[Select section or tag to display from a menu]" "--not[Show items that *dont* match search/tag filterst* match search/tag filters]" {'(--output)-o','(-o)--output'}"[Output to export format]:FORMAT:" "--only_timed[Only show items with recorded time intervals]" {'(--sort)-s','(-s)--sort'}"[Sort order]:ORDER:" "--search[Filter entries using a search query]:QUERY:" {'(--times)-t','(-t)--times'}"[Show time intervals on @done tasks]" "--tag[Filter entries by tag]:TAG:" "--tag_order[Tag sort direction]:DIRECTION:" "--tag_sort[Sort tags by]:KEY:" "--template[Override output format with a template string containing %placeholders]:TEMPLATE_STRING:" "--totals[Show time totals at the end of output]" "--val[Perform a tag value query]:QUERY:" {'(--exact)-x','(-x)--exact'}"[Force exact search string matching]" )
177
+ args=( {'(--age)-a','(-a)--age'}"[Age]:AGE:" "--after[Show entries newer than date]:DATE_STRING:" "--before[Show entries older than date]:DATE_STRING:" "--bool[Boolean used to combine multiple tags]:BOOLEAN:" {'(--count)-c','(-c)--count'}"[Max count to show]:MAX:" "--case[Case sensitivity for search string matching ((c)ase-sensitive]:TYPE:" "--config_template[Output using a template from configuration]:TEMPLATE_KEY:" "--duration[Show elapsed time on entries without @done tag]" {'(--editor)-e','(-e)--editor'}"[Edit matching entries with vim]" "--from[Date range]:DATE_OR_RANGE:" {'(--hilite)-h','(-h)--hilite'}"[Highlight search matches in output]" {'(--interactive)-i','(-i)--interactive'}"[Select from a menu of matching entries to perform additional operations]" {'(--menu)-m','(-m)--menu'}"[Select section or tag to display from a menu]" "--not[Show items that *dont* match search/tag filterst* match search/tag filters]" {'(--output)-o','(-o)--output'}"[Output to export format]:FORMAT:" "--only_timed[Only show items with recorded time intervals]" {'(--sort)-s','(-s)--sort'}"[Sort order]:ORDER:" "--search[Filter entries using a search query]:QUERY:" {'(--times)-t','(-t)--times'}"[Show time intervals on @done tasks]" "--tag[Filter entries by tag]:TAG:" "--tag_order[Tag sort direction]:DIRECTION:" "--tag_sort[Sort tags by]:KEY:" "--template[Override output format with a template string containing %placeholders]:TEMPLATE_STRING:" "--totals[Show time totals at the end of output]" "--val[Perform a tag value query]:QUERY:" {'(--exact)-x','(-x)--exact'}"[Force exact search string matching]" )
178
178
  ;;
179
179
  since)
180
180
  args=( "--bool[Boolean used to combine multiple tags]:BOOLEAN:" "--case[Case sensitivity for search string matching ((c)ase-sensitive]:TYPE:" "--config_template[Output using a template from configuration]:TEMPLATE_KEY:" "--duration[Show elapsed time on entries without @done tag]" "--not[Since items that *dont* match search/tag filterst* match search/tag filters]" {'(--output)-o','(-o)--output'}"[Output to export format]:FORMAT:" "--only_timed[Only show items with recorded time intervals]" {'(--section)-s','(-s)--section'}"[Section]:NAME:" "--search[Filter entries using a search query]:QUERY:" {'(--times)-t','(-t)--times'}"[Show time intervals on @done tasks]" "--tag[Filter entries by tag]:TAG:" "--tag_order[Tag sort direction]:DIRECTION:" "--tag_sort[Sort tags by]:KEY:" "--template[Override output format with a template string containing %placeholders]:TEMPLATE_STRING:" "--totals[Show time totals at the end of output]" "--val[Perform a tag value query]:QUERY:" {'(--exact)-x','(-x)--exact'}"[Force exact search string matching]" )
@@ -276,9 +276,9 @@ local words=$(doing sections)
276
276
  IFS="$OLD_IFS"
277
277
 
278
278
  if [[ "$token" == --* ]]; then
279
- COMPREPLY=( $( compgen -W '--age --after --before --bool --count --case --config_template --duration --from --hilite --interactive --menu --not --output --only_timed --sort --search --times --tag --tag_order --tag_sort --template --totals --val --exact' -- $token ) )
279
+ COMPREPLY=( $( compgen -W '--age --after --before --bool --count --case --config_template --duration --editor --from --hilite --interactive --menu --not --output --only_timed --sort --search --times --tag --tag_order --tag_sort --template --totals --val --exact' -- $token ) )
280
280
  elif [[ "$token" == -* ]]; then
281
- COMPREPLY=( $( compgen -W '-a -c -h -i -m -o -s -t -x --age --after --before --bool --count --case --config_template --duration --from --hilite --interactive --menu --not --output --only_timed --sort --search --times --tag --tag_order --tag_sort --template --totals --val --exact' -- $token ) )
281
+ COMPREPLY=( $( compgen -W '-a -c -e -h -i -m -o -s -t -x --age --after --before --bool --count --case --config_template --duration --editor --from --hilite --interactive --menu --not --output --only_timed --sort --search --times --tag --tag_order --tag_sort --template --totals --val --exact' -- $token ) )
282
282
  else
283
283
  local nocasematchWasOff=0
284
284
  shopt nocasematch >/dev/null || nocasematchWasOff=1
@@ -447,6 +447,7 @@ complete -c doing -l count -s c -f -r -n '__fish_doing_using_command show' -d Ma
447
447
  complete -c doing -l case -f -r -n '__fish_doing_using_command show' -d Case\ sensitivity\ for\ search\ string\ matching\ \[\(c\)ase-sensitive
448
448
  complete -c doing -l config_template -f -r -n '__fish_doing_using_command show' -d Output\ using\ a\ template\ from\ configuration
449
449
  complete -c doing -l duration -f -n '__fish_doing_using_command show' -d Show\ elapsed\ time\ on\ entries\ without\ @done\ tag
450
+ complete -c doing -l editor -s e -f -n '__fish_doing_using_command show' -d Edit\ matching\ entries\ with\ vim
450
451
  complete -c doing -l from -f -r -n '__fish_doing_using_command show' -d Date\ range
451
452
  complete -c doing -l hilite -s h -f -n '__fish_doing_using_command show' -d Highlight\ search\ matches\ in\ output
452
453
  complete -c doing -l interactive -s i -f -n '__fish_doing_using_command show' -d Select\ from\ a\ menu\ of\ matching\ entries\ to\ perform\ additional\ operations
@@ -196,7 +196,9 @@ module Doing
196
196
  finish = dates[-1].chronify(guess: inclusive ? :end : :begin, future: true)
197
197
  end
198
198
 
199
- raise Errors::InvalidTimeExpression, 'Unrecognized date string' if start.nil? || finish.nil?
199
+ raise Errors::InvalidTimeExpression, "Unrecognized date string (#{dates[0]})" if start.nil?
200
+
201
+ raise Errors::InvalidTimeExpression, "Unrecognized date string (#{dates[-1]})" if finish.nil?
200
202
 
201
203
  else
202
204
  if date_string.strip =~ time_rx
data/lib/doing/item.rb CHANGED
@@ -180,8 +180,14 @@ module Doing
180
180
 
181
181
  remove = options.fetch(:remove, false)
182
182
  tags.each do |tag|
183
+ if tag =~ /^(\S+)\((.*?)\)$/
184
+ m = Regexp.last_match
185
+ tag = m[1]
186
+ options[:value] ||= m[2]
187
+ end
188
+
183
189
  bool = remove ? :and : :not
184
- if tags?(tag, bool)
190
+ if tags?(tag, bool) || options[:value]
185
191
  @title = @title.tag(tag, **options).strip
186
192
  remove ? removed.push(tag) : added.push(tag)
187
193
  end
@@ -201,6 +207,16 @@ module Doing
201
207
  @title.scan(/(?<= |\A)@([^\s(]+)/).map { |tag| tag[0] }.sort.uniq
202
208
  end
203
209
 
210
+ ##
211
+ ## Return all tags including parenthetical values
212
+ ##
213
+ ## @return [Array<Array>] Array of array pairs,
214
+ ## [[tag1, value], [tag2, value]]
215
+ ##
216
+ def tags_with_values
217
+ @title.scan(/(?<= |\A)@([^\s(]+)(?:\((.*?)\))?/).map { |tag| [tag[0], tag[1]] }.sort.uniq
218
+ end
219
+
204
220
  ##
205
221
  ## convert tags on item to an array with @ symbols removed
206
222
  ##
@@ -557,6 +573,12 @@ module Doing
557
573
  false
558
574
  end
559
575
 
576
+ def tag_pattern?(tags)
577
+ query = tags.to_query
578
+
579
+ no_tags?(query[:must_not]) && all_tags?(query[:must]) && any_tags?(query[:should])
580
+ end
581
+
560
582
  def tag_value(tag)
561
583
  res = @title.match(/@#{tag.sub(/^@/, '').wildcard_to_rx}\((.*?)\)/)
562
584
  res ? res[1] : nil
@@ -592,6 +614,7 @@ module Doing
592
614
 
593
615
  queries.each do |q|
594
616
  parts = split_value_query(q)
617
+
595
618
  return false unless tag_value_matches?(parts[2], parts[3], parts[4], parts[1])
596
619
  end
597
620
  true
@@ -607,78 +630,153 @@ module Doing
607
630
  true
608
631
  end
609
632
 
633
+ def duration_matches?(value, comp)
634
+ return false if interval.nil?
635
+
636
+ val = value.chronify_qty
637
+ case comp
638
+ when /^<$/
639
+ interval < val
640
+ when /^<=$/
641
+ interval <= val
642
+ when /^>$/
643
+ interval > val
644
+ when /^>=$/
645
+ interval >= val
646
+ when /^!=/
647
+ interval != val
648
+ when /^=/
649
+ interval == val
650
+ end
651
+ end
652
+
653
+ def date_matches?(value, comp)
654
+ time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/i
655
+ value = "#{@date.strftime('%Y-%m-%d')} #{value}" if value =~ time_rx
656
+
657
+ val = value.chronify(guess: :begin)
658
+ raise InvalidTimeExpression, "Unrecognized date/time expression (#{value})" if val.nil?
659
+
660
+ case comp
661
+ when /^<$/
662
+ @date < val
663
+ when /^<=$/
664
+ @date <= val
665
+ when /^>$/
666
+ @date > val
667
+ when /^>=$/
668
+ @date >= val
669
+ when /^!=/
670
+ @date != val
671
+ when /^=/
672
+ @date == val
673
+ end
674
+ end
675
+
676
+ def value_string_matches?(tag_val, comp, value)
677
+ case comp
678
+ when /\^=/
679
+ tag_val =~ /^#{value.wildcard_to_rx}/i
680
+ when /\$=/
681
+ tag_val =~ /#{value.wildcard_to_rx}$/i
682
+ when %r{==}
683
+ tag_val =~ /^#{value.wildcard_to_rx}$/i
684
+ else
685
+ tag_val =~ /#{value.wildcard_to_rx}/i
686
+ end
687
+ end
688
+
689
+ def value_number_matches?(tag_val, comp, value)
690
+ case comp
691
+ when /^<$/
692
+ tag_val < value
693
+ when /^<=$/
694
+ tag_val <= value
695
+ when /^>$/
696
+ tag_val > value
697
+ when /^>=$/
698
+ tag_val >= value
699
+ when /^!=/
700
+ tag_val != value
701
+ when /^=/
702
+ tag_val == value
703
+ end
704
+ end
705
+
706
+ ##
707
+ ## Test if a tag's value matches a given value. Value
708
+ ## can be a date string, a text string, or a
709
+ ## number/percentage. Type of comparison is determined
710
+ ## by the comparitor and the objects being compared.
711
+ ##
712
+ ## @param tag [String] The tag name from which
713
+ ## to get the value
714
+ ## @param comp [String] The comparator (e.g. >=
715
+ ## or *=)
716
+ ## @param value [String] The value to test
717
+ ## against
718
+ ## @param negate [Boolean] Negate the response
719
+ ##
720
+ ## @return True if tag value matches, False otherwise.
721
+ ##
610
722
  def tag_value_matches?(tag, comp, value, negate)
611
- if tag =~ /^(elapsed|dur(ation)?|int(erval)?|t(ime)?)$/
612
-
613
- return false if interval.nil?
614
-
615
- val = value.chronify_qty
616
- is_match = case comp
617
- when /^<$/
618
- interval < val
619
- when /^<=$/
620
- interval <= val
621
- when /^>$/
622
- interval > val
623
- when /^>=$/
624
- interval >= val
625
- when /^!=/
626
- interval != val
627
- when /^=/
628
- interval == val
629
- end
630
- comp =~ /!/ || negate ? !is_match : is_match
631
- elsif all_tags?([tag])
723
+ # If tag matches existing tag
724
+ if tags?(tag, :and)
632
725
  tag_val = tag_value(tag)
633
726
 
727
+ # If the tag value is not a date and contains alpha
728
+ # characters and comparison is ==, or comparison is
729
+ # a string comparitor (*= ^= $=)
634
730
  if (value.chronify.nil? && value =~ /[a-z]/i && comp =~ /^!?==?$/) || comp =~ /[$*^]=/
635
- is_match = case comp
636
- when /\^=/
637
- tag_val =~ /^#{value.wildcard_to_rx}/i
638
- when /\$=/
639
- tag_val =~ /#{value.wildcard_to_rx}$/i
640
- when %r{==}
641
- tag_val =~ /^#{value.wildcard_to_rx}$/i
642
- else
643
- tag_val =~ /#{value.wildcard_to_rx}/i
644
- end
731
+ is_match = value_string_matches?(tag_val, comp, value)
645
732
 
646
733
  comp =~ /!/ || negate ? !is_match : is_match
647
734
  else
735
+ # Convert values to either a number or a date
648
736
  tag_val = number_or_date(tag_val)
649
737
  val = number_or_date(value)
650
738
 
739
+ # Fail if either value is nil
651
740
  return false if val.nil? || tag_val.nil?
652
741
 
742
+ # Fail unless both values are of the same class (float or date)
653
743
  return false unless val.class == tag_val.class
654
744
 
655
- matches = case comp
656
- when /^<$/
657
- tag_val < val
658
- when /^<=$/
659
- tag_val <= val
660
- when /^>$/
661
- tag_val > val
662
- when /^>=$/
663
- tag_val >= val
664
- when /^!=/
665
- tag_val != val
666
- when /^=/
667
- tag_val == val
668
- end
669
- negate.nil? ? matches : !matches
745
+ is_match = value_number_matches?(tag_val, comp, val)
746
+
747
+ negate.nil? ? is_match : !is_match
670
748
  end
749
+ # If tag name matches a trigger for elapsed time test
750
+ elsif tag =~ /^(elapsed|dur(ation)?|int(erval)?)$/i
751
+ is_match = duration_matches?(value, comp)
752
+
753
+ comp =~ /!/ || negate ? !is_match : is_match
754
+ # Else if tag name matches a trigger for start date
755
+ elsif tag =~ /^(d(ate)?|t(ime)?)$/i
756
+ is_match = date_matches?(value, comp)
757
+
758
+ comp =~ /!/ || negate ? !is_match : is_match
759
+ # Else if tag name matches a trigger for all text
760
+ elsif tag =~ /^text$/i
761
+ is_match = value_string_matches?([@title, @note.to_s(prefix: '')].join(' '), comp, value)
762
+
763
+ comp =~ /!/ || negate ? !is_match : is_match
764
+ # Else if tag name matches a trigger for title
765
+ elsif tag =~ /^title$/i
766
+ is_match = value_string_matches?(@title, comp, value)
767
+
768
+ comp =~ /!/ || negate ? !is_match : is_match
769
+ # Else if tag name matches a trigger for note
770
+ elsif tag =~ /^note$/i
771
+ is_match = value_string_matches?(@note.to_s(prefix: ''), comp, value)
772
+
773
+ comp =~ /!/ || negate ? !is_match : is_match
774
+ # Else if item contains tag being tested
671
775
  else
672
776
  false
673
777
  end
674
778
  end
675
779
 
676
- def tag_pattern?(tags)
677
- query = tags.to_query
678
-
679
- no_tags?(query[:must_not]) && all_tags?(query[:must]) && any_tags?(query[:should])
680
- end
681
-
682
780
  def split_tags(tags)
683
781
  tags.to_tags.tags_to_array
684
782
  end
data/lib/doing/items.rb CHANGED
@@ -179,7 +179,7 @@ module Doing
179
179
  out = []
180
180
  @sections.each do |section|
181
181
  out.push(section.original)
182
- items = in_section(section.title).sort_by(&:date)
182
+ items = in_section(section.title).sort_by { |i| [i.date, i.title] }
183
183
  items.reverse! if Doing.setting('doing_file_sort').normalize_order == :desc
184
184
  items.each { |item| out.push(item.to_s) }
185
185
  end
data/lib/doing/prompt.rb CHANGED
@@ -58,14 +58,12 @@ module Doing
58
58
  comp = proc { |s| completions.grep(/^#{Regexp.escape(s)}/) }
59
59
  Readline.completion_append_character = ' '
60
60
  Readline.completion_proc = comp
61
- prompt_text = []
62
- prompt_text << boldgreen(prompt.sub(/:?$/, ':'))
63
- prompt_text << yellow(' Enter a blank line (')
64
- prompt_text << boldwhite('return twice')
65
- prompt_text << yellow(') to end editing and save, ')
66
- prompt_text << boldwhite('CTRL-C')
67
- prompt_text << yellow(' to cancel')
68
- puts prompt_text.join('')
61
+ puts format(['%<promptcolor>s%<prompt>s %<textcolor>sEnter a blank line',
62
+ '(%<keycolor>sreturn twice%<textcolor>s)',
63
+ 'to end editing and save,',
64
+ '%<keycolor>sCTRL-C%<textcolor>s to cancel%<reset>s'].join(' '),
65
+ { promptcolor: boldgreen, prompt: prompt.sub(/:?$/, ':'),
66
+ textcolor: yellow, keycolor: boldwhite, reset: reset })
69
67
 
70
68
  res = []
71
69
 
@@ -117,14 +117,20 @@ module Doing
117
117
  else
118
118
  Doing.logger.debug('Skipped:', "not tagged #{"@#{tag}".cyan}")
119
119
  end
120
- elsif title =~ /@#{tag}(?=[ (]|$)/
120
+ elsif title =~ /@#{tag}(?=[ (]|$)/ && !value.good?
121
121
  Doing.logger.debug('Skipped:', "already tagged #{"@#{tag}".cyan}")
122
122
  return title
123
123
  else
124
124
  add = tag
125
125
  add += "(#{value})" unless value.nil?
126
+
126
127
  title.chomp!
127
- title += " @#{add}"
128
+
129
+ if value && title =~ /@#{tag}(?=[ (]|$)/
130
+ title.sub!(/@#{tag}(\(.*?\))?/, "@#{add}")
131
+ else
132
+ title += " @#{add}"
133
+ end
128
134
 
129
135
  title.dedup_tags!
130
136
  title.chomp!
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.1.38'
2
+ VERSION = '2.1.39'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -665,7 +665,7 @@ module Doing
665
665
  ##
666
666
  def filter_items(items = Items.new, opt: {})
667
667
  logger.benchmark(:filter_items, :start)
668
- time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/
668
+ time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/i
669
669
 
670
670
  if items.nil? || items.empty?
671
671
  section = opt[:section] ? guess_section(opt[:section]) : 'All'
@@ -1292,9 +1292,14 @@ module Doing
1292
1292
  next
1293
1293
  end
1294
1294
 
1295
-
1296
1295
  tag = tag.strip
1297
1296
 
1297
+ if tag =~ /^(\S+)\((.*?)\)$/
1298
+ m = Regexp.last_match
1299
+ tag = m[1]
1300
+ opt[:value] ||= m[2]
1301
+ end
1302
+
1298
1303
  if tag =~ /^done$/ && opt[:date] && item.should_time?
1299
1304
  max_elapsed = Doing.setting('interaction.confirm_longer_than', 0)
1300
1305
  max_elapsed = max_elapsed.chronify_qty if max_elapsed.is_a?(String)
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: 2.1.38
4
+ version: 2.1.39
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-12 00:00:00.000000000 Z
11
+ date: 2022-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: github-markup