doing 2.1.40 → 2.1.41

Sign up to get free protection for your applications and to get access to all the features.
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