doing 2.1.40 → 2.1.41
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +4 -4
- data/bin/commands/changes.rb +1 -1
- data/bin/commands/tag_dir.rb +49 -15
- data/{Dockerfile → docker/Dockerfile} +3 -1
- data/{Dockerfile-2.6 → docker/Dockerfile-2.6} +2 -2
- data/{Dockerfile-2.7 → docker/Dockerfile-2.7} +2 -2
- data/{Dockerfile-3.0 → docker/Dockerfile-3.0} +2 -2
- data/{bash_profile → docker/bash_profile} +0 -0
- data/{inputrc → docker/inputrc} +0 -0
- data/docs/doc/Array.html +84 -2
- 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/ArrayNestedHash.html +198 -0
- data/docs/doc/Doing/ArrayTags.html +424 -0
- data/docs/doc/Doing/CSVExport.html +266 -0
- data/docs/doc/Doing/CalendarImport.html +232 -0
- data/docs/doc/Doing/Change.html +617 -0
- data/docs/doc/Doing/Changes.html +468 -0
- data/docs/doc/Doing/ChronifyArray.html +347 -0
- data/docs/doc/Doing/ChronifyNumeric.html +271 -0
- data/docs/doc/Doing/ChronifyString.html +682 -0
- data/docs/doc/Doing/Color.html +2 -2
- data/docs/doc/Doing/Completion/BashCompletions.html +445 -0
- data/docs/doc/Doing/Completion/FishCompletions.html +445 -0
- data/docs/doc/Doing/Completion/StringUtils.html +229 -0
- data/docs/doc/Doing/Completion/ZshCompletions.html +445 -0
- data/docs/doc/Doing/Completion.html +17 -3
- data/docs/doc/Doing/Configuration.html +1 -1
- data/docs/doc/Doing/DayOneRenderer.html +383 -0
- data/docs/doc/Doing/DayoneExport.html +290 -0
- data/docs/doc/Doing/DoingImport.html +391 -0
- data/docs/doc/Doing/Entry.html +381 -0
- 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/HistoryLimitError.html +1 -1
- data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
- data/docs/doc/Doing/Errors/MissingBackupFile.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/HTMLExport.html +256 -0
- data/docs/doc/Doing/Hooks.html +1 -1
- data/docs/doc/Doing/Item.html +47 -3
- data/docs/doc/Doing/ItemDates.html +564 -0
- data/docs/doc/Doing/ItemQuery.html +614 -0
- data/docs/doc/Doing/ItemState.html +387 -0
- data/docs/doc/Doing/ItemTags.html +498 -0
- data/docs/doc/Doing/Items.html +460 -11
- data/docs/doc/Doing/JSONExport.html +222 -0
- data/docs/doc/Doing/Logger.html +1 -1
- data/docs/doc/Doing/MarkdownExport.html +266 -0
- data/docs/doc/Doing/MarkdownRenderer.html +383 -0
- data/docs/doc/Doing/Note.html +16 -3
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +1 -1
- data/docs/doc/Doing/Prompt.html +31 -682
- data/docs/doc/Doing/PromptChoose.html +484 -0
- data/docs/doc/Doing/PromptFZF.html +391 -0
- data/docs/doc/Doing/PromptInput.html +572 -0
- data/docs/doc/Doing/PromptSTD.html +293 -0
- data/docs/doc/Doing/PromptYN.html +237 -0
- data/docs/doc/Doing/Section.html +58 -2
- data/docs/doc/Doing/StringHighlight.html +533 -0
- data/docs/doc/Doing/StringNormalize.html +929 -0
- data/docs/doc/Doing/StringQuery.html +725 -0
- data/docs/doc/Doing/StringTags.html +884 -0
- data/docs/doc/Doing/StringTransform.html +565 -0
- data/docs/doc/Doing/StringTruncate.html +448 -0
- data/docs/doc/Doing/StringURL.html +409 -0
- data/docs/doc/Doing/SymbolNormalize.html +341 -0
- data/docs/doc/Doing/TaskPaperExport.html +222 -0
- data/docs/doc/Doing/TemplateExport.html +249 -0
- data/docs/doc/Doing/TemplateString.html +101 -2
- data/docs/doc/Doing/TimingImport.html +285 -0
- data/docs/doc/Doing/Types.html +1 -1
- data/docs/doc/Doing/Util/Backup.html +9 -7
- data/docs/doc/Doing/Util.html +2 -2
- data/docs/doc/Doing/Version.html +523 -0
- data/docs/doc/Doing/WWID/WWIDUtil.html +510 -0
- data/docs/doc/Doing/WWID.html +4377 -217
- data/docs/doc/Doing/WWIDDisplay.html +865 -0
- data/docs/doc/Doing/WWIDEditor.html +466 -0
- data/docs/doc/Doing/WWIDFileTools.html +359 -0
- data/docs/doc/Doing/WWIDFilter.html +466 -0
- data/docs/doc/Doing/WWIDGuess.html +299 -0
- data/docs/doc/Doing/WWIDInteractive.html +752 -0
- data/docs/doc/Doing/WWIDModify.html +1078 -0
- data/docs/doc/Doing/WWIDTags.html +302 -0
- data/docs/doc/Doing/WWIDTimers.html +359 -0
- data/docs/doc/Doing/WWIDUtil.html +510 -0
- data/docs/doc/Doing.html +9 -6
- 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/Numeric.html +23 -78
- 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 +58 -633
- data/docs/doc/Symbol.html +9 -224
- data/docs/doc/Time.html +119 -13
- data/docs/doc/TrueClass.html +1 -1
- data/docs/doc/_index.html +324 -8
- data/docs/doc/class_list.html +1 -1
- data/docs/doc/file.README.html +1 -1
- data/docs/doc/index.html +1 -1
- data/docs/doc/method_list.html +2326 -542
- data/docs/doc/top-level-namespace.html +2 -2
- data/doing.rdoc +13 -3
- data/lib/completion/_doing.zsh +1 -1
- data/lib/completion/doing.bash +2 -2
- data/lib/completion/doing.fish +3 -1
- data/lib/doing/array/array.rb +16 -12
- data/lib/doing/array/nested_hash.rb +1 -1
- data/lib/doing/array/tags.rb +6 -5
- data/lib/doing/changelog/changelog.rb +6 -0
- data/lib/doing/chronify/array.rb +1 -3
- data/lib/doing/chronify/chronify.rb +12 -0
- data/lib/doing/chronify/numeric.rb +3 -2
- data/lib/doing/chronify/string.rb +1 -1
- data/lib/doing/completion/completion_string.rb +25 -0
- data/lib/doing/completion.rb +1 -1
- data/lib/doing/good.rb +8 -0
- data/lib/doing/item/dates.rb +1 -1
- data/lib/doing/{item.rb → item/item.rb} +10 -5
- data/lib/doing/item/query.rb +1 -1
- data/lib/doing/item/state.rb +1 -1
- data/lib/doing/item/tags.rb +1 -1
- data/lib/doing/items/filter.rb +67 -0
- data/lib/doing/items/items.rb +57 -0
- data/lib/doing/items/modify.rb +36 -0
- data/lib/doing/items/sections.rb +83 -0
- data/lib/doing/items/util.rb +74 -0
- data/lib/doing/normalize.rb +10 -2
- data/lib/doing/plugins/export/markdown_export.rb +4 -2
- data/lib/doing/plugins/import/doing_import.rb +1 -1
- data/lib/doing/prompt/choose.rb +118 -0
- data/lib/doing/prompt/fzf.rb +84 -0
- data/lib/doing/prompt/input.rb +129 -0
- data/lib/doing/prompt/prompt.rb +41 -0
- data/lib/doing/prompt/std.rb +32 -0
- data/lib/doing/prompt/yn.rb +64 -0
- data/lib/doing/section.rb +4 -0
- data/lib/doing/string/highlight.rb +1 -1
- data/lib/doing/string/query.rb +1 -1
- data/lib/doing/string/string.rb +18 -7
- data/lib/doing/string/tags.rb +14 -3
- data/lib/doing/string/transform.rb +1 -1
- data/lib/doing/string/truncate.rb +1 -1
- data/lib/doing/string/url.rb +1 -1
- data/lib/doing/time.rb +19 -1
- data/lib/doing/util_backup.rb +2 -2
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid/display.rb +357 -360
- data/lib/doing/wwid/editor.rb +173 -176
- data/lib/doing/wwid/filetools.rb +156 -159
- data/lib/doing/wwid/filter.rb +191 -183
- data/lib/doing/wwid/guess.rb +58 -60
- data/lib/doing/wwid/interactive.rb +332 -330
- data/lib/doing/wwid/modify.rb +509 -512
- data/lib/doing/wwid/tags.rb +38 -41
- data/lib/doing/wwid/timers.rb +293 -296
- data/lib/doing/{wwid.rb → wwid/wwid.rb} +32 -23
- data/lib/doing/wwid/wwidutil.rb +79 -82
- data/lib/doing.rb +5 -5
- data/lib/helpers/threaded_tests.rb +1 -0
- metadata +76 -14
- data/lib/doing/changelog.rb +0 -6
- data/lib/doing/completion/string.rb +0 -17
- data/lib/doing/items.rb +0 -221
- data/lib/doing/prompt.rb +0 -330
data/lib/doing/wwid/filter.rb
CHANGED
@@ -2,217 +2,225 @@
|
|
2
2
|
|
3
3
|
module Doing
|
4
4
|
class WWID
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
148
|
-
|
124
|
+
if keep && opt[:search]
|
125
|
+
search_match = if opt[:search].nil? || opt[:search].empty?
|
126
|
+
true
|
149
127
|
else
|
150
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
-
|
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
|
-
|
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
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/doing/wwid/guess.rb
CHANGED
@@ -2,86 +2,84 @@
|
|
2
2
|
|
3
3
|
module Doing
|
4
4
|
class WWID
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
14
|
+
frag ||= Doing.setting('current_section')
|
18
15
|
|
19
|
-
|
16
|
+
return frag.cap_first if @content.section?(frag)
|
20
17
|
|
21
|
-
|
18
|
+
found = @content.guess_section(frag, distance: 2)
|
22
19
|
|
23
|
-
|
20
|
+
section = found ? found.title : nil
|
24
21
|
|
25
|
-
|
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
|
-
|
32
|
-
|
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
|
-
|
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
|
-
|
33
|
+
end
|
37
34
|
|
38
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
68
|
+
raise Errors::InvalidView.new(%(unknown view #{frag.bold.white}), topic: 'Missing:') unless alt
|
70
69
|
|
71
|
-
|
72
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
77
|
+
end
|
79
78
|
|
80
|
-
|
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
|