doing 2.1.40 → 2.1.41

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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile.lock +1 -1
  5. data/Rakefile +4 -4
  6. data/bin/commands/changes.rb +1 -1
  7. data/bin/commands/tag_dir.rb +49 -15
  8. data/{Dockerfile → docker/Dockerfile} +3 -1
  9. data/{Dockerfile-2.6 → docker/Dockerfile-2.6} +2 -2
  10. data/{Dockerfile-2.7 → docker/Dockerfile-2.7} +2 -2
  11. data/{Dockerfile-3.0 → docker/Dockerfile-3.0} +2 -2
  12. data/{bash_profile → docker/bash_profile} +0 -0
  13. data/{inputrc → docker/inputrc} +0 -0
  14. data/docs/doc/Array.html +84 -2
  15. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  16. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  17. data/docs/doc/BooleanTermParser/Query.html +1 -1
  18. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  19. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  20. data/docs/doc/BooleanTermParser.html +1 -1
  21. data/docs/doc/Doing/ArrayNestedHash.html +198 -0
  22. data/docs/doc/Doing/ArrayTags.html +424 -0
  23. data/docs/doc/Doing/CSVExport.html +266 -0
  24. data/docs/doc/Doing/CalendarImport.html +232 -0
  25. data/docs/doc/Doing/Change.html +617 -0
  26. data/docs/doc/Doing/Changes.html +468 -0
  27. data/docs/doc/Doing/ChronifyArray.html +347 -0
  28. data/docs/doc/Doing/ChronifyNumeric.html +271 -0
  29. data/docs/doc/Doing/ChronifyString.html +682 -0
  30. data/docs/doc/Doing/Color.html +2 -2
  31. data/docs/doc/Doing/Completion/BashCompletions.html +445 -0
  32. data/docs/doc/Doing/Completion/FishCompletions.html +445 -0
  33. data/docs/doc/Doing/Completion/StringUtils.html +229 -0
  34. data/docs/doc/Doing/Completion/ZshCompletions.html +445 -0
  35. data/docs/doc/Doing/Completion.html +17 -3
  36. data/docs/doc/Doing/Configuration.html +1 -1
  37. data/docs/doc/Doing/DayOneRenderer.html +383 -0
  38. data/docs/doc/Doing/DayoneExport.html +290 -0
  39. data/docs/doc/Doing/DoingImport.html +391 -0
  40. data/docs/doc/Doing/Entry.html +381 -0
  41. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  42. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  43. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  44. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  45. data/docs/doc/Doing/Errors/HistoryLimitError.html +1 -1
  46. data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
  47. data/docs/doc/Doing/Errors/MissingBackupFile.html +1 -1
  48. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  49. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  50. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  51. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  52. data/docs/doc/Doing/Errors.html +1 -1
  53. data/docs/doc/Doing/HTMLExport.html +256 -0
  54. data/docs/doc/Doing/Hooks.html +1 -1
  55. data/docs/doc/Doing/Item.html +47 -3
  56. data/docs/doc/Doing/ItemDates.html +564 -0
  57. data/docs/doc/Doing/ItemQuery.html +614 -0
  58. data/docs/doc/Doing/ItemState.html +387 -0
  59. data/docs/doc/Doing/ItemTags.html +498 -0
  60. data/docs/doc/Doing/Items.html +460 -11
  61. data/docs/doc/Doing/JSONExport.html +222 -0
  62. data/docs/doc/Doing/Logger.html +1 -1
  63. data/docs/doc/Doing/MarkdownExport.html +266 -0
  64. data/docs/doc/Doing/MarkdownRenderer.html +383 -0
  65. data/docs/doc/Doing/Note.html +16 -3
  66. data/docs/doc/Doing/Pager.html +1 -1
  67. data/docs/doc/Doing/Plugins.html +1 -1
  68. data/docs/doc/Doing/Prompt.html +31 -682
  69. data/docs/doc/Doing/PromptChoose.html +484 -0
  70. data/docs/doc/Doing/PromptFZF.html +391 -0
  71. data/docs/doc/Doing/PromptInput.html +572 -0
  72. data/docs/doc/Doing/PromptSTD.html +293 -0
  73. data/docs/doc/Doing/PromptYN.html +237 -0
  74. data/docs/doc/Doing/Section.html +58 -2
  75. data/docs/doc/Doing/StringHighlight.html +533 -0
  76. data/docs/doc/Doing/StringNormalize.html +929 -0
  77. data/docs/doc/Doing/StringQuery.html +725 -0
  78. data/docs/doc/Doing/StringTags.html +884 -0
  79. data/docs/doc/Doing/StringTransform.html +565 -0
  80. data/docs/doc/Doing/StringTruncate.html +448 -0
  81. data/docs/doc/Doing/StringURL.html +409 -0
  82. data/docs/doc/Doing/SymbolNormalize.html +341 -0
  83. data/docs/doc/Doing/TaskPaperExport.html +222 -0
  84. data/docs/doc/Doing/TemplateExport.html +249 -0
  85. data/docs/doc/Doing/TemplateString.html +101 -2
  86. data/docs/doc/Doing/TimingImport.html +285 -0
  87. data/docs/doc/Doing/Types.html +1 -1
  88. data/docs/doc/Doing/Util/Backup.html +9 -7
  89. data/docs/doc/Doing/Util.html +2 -2
  90. data/docs/doc/Doing/Version.html +523 -0
  91. data/docs/doc/Doing/WWID/WWIDUtil.html +510 -0
  92. data/docs/doc/Doing/WWID.html +4377 -217
  93. data/docs/doc/Doing/WWIDDisplay.html +865 -0
  94. data/docs/doc/Doing/WWIDEditor.html +466 -0
  95. data/docs/doc/Doing/WWIDFileTools.html +359 -0
  96. data/docs/doc/Doing/WWIDFilter.html +466 -0
  97. data/docs/doc/Doing/WWIDGuess.html +299 -0
  98. data/docs/doc/Doing/WWIDInteractive.html +752 -0
  99. data/docs/doc/Doing/WWIDModify.html +1078 -0
  100. data/docs/doc/Doing/WWIDTags.html +302 -0
  101. data/docs/doc/Doing/WWIDTimers.html +359 -0
  102. data/docs/doc/Doing/WWIDUtil.html +510 -0
  103. data/docs/doc/Doing.html +9 -6
  104. data/docs/doc/FalseClass.html +1 -1
  105. data/docs/doc/GLI/Commands/Help.html +1 -1
  106. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  107. data/docs/doc/GLI/Commands.html +1 -1
  108. data/docs/doc/GLI.html +1 -1
  109. data/docs/doc/Hash.html +1 -1
  110. data/docs/doc/Numeric.html +23 -78
  111. data/docs/doc/Object.html +1 -1
  112. data/docs/doc/PhraseParser/Operator.html +1 -1
  113. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  114. data/docs/doc/PhraseParser/Query.html +1 -1
  115. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  116. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  117. data/docs/doc/PhraseParser/TermClause.html +1 -1
  118. data/docs/doc/PhraseParser.html +1 -1
  119. data/docs/doc/Status.html +1 -1
  120. data/docs/doc/String.html +58 -633
  121. data/docs/doc/Symbol.html +9 -224
  122. data/docs/doc/Time.html +119 -13
  123. data/docs/doc/TrueClass.html +1 -1
  124. data/docs/doc/_index.html +324 -8
  125. data/docs/doc/class_list.html +1 -1
  126. data/docs/doc/file.README.html +1 -1
  127. data/docs/doc/index.html +1 -1
  128. data/docs/doc/method_list.html +2326 -542
  129. data/docs/doc/top-level-namespace.html +2 -2
  130. data/doing.rdoc +13 -3
  131. data/lib/completion/_doing.zsh +1 -1
  132. data/lib/completion/doing.bash +2 -2
  133. data/lib/completion/doing.fish +3 -1
  134. data/lib/doing/array/array.rb +16 -12
  135. data/lib/doing/array/nested_hash.rb +1 -1
  136. data/lib/doing/array/tags.rb +6 -5
  137. data/lib/doing/changelog/changelog.rb +6 -0
  138. data/lib/doing/chronify/array.rb +1 -3
  139. data/lib/doing/chronify/chronify.rb +12 -0
  140. data/lib/doing/chronify/numeric.rb +3 -2
  141. data/lib/doing/chronify/string.rb +1 -1
  142. data/lib/doing/completion/completion_string.rb +25 -0
  143. data/lib/doing/completion.rb +1 -1
  144. data/lib/doing/good.rb +8 -0
  145. data/lib/doing/item/dates.rb +1 -1
  146. data/lib/doing/{item.rb → item/item.rb} +10 -5
  147. data/lib/doing/item/query.rb +1 -1
  148. data/lib/doing/item/state.rb +1 -1
  149. data/lib/doing/item/tags.rb +1 -1
  150. data/lib/doing/items/filter.rb +67 -0
  151. data/lib/doing/items/items.rb +57 -0
  152. data/lib/doing/items/modify.rb +36 -0
  153. data/lib/doing/items/sections.rb +83 -0
  154. data/lib/doing/items/util.rb +74 -0
  155. data/lib/doing/normalize.rb +10 -2
  156. data/lib/doing/plugins/export/markdown_export.rb +4 -2
  157. data/lib/doing/plugins/import/doing_import.rb +1 -1
  158. data/lib/doing/prompt/choose.rb +118 -0
  159. data/lib/doing/prompt/fzf.rb +84 -0
  160. data/lib/doing/prompt/input.rb +129 -0
  161. data/lib/doing/prompt/prompt.rb +41 -0
  162. data/lib/doing/prompt/std.rb +32 -0
  163. data/lib/doing/prompt/yn.rb +64 -0
  164. data/lib/doing/section.rb +4 -0
  165. data/lib/doing/string/highlight.rb +1 -1
  166. data/lib/doing/string/query.rb +1 -1
  167. data/lib/doing/string/string.rb +18 -7
  168. data/lib/doing/string/tags.rb +14 -3
  169. data/lib/doing/string/transform.rb +1 -1
  170. data/lib/doing/string/truncate.rb +1 -1
  171. data/lib/doing/string/url.rb +1 -1
  172. data/lib/doing/time.rb +19 -1
  173. data/lib/doing/util_backup.rb +2 -2
  174. data/lib/doing/version.rb +1 -1
  175. data/lib/doing/wwid/display.rb +357 -360
  176. data/lib/doing/wwid/editor.rb +173 -176
  177. data/lib/doing/wwid/filetools.rb +156 -159
  178. data/lib/doing/wwid/filter.rb +191 -183
  179. data/lib/doing/wwid/guess.rb +58 -60
  180. data/lib/doing/wwid/interactive.rb +332 -330
  181. data/lib/doing/wwid/modify.rb +509 -512
  182. data/lib/doing/wwid/tags.rb +38 -41
  183. data/lib/doing/wwid/timers.rb +293 -296
  184. data/lib/doing/{wwid.rb → wwid/wwid.rb} +32 -23
  185. data/lib/doing/wwid/wwidutil.rb +79 -82
  186. data/lib/doing.rb +5 -5
  187. data/lib/helpers/threaded_tests.rb +1 -0
  188. metadata +76 -14
  189. data/lib/doing/changelog.rb +0 -6
  190. data/lib/doing/completion/string.rb +0 -17
  191. data/lib/doing/items.rb +0 -221
  192. data/lib/doing/prompt.rb +0 -330
@@ -2,217 +2,225 @@
2
2
 
3
3
  module Doing
4
4
  class WWID
5
- # Filter methods for WWID class
6
- module Filter
7
- def fuzzy_filter_items(items, opt: {})
8
- scannable = items.map.with_index { |item, idx| "#{item.title} #{item.note.join(' ')}".gsub(/[|*?!]/, '') + "|#{idx}" }.join("\n")
9
-
10
- fzf_args = [
11
- '--multi',
12
- %(--filter="#{opt[:search].sub(/^'?/, "'")}"),
13
- '--no-sort',
14
- '-d "\|"',
15
- '--nth=1'
16
- ]
17
- if opt[:case]
18
- fzf_args << case opt[:case].normalize_case
19
- when :sensitive
20
- '+i'
21
- when :ignore
22
- '-i'
23
- end
24
- end
25
- # fzf_args << '-e' if opt[:exact]
26
- # puts fzf_args.join(' ')
27
- res = `echo #{Shellwords.escape(scannable)}|#{Prompt.fzf} #{fzf_args.join(' ')}`
28
- selected = Items.new
29
- res.split(/\n/).each do |item|
30
- idx = item.match(/\|(\d+)$/)[1].to_i
31
- selected.push(items[idx])
32
- end
33
- selected
5
+ # Use fzf to filter an Items object with a search query.
6
+ # Faster than {#filter_items} when all you need is a
7
+ # text search of the title and note
8
+ #
9
+ # @param items [Items] an Items object
10
+ # @param query [String] The search query
11
+ # @param case_type [Symbol] The case type (:smart, :sensitive, :ignore)
12
+ #
13
+ # @return [Items] Filtered Items array
14
+ #
15
+ def fuzzy_filter_items(items, query, case_type: :smart)
16
+ scannable = items.map.with_index { |item, idx| "#{item.title} #{item.note.join(' ')}".gsub(/[|*?!]/, '') + "|#{idx}" }.join("\n")
17
+
18
+ fzf_args = [
19
+ '--multi',
20
+ %(--filter="#{query.sub(/^'?/, "'")}"),
21
+ '--no-sort',
22
+ '-d "\|"',
23
+ '--nth=1'
24
+ ]
25
+ fzf_args << case case_type.normalize_case
26
+ when :smart
27
+ query =~ /[A-Z]/ ? '+i' : '-i'
28
+ when :sensitive
29
+ '+i'
30
+ when :ignore
31
+ '-i'
32
+ end
33
+
34
+ # fzf_args << '-e' if opt[:exact]
35
+ # puts fzf_args.join(' ')
36
+ res = `echo #{Shellwords.escape(scannable)}|#{Prompt.fzf} #{fzf_args.join(' ')}`
37
+ selected = Items.new
38
+ res.split(/\n/).each do |item|
39
+ idx = item.match(/\|(\d+)$/)[1].to_i
40
+ selected.push(items[idx])
34
41
  end
42
+ selected
43
+ end
35
44
 
36
- ##
37
- ## Filter items based on search criteria
38
- ##
39
- ## @param items [Array] The items to filter (if empty, filters all items)
40
- ## @param opt [Hash] The filter parameters
41
- ##
42
- ## @option opt [String] :section ('all')
43
- ## @option opt [Boolean] :unfinished (false)
44
- ## @option opt [Array or String] :tag ([]) Array or comma-separated string
45
- ## @option opt [Symbol] :tag_bool (:and) :and, :or, :not
46
- ## @option opt [String] :search ('') string, optional regex with `/string/`
47
- ## @option opt [Array] :date_filter (nil) [[Time]start, [Time]end]
48
- ## @option opt [Boolean] :only_timed (false)
49
- ## @option opt [String] :before (nil) Date/Time string, unparsed
50
- ## @option opt [String] :after (nil) Date/Time string, unparsed
51
- ## @option opt [Boolean] :today (false) limit to entries from today
52
- ## @option opt [Boolean] :yesterday (false) limit to entries from yesterday
53
- ## @option opt [Number] :count (0) max entries to return
54
- ## @option opt [String] :age (new) 'old' or 'new'
55
- ## @option opt [Array] :val (nil) Array of tag value queries
56
- ##
57
- def filter_items(items = Items.new, opt: {})
58
- logger.benchmark(:filter_items, :start)
59
- time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/i
60
-
61
- if items.nil? || items.empty?
62
- section = opt[:section] ? guess_section(opt[:section]) : 'All'
63
- items = section =~ /^all$/i ? @content.clone : @content.in_section(section)
64
- end
45
+ ##
46
+ ## Filter items based on search criteria
47
+ ##
48
+ ## @param items [Array] The items to filter (if empty, filters all items)
49
+ ## @param opt [Hash] The filter parameters
50
+ ##
51
+ ## @option opt [String] :section ('all')
52
+ ## @option opt [Boolean] :unfinished (false)
53
+ ## @option opt [Array or String] :tag ([]) Array or comma-separated string
54
+ ## @option opt [Symbol] :tag_bool (:and) :and, :or, :not
55
+ ## @option opt [String] :search ('') string, optional regex with `/string/`
56
+ ## @option opt [Array] :date_filter (nil) [[Time]start, [Time]end]
57
+ ## @option opt [Boolean] :only_timed (false)
58
+ ## @option opt [String] :before (nil) Date/Time string, unparsed
59
+ ## @option opt [String] :after (nil) Date/Time string, unparsed
60
+ ## @option opt [Boolean] :today (false) limit to entries from today
61
+ ## @option opt [Boolean] :yesterday (false) limit to entries from yesterday
62
+ ## @option opt [Number] :count (0) max entries to return
63
+ ## @option opt [String] :age (new) 'old' or 'new'
64
+ ## @option opt [Array] :val (nil) Array of tag value queries
65
+ ##
66
+ def filter_items(items = Items.new, opt: {})
67
+ logger.benchmark(:filter_items, :start)
68
+ time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/i
69
+
70
+ if items.nil? || items.empty?
71
+ section = opt[:section] ? guess_section(opt[:section]) : 'All'
72
+ items = section =~ /^all$/i ? @content.clone : @content.in_section(section)
73
+ end
65
74
 
66
- if !opt[:time_filter]
67
- opt[:time_filter] = [nil, nil]
68
- if opt[:from] && !opt[:date_filter]
69
- if opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx
70
- opt[:time_filter] = opt[:from]
71
- elsif opt[:from][0].is_a?(Time)
72
- opt[:date_filter] = opt[:from]
73
- end
75
+ unless opt[:time_filter]
76
+ opt[:time_filter] = [nil, nil]
77
+ if opt[:from] && !opt[:date_filter]
78
+ if opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx
79
+ opt[:time_filter] = opt[:from]
80
+ elsif opt[:from][0].is_a?(Time)
81
+ opt[:date_filter] = opt[:from]
74
82
  end
75
83
  end
84
+ end
76
85
 
77
- if opt[:before].is_a?(String) && opt[:before] =~ time_rx
78
- opt[:time_filter][1] = opt[:before]
79
- opt[:before] = nil
80
- end
81
-
82
- if opt[:after].is_a?(String) && opt[:after] =~ time_rx
83
- opt[:time_filter][0] = opt[:after]
84
- opt[:after] = nil
85
- end
86
-
87
- items.sort_by! { |item| [item.date, item.title.downcase] }.reverse
88
-
89
- filtered_items = items.select do |item|
90
- keep = true
91
- if opt[:unfinished]
92
- finished = item.tags?('done', :and)
93
- finished = opt[:not] ? !finished : finished
94
- keep = false if finished
95
- end
96
-
97
- if keep && opt[:val]&.count&.positive?
98
- bool = opt[:bool].normalize_bool if opt[:bool]
99
- bool ||= :and
100
- bool = :and if bool == :pattern
86
+ if opt[:before].is_a?(String) && opt[:before] =~ time_rx
87
+ opt[:time_filter][1] = opt[:before]
88
+ opt[:before] = nil
89
+ end
101
90
 
102
- val_match = opt[:val].nil? || opt[:val].empty? ? true : item.tag_values?(opt[:val], bool)
103
- keep = false unless val_match
104
- keep = opt[:not] ? !keep : keep
105
- end
91
+ if opt[:after].is_a?(String) && opt[:after] =~ time_rx
92
+ opt[:time_filter][0] = opt[:after]
93
+ opt[:after] = nil
94
+ end
106
95
 
107
- if keep && opt[:tag]
108
- opt[:tag_bool] = opt[:bool].normalize_bool if opt[:bool]
109
- opt[:tag_bool] ||= :and
110
- tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.tags?(opt[:tag], opt[:tag_bool])
111
- keep = false unless tag_match
112
- keep = opt[:not] ? !keep : keep
113
- end
96
+ items.sort_by! { |item| [item.date, item.title.downcase] }.reverse
114
97
 
115
- if keep && opt[:search]
116
- search_match = if opt[:search].nil? || opt[:search].empty?
117
- true
118
- else
119
- item.search(opt[:search], case_type: opt[:case].normalize_case)
120
- end
98
+ filtered_items = items.select do |item|
99
+ keep = true
100
+ if opt[:unfinished]
101
+ finished = item.tags?('done', :and)
102
+ finished = opt[:not] ? !finished : finished
103
+ keep = false if finished
104
+ end
121
105
 
122
- keep = false unless search_match
123
- keep = opt[:not] ? !keep : keep
124
- end
106
+ if keep && opt[:val]&.count&.positive?
107
+ bool = opt[:bool].normalize_bool if opt[:bool]
108
+ bool ||= :and
109
+ bool = :and if bool == :pattern
125
110
 
126
- if keep && opt[:date_filter]&.length == 2
127
- start_date = opt[:date_filter][0]
128
- end_date = opt[:date_filter][1]
129
-
130
- in_date_range = if end_date
131
- item.date >= start_date && item.date <= end_date
132
- else
133
- item.date.strftime('%F') == start_date.strftime('%F')
134
- end
135
- keep = false unless in_date_range
136
- keep = opt[:not] ? !keep : keep
137
- end
111
+ val_match = opt[:val].nil? || opt[:val].empty? ? true : item.tag_values?(opt[:val], bool)
112
+ keep = false unless val_match
113
+ keep = opt[:not] ? !keep : keep
114
+ end
138
115
 
139
- if keep && opt[:time_filter][0] || opt[:time_filter][1]
140
- start_string = if opt[:time_filter][0].nil?
141
- "#{item.date.strftime('%Y-%m-%d')} 12am"
142
- else
143
- "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][0]}"
144
- end
145
- start_time = start_string.chronify(guess: :begin)
116
+ if keep && opt[:tag]
117
+ opt[:tag_bool] = opt[:bool].normalize_bool if opt[:bool]
118
+ opt[:tag_bool] ||= :and
119
+ tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.tags?(opt[:tag], opt[:tag_bool])
120
+ keep = false unless tag_match
121
+ keep = opt[:not] ? !keep : keep
122
+ end
146
123
 
147
- end_string = if opt[:time_filter][1].nil?
148
- "#{item.date.to_datetime.next_day.strftime('%Y-%m-%d')} 12am"
124
+ if keep && opt[:search]
125
+ search_match = if opt[:search].nil? || opt[:search].empty?
126
+ true
149
127
  else
150
- "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][1]}"
128
+ item.search(opt[:search], case_type: opt[:case].normalize_case)
151
129
  end
152
- end_time = end_string.chronify(guess: :end)
153
130
 
154
- in_time_range = item.date >= start_time && item.date <= end_time
155
- keep = false unless in_time_range
156
- keep = opt[:not] ? !keep : keep
157
- end
158
-
159
- keep = false if keep && opt[:only_timed] && !item.interval
131
+ keep = false unless search_match
132
+ keep = opt[:not] ? !keep : keep
133
+ end
160
134
 
161
- if keep && opt[:tag_filter]
162
- keep = item.tags?(opt[:tag_filter]['tags'], opt[:tag_filter]['bool'])
163
- keep = opt[:not] ? !keep : keep
164
- end
135
+ if keep && opt[:date_filter]&.length == 2
136
+ start_date = opt[:date_filter][0]
137
+ end_date = opt[:date_filter][1]
138
+
139
+ in_date_range = if end_date
140
+ item.date >= start_date && item.date <= end_date
141
+ else
142
+ item.date.strftime('%F') == start_date.strftime('%F')
143
+ end
144
+ keep = false unless in_date_range
145
+ keep = opt[:not] ? !keep : keep
146
+ end
165
147
 
166
- if keep && opt[:before]
167
- before = opt[:before]
168
- cutoff = if before =~ time_rx
169
- "#{item.date.strftime('%Y-%m-%d')} #{before}".chronify(guess: :begin)
170
- elsif before.is_a?(String)
171
- before.chronify(guess: :begin)
172
- else
173
- before
174
- end
175
- keep = cutoff && item.date <= cutoff
176
- keep = opt[:not] ? !keep : keep
177
- end
148
+ if keep && opt[:time_filter][0] || opt[:time_filter][1]
149
+ start_string = if opt[:time_filter][0].nil?
150
+ "#{item.date.strftime('%Y-%m-%d')} 12am"
151
+ else
152
+ "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][0]}"
153
+ end
154
+ start_time = start_string.chronify(guess: :begin)
155
+
156
+ end_string = if opt[:time_filter][1].nil?
157
+ "#{item.date.to_datetime.next_day.strftime('%Y-%m-%d')} 12am"
158
+ else
159
+ "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][1]}"
160
+ end
161
+ end_time = end_string.chronify(guess: :end)
162
+
163
+ in_time_range = item.date >= start_time && item.date <= end_time
164
+ keep = false unless in_time_range
165
+ keep = opt[:not] ? !keep : keep
166
+ end
178
167
 
179
- if keep && opt[:after]
180
- after = opt[:after]
181
- cutoff = if after =~ time_rx
182
- "#{item.date.strftime('%Y-%m-%d')} #{after}".chronify(guess: :end)
183
- elsif after.is_a?(String)
184
- after.chronify(guess: :end)
185
- else
186
- after
187
- end
188
- keep = cutoff && item.date >= cutoff
189
- keep = opt[:not] ? !keep : keep
190
- end
168
+ keep = false if keep && opt[:only_timed] && !item.interval
191
169
 
192
- if keep && opt[:today]
193
- keep = item.date >= Date.today.to_time && item.date < Date.today.next_day.to_time
194
- keep = opt[:not] ? !keep : keep
195
- elsif keep && opt[:yesterday]
196
- keep = item.date >= Date.today.prev_day.to_time && item.date < Date.today.to_time
197
- keep = opt[:not] ? !keep : keep
198
- end
170
+ if keep && opt[:tag_filter]
171
+ keep = item.tags?(opt[:tag_filter]['tags'], opt[:tag_filter]['bool'])
172
+ keep = opt[:not] ? !keep : keep
173
+ end
199
174
 
200
- keep
175
+ if keep && opt[:before]
176
+ before = opt[:before]
177
+ cutoff = if before =~ time_rx
178
+ "#{item.date.strftime('%Y-%m-%d')} #{before}".chronify(guess: :begin)
179
+ elsif before.is_a?(String)
180
+ before.chronify(guess: :begin)
181
+ else
182
+ before
183
+ end
184
+ keep = cutoff && item.date <= cutoff
185
+ keep = opt[:not] ? !keep : keep
201
186
  end
202
- count = opt[:count].to_i&.positive? ? opt[:count].to_i : filtered_items.count
203
187
 
204
- output = Items.new
188
+ if keep && opt[:after]
189
+ after = opt[:after]
190
+ cutoff = if after =~ time_rx
191
+ "#{item.date.strftime('%Y-%m-%d')} #{after}".chronify(guess: :end)
192
+ elsif after.is_a?(String)
193
+ after.chronify(guess: :end)
194
+ else
195
+ after
196
+ end
197
+ keep = cutoff && item.date >= cutoff
198
+ keep = opt[:not] ? !keep : keep
199
+ end
205
200
 
206
- if opt[:age] && opt[:age].normalize_age == :oldest
207
- output.concat(filtered_items.slice(0, count).reverse)
208
- else
209
- output.concat(filtered_items.reverse.slice(0, count))
201
+ if keep && opt[:today]
202
+ keep = item.date >= Date.today.to_time && item.date < Date.today.next_day.to_time
203
+ keep = opt[:not] ? !keep : keep
204
+ elsif keep && opt[:yesterday]
205
+ keep = item.date >= Date.today.prev_day.to_time && item.date < Date.today.to_time
206
+ keep = opt[:not] ? !keep : keep
210
207
  end
211
208
 
212
- logger.benchmark(:filter_items, :finish)
209
+ keep
210
+ end
211
+ count = opt[:count].to_i&.positive? ? opt[:count].to_i : filtered_items.count
212
+
213
+ output = Items.new
213
214
 
214
- output
215
+ if opt[:age] && opt[:age].normalize_age == :oldest
216
+ output.concat(filtered_items.slice(0, count).reverse)
217
+ else
218
+ output.concat(filtered_items.reverse.slice(0, count))
215
219
  end
220
+
221
+ logger.benchmark(:filter_items, :finish)
222
+
223
+ output
216
224
  end
217
225
  end
218
226
  end
@@ -2,86 +2,84 @@
2
2
 
3
3
  module Doing
4
4
  class WWID
5
- # Section and view guessing methods for WWID class
6
- module Guess
7
- ##
8
- ## Attempt to match a string with an existing section
9
- ##
10
- ## @param frag [String] The user-provided string
11
- ## @param guessed [Boolean] already guessed and failed
12
- ##
13
- def guess_section(frag, guessed: false, suggest: false)
14
- return 'All' if frag =~ /^all$/i
15
- frag ||= Doing.setting('current_section')
5
+ ##
6
+ ## Attempt to match a string with an existing section
7
+ ##
8
+ ## @param frag [String] The user-provided string
9
+ ## @param guessed [Boolean] already guessed and failed
10
+ ##
11
+ def guess_section(frag, guessed: false, suggest: false)
12
+ return 'All' if frag =~ /^all$/i
16
13
 
17
- return frag.cap_first if @content.section?(frag)
14
+ frag ||= Doing.setting('current_section')
18
15
 
19
- found = @content.guess_section(frag, distance: 2)
16
+ return frag.cap_first if @content.section?(frag)
20
17
 
21
- section = found ? found.title : nil
18
+ found = @content.guess_section(frag, distance: 2)
22
19
 
23
- return section if suggest
20
+ section = found ? found.title : nil
24
21
 
25
- unless section || guessed
26
- alt = guess_view(frag, guessed: true, suggest: true)
27
- if alt
28
- prompt = Color.template("{bw}Did you mean `{xy}doing {by}view {xy}#{alt}`{bw}?{x}")
29
- meant_view = Prompt.yn(prompt, default_response: 'n')
22
+ return section if suggest
30
23
 
31
- msg = format('%<y>srun with `%<w>sdoing view %<alt>s%<y>s`', w: boldwhite, y: yellow, alt: alt)
32
- raise Errors::WrongCommand.new(msg, topic: 'Try again:') if meant_view
24
+ unless section || guessed
25
+ alt = guess_view(frag, guessed: true, suggest: true)
26
+ if alt
27
+ prompt = Color.template("{bw}Did you mean `{xy}doing {by}view {xy}#{alt}`{bw}?{x}")
28
+ meant_view = Prompt.yn(prompt, default_response: 'n')
33
29
 
34
- end
30
+ msg = format('%<y>srun with `%<w>sdoing view %<alt>s%<y>s`', w: boldwhite, y: yellow, alt: alt)
31
+ raise Errors::WrongCommand.new(msg, topic: 'Try again:') if meant_view
35
32
 
36
- res = Prompt.yn("#{boldwhite}Section #{frag.yellow}#{boldwhite} not found, create it", default_response: 'n')
33
+ end
37
34
 
38
- if res
39
- @content.add_section(frag.cap_first, log: true)
40
- write(@doing_file)
41
- return frag.cap_first
42
- end
35
+ res = Prompt.yn("#{boldwhite}Section #{frag.yellow}#{boldwhite} not found, create it", default_response: 'n')
43
36
 
44
- raise Errors::InvalidSection.new("unknown section #{frag.bold.white}", topic: 'Missing:')
37
+ if res
38
+ @content.add_section(frag.cap_first, log: true)
39
+ write(@doing_file)
40
+ return frag.cap_first
45
41
  end
46
- section ? section.cap_first : guessed
42
+
43
+ raise Errors::InvalidSection.new("unknown section #{frag.bold.white}", topic: 'Missing:')
47
44
  end
45
+ section ? section.cap_first : guessed
46
+ end
48
47
 
49
- ##
50
- ## Attempt to match a string with an existing view
51
- ##
52
- ## @param frag [String] The user-provided string
53
- ## @param guessed [Boolean] already guessed
54
- ##
55
- def guess_view(frag, guessed: false, suggest: false)
56
- views.each { |view| return view if frag.downcase == view.downcase }
57
- view = false
58
- re = frag.to_rx(distance: 2, case_type: :ignore)
59
- views.each do |v|
60
- next unless v =~ /#{re}/i
61
-
62
- logger.debug('Match:', %(Assuming "#{v}" from "#{frag}"))
63
- view = v
64
- break
65
- end
66
- unless view || guessed
67
- alt = guess_section(frag, guessed: true, suggest: true)
48
+ ##
49
+ ## Attempt to match a string with an existing view
50
+ ##
51
+ ## @param frag [String] The user-provided string
52
+ ## @param guessed [Boolean] already guessed
53
+ ##
54
+ def guess_view(frag, guessed: false, suggest: false)
55
+ views.each { |view| return view if frag.downcase == view.downcase }
56
+ view = nil
57
+ re = frag.to_rx(distance: 2, case_type: :ignore)
58
+ views.each do |v|
59
+ next unless v =~ /#{re}/i
60
+
61
+ logger.debug('Match:', %(Assuming "#{v}" from "#{frag}"))
62
+ view = v
63
+ break
64
+ end
65
+ unless view || guessed
66
+ alt = guess_section(frag, guessed: true, suggest: true)
68
67
 
69
- raise Errors::InvalidView.new(%(unknown view #{frag.bold.white}), topic: 'Missing:') unless alt
68
+ raise Errors::InvalidView.new(%(unknown view #{frag.bold.white}), topic: 'Missing:') unless alt
70
69
 
71
- prompt = Color.template("{bw}Did you mean `{xy}doing {by}show {xy}#{alt}`{bw}?{x}")
72
- meant_view = Prompt.yn(prompt, default_response: 'n')
70
+ prompt = Color.template("{bw}Did you mean `{xy}doing {by}show {xy}#{alt}`{bw}?{x}")
71
+ meant_view = Prompt.yn(prompt, default_response: 'n')
73
72
 
74
- if meant_view
75
- msg = format('%<y>srun with `%<w>sdoing show %<alt>s%<y>s`', w: boldwhite, y: yellow, alt: alt)
76
- raise Errors::WrongCommand.new(msg, topic: 'Try again:')
73
+ if meant_view
74
+ msg = format('%<y>srun with `%<w>sdoing show %<alt>s%<y>s`', w: boldwhite, y: yellow, alt: alt)
75
+ raise Errors::WrongCommand.new(msg, topic: 'Try again:')
77
76
 
78
- end
77
+ end
79
78
 
80
- raise Errors::InvalidView.new(%(unknown view #{alt.bold.white}), topic: 'Missing:')
79
+ raise Errors::InvalidView.new(%(unknown view #{alt.bold.white}), topic: 'Missing:')
81
80
 
82
- end
83
- view
84
81
  end
82
+ view
85
83
  end
86
84
  end
87
85
  end