doing 2.1.36 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -4912
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +7 -1
- data/bin/commands/done.rb +4 -4
- data/bin/commands/meanwhile.rb +3 -3
- data/bin/commands/note.rb +2 -2
- data/bin/commands/now.rb +5 -7
- data/bin/commands/on.rb +5 -2
- data/bin/commands/show.rb +3 -0
- data/bin/doing +35 -30
- data/docs/doc/Array.html +1 -1
- data/docs/doc/BooleanTermParser/Clause.html +1 -1
- data/docs/doc/BooleanTermParser/Operator.html +1 -1
- data/docs/doc/BooleanTermParser/Query.html +1 -1
- data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
- data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/Color.html +1 -1
- data/docs/doc/Doing/Completion.html +1 -1
- data/docs/doc/Doing/Configuration.html +1 -1
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
- data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
- data/docs/doc/Doing/Errors/NoResults.html +1 -1
- data/docs/doc/Doing/Errors/PluginException.html +1 -1
- data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
- data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/Hooks.html +1 -1
- data/docs/doc/Doing/Item.html +64 -1
- data/docs/doc/Doing/Items.html +1 -1
- data/docs/doc/Doing/Logger.html +1 -1
- data/docs/doc/Doing/Note.html +1 -1
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +1 -1
- data/docs/doc/Doing/Prompt.html +1 -1
- data/docs/doc/Doing/Section.html +1 -1
- data/docs/doc/Doing/TemplateString.html +1 -1
- data/docs/doc/Doing/Types.html +1 -1
- data/docs/doc/Doing/Util/Backup.html +1 -1
- data/docs/doc/Doing/Util.html +1 -1
- data/docs/doc/Doing/WWID.html +1 -1
- data/docs/doc/Doing.html +2 -2
- data/docs/doc/FalseClass.html +1 -1
- data/docs/doc/GLI/Commands/Help.html +1 -1
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/docs/doc/GLI/Commands.html +1 -1
- data/docs/doc/GLI.html +1 -1
- data/docs/doc/Hash.html +1 -1
- data/docs/doc/Object.html +1 -1
- data/docs/doc/PhraseParser/Operator.html +1 -1
- data/docs/doc/PhraseParser/PhraseClause.html +1 -1
- data/docs/doc/PhraseParser/Query.html +1 -1
- data/docs/doc/PhraseParser/QueryParser.html +1 -1
- data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
- data/docs/doc/PhraseParser/TermClause.html +1 -1
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +1 -1
- data/docs/doc/String.html +1 -1
- data/docs/doc/Symbol.html +1 -1
- data/docs/doc/Time.html +1 -1
- data/docs/doc/TrueClass.html +1 -1
- data/docs/doc/_index.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +159 -151
- data/docs/doc/top-level-namespace.html +1 -1
- data/doing.rdoc +6 -1
- data/lib/completion/_doing.zsh +1 -1
- data/lib/completion/doing.bash +2 -2
- data/lib/completion/doing.fish +1 -0
- data/lib/doing/chronify/array.rb +8 -0
- data/lib/doing/chronify/string.rb +8 -5
- data/lib/doing/item.rb +166 -36
- data/lib/doing/items.rb +1 -1
- data/lib/doing/prompt.rb +6 -6
- data/lib/doing/string/tags.rb +8 -2
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +11 -2
- data/lib/doing.rb +1 -0
- metadata +2 -2
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
</div>
|
|
207
207
|
|
|
208
208
|
<div id="footer">
|
|
209
|
-
Generated on
|
|
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.
|
|
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
|
|
data/lib/completion/_doing.zsh
CHANGED
|
@@ -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]" )
|
data/lib/completion/doing.bash
CHANGED
|
@@ -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
|
data/lib/completion/doing.fish
CHANGED
|
@@ -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
|
data/lib/doing/chronify/array.rb
CHANGED
|
@@ -18,8 +18,16 @@ module Doing
|
|
|
18
18
|
d, h, m = self
|
|
19
19
|
case format
|
|
20
20
|
when :clock
|
|
21
|
+
if d.zero? && h > 24
|
|
22
|
+
d = (h / 24).floor
|
|
23
|
+
h = h % 24
|
|
24
|
+
end
|
|
21
25
|
format('%<d>02d:%<h>02d:%<m>02d', d: d, h: h, m: m)
|
|
22
26
|
when :dhm
|
|
27
|
+
if d.zero? && h > 24
|
|
28
|
+
d = (h / 24).floor
|
|
29
|
+
h = h % 24
|
|
30
|
+
end
|
|
23
31
|
output = []
|
|
24
32
|
output.push(format('%<d>dd', d: d)) if d.positive?
|
|
25
33
|
output.push(format('%<h>dh', h: h)) if h.positive?
|
|
@@ -43,8 +43,8 @@ module Doing
|
|
|
43
43
|
Doing.logger.debug('Parser:', %(date/time string "#{self}" interpreted as #{res} (#{secs_ago} seconds ago)))
|
|
44
44
|
else
|
|
45
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]
|
|
46
|
+
date_string = 'today' if date_string.match(Types::REGEX_DAY) && now.strftime('%a') =~ /^#{Regexp.last_match(1)}/i
|
|
47
|
+
date_string = "#{options[:context].to_s} #{date_string}" if date_string =~ Types::REGEX_TIME && options[:context]
|
|
48
48
|
|
|
49
49
|
res = Chronic.parse(date_string, {
|
|
50
50
|
guess: options.fetch(:guess, :begin),
|
|
@@ -176,7 +176,7 @@ module Doing
|
|
|
176
176
|
## "mon 3pm to mon 5pm".split_date_range
|
|
177
177
|
##
|
|
178
178
|
def split_date_range
|
|
179
|
-
time_rx = /^(\d{1,2}
|
|
179
|
+
time_rx = /^(\d{1,2}(:\d{1,2})?( *(am|pm))?|midnight|noon)$/
|
|
180
180
|
range_rx = / (to|through|thru|(?:un)?til|-+) /
|
|
181
181
|
|
|
182
182
|
date_string = dup
|
|
@@ -187,15 +187,18 @@ module Doing
|
|
|
187
187
|
inclusive = true
|
|
188
188
|
|
|
189
189
|
dates = date_string.split(range_rx)
|
|
190
|
+
|
|
190
191
|
if dates[0].strip =~ time_rx && dates[-1].strip =~ time_rx
|
|
191
192
|
start = dates[0].strip
|
|
192
193
|
finish = dates[-1].strip
|
|
193
194
|
else
|
|
194
195
|
start = dates[0].chronify(guess: :begin, future: false)
|
|
195
|
-
finish = dates[-1].chronify(guess: inclusive ? :end : :begin, future:
|
|
196
|
+
finish = dates[-1].chronify(guess: inclusive ? :end : :begin, future: true)
|
|
196
197
|
end
|
|
197
198
|
|
|
198
|
-
raise Errors::InvalidTimeExpression,
|
|
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?
|
|
199
202
|
|
|
200
203
|
else
|
|
201
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
|
##
|
|
@@ -522,7 +538,11 @@ module Doing
|
|
|
522
538
|
return true unless tags.good?
|
|
523
539
|
|
|
524
540
|
tags.each do |tag|
|
|
525
|
-
|
|
541
|
+
if tag =~ /done/ && !should_finish?
|
|
542
|
+
next
|
|
543
|
+
else
|
|
544
|
+
return false unless @title =~ /@#{tag.wildcard_to_rx}(?= |\(|\Z)/i
|
|
545
|
+
end
|
|
526
546
|
end
|
|
527
547
|
true
|
|
528
548
|
end
|
|
@@ -531,7 +551,11 @@ module Doing
|
|
|
531
551
|
return true unless tags.good?
|
|
532
552
|
|
|
533
553
|
tags.each do |tag|
|
|
534
|
-
|
|
554
|
+
if tag =~ /done/ && !should_finish?
|
|
555
|
+
return false
|
|
556
|
+
else
|
|
557
|
+
return false if @title =~ /@#{tag.wildcard_to_rx}(?= |\(|\Z)/i
|
|
558
|
+
end
|
|
535
559
|
end
|
|
536
560
|
true
|
|
537
561
|
end
|
|
@@ -540,11 +564,21 @@ module Doing
|
|
|
540
564
|
return true unless tags.good?
|
|
541
565
|
|
|
542
566
|
tags.each do |tag|
|
|
543
|
-
|
|
567
|
+
if tag =~ /done/ && !should_finish?
|
|
568
|
+
return true
|
|
569
|
+
else
|
|
570
|
+
return true if @title =~ /@#{tag.wildcard_to_rx}(?= |\(|\Z)/i
|
|
571
|
+
end
|
|
544
572
|
end
|
|
545
573
|
false
|
|
546
574
|
end
|
|
547
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
|
+
|
|
548
582
|
def tag_value(tag)
|
|
549
583
|
res = @title.match(/@#{tag.sub(/^@/, '').wildcard_to_rx}\((.*?)\)/)
|
|
550
584
|
res ? res[1] : nil
|
|
@@ -580,6 +614,7 @@ module Doing
|
|
|
580
614
|
|
|
581
615
|
queries.each do |q|
|
|
582
616
|
parts = split_value_query(q)
|
|
617
|
+
|
|
583
618
|
return false unless tag_value_matches?(parts[2], parts[3], parts[4], parts[1])
|
|
584
619
|
end
|
|
585
620
|
true
|
|
@@ -595,58 +630,153 @@ module Doing
|
|
|
595
630
|
true
|
|
596
631
|
end
|
|
597
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
|
+
##
|
|
598
722
|
def tag_value_matches?(tag, comp, value, negate)
|
|
599
|
-
|
|
723
|
+
# If tag matches existing tag
|
|
724
|
+
if tags?(tag, :and)
|
|
600
725
|
tag_val = tag_value(tag)
|
|
601
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 (*= ^= $=)
|
|
602
730
|
if (value.chronify.nil? && value =~ /[a-z]/i && comp =~ /^!?==?$/) || comp =~ /[$*^]=/
|
|
603
|
-
is_match =
|
|
604
|
-
when /\^=/
|
|
605
|
-
tag_val =~ /^#{value.wildcard_to_rx}/i
|
|
606
|
-
when /\$=/
|
|
607
|
-
tag_val =~ /#{value.wildcard_to_rx}$/i
|
|
608
|
-
when %r{==}
|
|
609
|
-
tag_val =~ /^#{value.wildcard_to_rx}$/i
|
|
610
|
-
else
|
|
611
|
-
tag_val =~ /#{value.wildcard_to_rx}/i
|
|
612
|
-
end
|
|
731
|
+
is_match = value_string_matches?(tag_val, comp, value)
|
|
613
732
|
|
|
614
733
|
comp =~ /!/ || negate ? !is_match : is_match
|
|
615
734
|
else
|
|
735
|
+
# Convert values to either a number or a date
|
|
616
736
|
tag_val = number_or_date(tag_val)
|
|
617
737
|
val = number_or_date(value)
|
|
618
738
|
|
|
739
|
+
# Fail if either value is nil
|
|
619
740
|
return false if val.nil? || tag_val.nil?
|
|
620
741
|
|
|
742
|
+
# Fail unless both values are of the same class (float or date)
|
|
621
743
|
return false unless val.class == tag_val.class
|
|
622
744
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
when /^<=$/
|
|
627
|
-
tag_val <= val
|
|
628
|
-
when /^>$/
|
|
629
|
-
tag_val > val
|
|
630
|
-
when /^>=$/
|
|
631
|
-
tag_val >= val
|
|
632
|
-
when /^!=/
|
|
633
|
-
tag_val != val
|
|
634
|
-
when /^=/
|
|
635
|
-
tag_val == val
|
|
636
|
-
end
|
|
637
|
-
negate.nil? ? matches : !matches
|
|
745
|
+
is_match = value_number_matches?(tag_val, comp, val)
|
|
746
|
+
|
|
747
|
+
negate.nil? ? is_match : !is_match
|
|
638
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
|
|
639
775
|
else
|
|
640
776
|
false
|
|
641
777
|
end
|
|
642
778
|
end
|
|
643
779
|
|
|
644
|
-
def tag_pattern?(tags)
|
|
645
|
-
query = tags.to_query
|
|
646
|
-
|
|
647
|
-
no_tags?(query[:must_not]) && all_tags?(query[:must]) && any_tags?(query[:should])
|
|
648
|
-
end
|
|
649
|
-
|
|
650
780
|
def split_tags(tags)
|
|
651
781
|
tags.to_tags.tags_to_array
|
|
652
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
|
|
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,12 +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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 })
|
|
67
67
|
|
|
68
68
|
res = []
|
|
69
69
|
|
data/lib/doing/string/tags.rb
CHANGED
|
@@ -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
|
-
|
|
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
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)
|
|
@@ -1688,9 +1693,13 @@ module Doing
|
|
|
1688
1693
|
|
|
1689
1694
|
if opt[:delete]
|
|
1690
1695
|
delete_items(items, force: opt[:force])
|
|
1696
|
+
|
|
1697
|
+
write(@doing_file)
|
|
1691
1698
|
return
|
|
1692
1699
|
elsif opt[:editor]
|
|
1693
1700
|
edit_items(items)
|
|
1701
|
+
|
|
1702
|
+
write(@doing_file)
|
|
1694
1703
|
return
|
|
1695
1704
|
elsif opt[:interactive]
|
|
1696
1705
|
opt[:menu] = !opt[:force]
|
data/lib/doing.rb
CHANGED
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.
|
|
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-
|
|
11
|
+
date: 2022-03-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: github-markup
|