doing 2.1.38 → 2.1.39

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 (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