doing 2.1.23 → 2.1.27
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardoc/checksums +17 -21
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +329 -102
- data/Dockerfile +5 -5
- data/Dockerfile-2.6 +5 -5
- data/Dockerfile-2.7 +5 -4
- data/Dockerfile-3.0 +5 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +3 -3
- data/bin/commands/add_section.rb +15 -0
- data/bin/commands/again.rb +57 -0
- data/bin/commands/archive.rb +55 -0
- data/bin/commands/cancel.rb +60 -0
- data/bin/commands/changes.rb +69 -0
- data/bin/commands/choose.rb +9 -0
- data/bin/commands/colors.rb +21 -0
- data/bin/commands/commands.rb +89 -0
- data/bin/commands/commands_accepting.rb +76 -0
- data/bin/commands/completion.rb +27 -0
- data/bin/commands/config.rb +245 -0
- data/bin/commands/done.rb +235 -0
- data/bin/commands/finish.rb +126 -0
- data/bin/commands/flag.rb +90 -0
- data/bin/commands/grep.rb +108 -0
- data/bin/commands/import.rb +71 -0
- data/bin/commands/install_fzf.rb +17 -0
- data/bin/commands/last.rb +81 -0
- data/bin/commands/meanwhile.rb +76 -0
- data/bin/commands/note.rb +91 -0
- data/bin/commands/now.rb +145 -0
- data/bin/commands/on.rb +65 -0
- data/bin/commands/open.rb +53 -0
- data/bin/commands/plugins.rb +23 -0
- data/bin/commands/recent.rb +77 -0
- data/bin/commands/redo.rb +26 -0
- data/bin/commands/reset.rb +73 -0
- data/bin/commands/rotate.rb +42 -0
- data/bin/commands/sections.rb +11 -0
- data/bin/commands/select.rb +105 -0
- data/bin/commands/show.rb +185 -0
- data/bin/commands/since.rb +63 -0
- data/bin/commands/tag.rb +149 -0
- data/bin/commands/tag_dir.rb +29 -0
- data/bin/commands/tags.rb +66 -0
- data/bin/commands/template.rb +61 -0
- data/bin/commands/today.rb +64 -0
- data/bin/commands/undo.rb +49 -0
- data/bin/commands/view.rb +201 -0
- data/bin/commands/views.rb +11 -0
- data/bin/commands/yesterday.rb +72 -0
- data/bin/doing +241 -3662
- data/docs/doc/Array.html +13 -449
- data/docs/doc/BooleanTermParser/Clause.html +5 -5
- data/docs/doc/BooleanTermParser/Operator.html +4 -4
- data/docs/doc/BooleanTermParser/Query.html +8 -8
- data/docs/doc/BooleanTermParser/QueryParser.html +2 -2
- data/docs/doc/BooleanTermParser/QueryTransformer.html +2 -2
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/Color.html +65 -59
- data/docs/doc/Doing/Completion.html +2 -2
- data/docs/doc/Doing/Configuration.html +49 -16
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +2 -2
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +2 -2
- data/docs/doc/Doing/Errors/DoingStandardError.html +2 -2
- data/docs/doc/Doing/Errors/EmptyInput.html +2 -2
- data/docs/doc/Doing/Errors/NoResults.html +2 -2
- data/docs/doc/Doing/Errors/PluginException.html +3 -3
- data/docs/doc/Doing/Errors/UserCancelled.html +2 -2
- data/docs/doc/Doing/Errors/WrongCommand.html +2 -2
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/Hooks.html +6 -6
- data/docs/doc/Doing/Item.html +50 -16
- data/docs/doc/Doing/Items.html +10 -10
- data/docs/doc/Doing/LogAdapter.html +24 -24
- data/docs/doc/Doing/Note.html +7 -7
- data/docs/doc/Doing/Pager.html +4 -4
- data/docs/doc/Doing/Plugins.html +7 -7
- data/docs/doc/Doing/Prompt.html +59 -14
- data/docs/doc/Doing/Section.html +6 -6
- data/docs/doc/Doing/TemplateString.html +8 -8
- data/docs/doc/Doing/Types.html +46 -1
- data/docs/doc/Doing/Util/Backup.html +10 -10
- data/docs/doc/Doing/Util.html +15 -15
- data/docs/doc/Doing/WWID.html +73 -61
- data/docs/doc/Doing.html +3 -3
- data/docs/doc/FalseClass.html +235 -0
- data/docs/doc/GLI/Commands/Help.html +3 -3
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +17 -17
- data/docs/doc/GLI/Commands.html +1 -1
- data/docs/doc/GLI.html +1 -1
- data/docs/doc/Hash.html +45 -11
- data/docs/doc/Numeric.html +5 -5
- data/docs/doc/Object.html +203 -0
- data/docs/doc/PhraseParser/Operator.html +4 -4
- data/docs/doc/PhraseParser/PhraseClause.html +5 -5
- data/docs/doc/PhraseParser/Query.html +10 -10
- data/docs/doc/PhraseParser/QueryParser.html +2 -2
- data/docs/doc/PhraseParser/QueryTransformer.html +2 -2
- data/docs/doc/PhraseParser/TermClause.html +5 -5
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +7 -7
- data/docs/doc/String.html +306 -3111
- data/docs/doc/Symbol.html +45 -11
- data/docs/doc/Time.html +6 -6
- data/docs/doc/TrueClass.html +235 -0
- data/docs/doc/_index.html +37 -19
- data/docs/doc/class_list.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +240 -576
- data/docs/doc/top-level-namespace.html +2 -2
- data/doing.rdoc +289 -169
- data/example_plugin.rb +2 -2
- data/lib/completion/_doing.zsh +35 -31
- data/lib/completion/doing.bash +30 -19
- data/lib/completion/doing.fish +81 -67
- data/lib/doing/array/array.rb +4 -0
- data/lib/doing/array/nested_hash.rb +17 -0
- data/lib/doing/{array.rb → array/tags.rb} +7 -25
- data/lib/doing/changelog/change.rb +26 -11
- data/lib/doing/changelog/changes.rb +14 -4
- data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
- data/lib/doing/chronify/chronify.rb +5 -0
- data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
- data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
- data/lib/doing/colors.rb +115 -54
- data/lib/doing/completion/fish_completion.rb +2 -1
- data/lib/doing/configuration.rb +9 -6
- data/lib/doing/good.rb +72 -0
- data/lib/doing/hash.rb +4 -0
- data/lib/doing/help_monkey_patch.rb +6 -5
- data/lib/doing/hooks.rb +3 -3
- data/lib/doing/item.rb +19 -15
- data/lib/doing/items.rb +2 -2
- data/lib/doing/log_adapter.rb +35 -2
- data/lib/doing/normalize.rb +188 -0
- data/lib/doing/pager.rb +1 -0
- data/lib/doing/plugins/export/dayone_export.rb +1 -1
- data/lib/doing/plugins/export/html_export.rb +1 -1
- data/lib/doing/plugins/export/json_export.rb +1 -1
- data/lib/doing/plugins/export/markdown_export.rb +1 -1
- data/lib/doing/plugins/export/template_export.rb +3 -1
- data/lib/doing/plugins/import/calendar_import.rb +1 -1
- data/lib/doing/plugins/import/doing_import.rb +1 -1
- data/lib/doing/plugins/import/timing_import.rb +1 -1
- data/lib/doing/prompt.rb +9 -3
- data/lib/doing/string/highlight.rb +95 -0
- data/lib/doing/string/query.rb +129 -0
- data/lib/doing/string/string.rb +12 -0
- data/lib/doing/string/tags.rb +164 -0
- data/lib/doing/string/transform.rb +168 -0
- data/lib/doing/string/truncate.rb +75 -0
- data/lib/doing/string/url.rb +82 -0
- data/lib/doing/template_string.rb +2 -24
- data/lib/doing/types.rb +9 -0
- data/lib/doing/util.rb +20 -16
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +91 -51
- data/lib/doing.rb +5 -6
- data/lib/examples/commands/wiki.rb +6 -7
- data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
- data/lib/helpers/threaded_tests.rb +69 -79
- data/lib/helpers/threaded_tests_string.rb +50 -0
- data/scripts/deploy.rb +107 -0
- data/scripts/runtests.sh +4 -0
- metadata +65 -8
- data/lib/doing/string.rb +0 -765
- data/lib/doing/symbol.rb +0 -28
@@ -0,0 +1,188 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doing
|
4
|
+
##
|
5
|
+
## String to symbol conversion
|
6
|
+
##
|
7
|
+
class ::String
|
8
|
+
##
|
9
|
+
## Convert tag sort string to a qualified type
|
10
|
+
##
|
11
|
+
## @return [Symbol] :name or :time
|
12
|
+
##
|
13
|
+
def normalize_tag_sort(default = :name)
|
14
|
+
case self
|
15
|
+
when /^n/i
|
16
|
+
:name
|
17
|
+
when /^t/i
|
18
|
+
:time
|
19
|
+
else
|
20
|
+
default
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
## @see #normalize_tag_sort
|
25
|
+
def normalize_tag_sort!(default = :name)
|
26
|
+
replace normalize_tag_sort(default)
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
## Convert an age string to a qualified type
|
31
|
+
##
|
32
|
+
## @return [Symbol] :oldest or :newest
|
33
|
+
##
|
34
|
+
def normalize_age(default = :newest)
|
35
|
+
case self
|
36
|
+
when /^o/i
|
37
|
+
:oldest
|
38
|
+
when /^n/i
|
39
|
+
:newest
|
40
|
+
else
|
41
|
+
default
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
## @see #normalize_age
|
46
|
+
def normalize_age!(default = :newest)
|
47
|
+
replace normalize_age(default)
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
## Convert a sort order string to a qualified type
|
52
|
+
##
|
53
|
+
## @return [Symbol] :asc or :desc
|
54
|
+
##
|
55
|
+
def normalize_order!(default = :asc)
|
56
|
+
replace normalize_order(default)
|
57
|
+
end
|
58
|
+
|
59
|
+
def normalize_order(default = :asc)
|
60
|
+
case self
|
61
|
+
when /^a/i
|
62
|
+
:asc
|
63
|
+
when /^d/i
|
64
|
+
:desc
|
65
|
+
else
|
66
|
+
default
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
## Convert a case sensitivity string to a symbol
|
72
|
+
##
|
73
|
+
## @return Symbol :smart, :sensitive, :ignore
|
74
|
+
##
|
75
|
+
def normalize_case(default = :smart)
|
76
|
+
case self
|
77
|
+
when /^(c|sens)/i
|
78
|
+
:sensitive
|
79
|
+
when /^i/i
|
80
|
+
:ignore
|
81
|
+
when /^s/i
|
82
|
+
:smart
|
83
|
+
else
|
84
|
+
default.is_a?(Symbol) ? default : default.normalize_case
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
## @see #normalize_case
|
89
|
+
def normalize_case!(default = :smart)
|
90
|
+
replace normalize_case(default)
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
## Convert a boolean string to a symbol
|
95
|
+
##
|
96
|
+
## @return Symbol :and, :or, or :not
|
97
|
+
##
|
98
|
+
def normalize_bool(default = :and)
|
99
|
+
case self
|
100
|
+
when /(and|all)/i
|
101
|
+
:and
|
102
|
+
when /(any|or)/i
|
103
|
+
:or
|
104
|
+
when /(not|none)/i
|
105
|
+
:not
|
106
|
+
when /^p/i
|
107
|
+
:pattern
|
108
|
+
else
|
109
|
+
default.is_a?(Symbol) ? default : default.normalize_bool
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
## @see #normalize_bool
|
114
|
+
def normalize_bool!(default = :and)
|
115
|
+
replace normalize_bool(default)
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
## Convert a matching configuration string to a symbol
|
120
|
+
##
|
121
|
+
## @param default [Symbol] the default matching
|
122
|
+
## type to return if the string
|
123
|
+
## doesn't match a known symbol
|
124
|
+
## @return Symbol :fuzzy, :pattern, :exact
|
125
|
+
##
|
126
|
+
def normalize_matching(default = :pattern)
|
127
|
+
case self
|
128
|
+
when /^f/i
|
129
|
+
:fuzzy
|
130
|
+
when /^p/i
|
131
|
+
:pattern
|
132
|
+
when /^e/i
|
133
|
+
:exact
|
134
|
+
else
|
135
|
+
default.is_a?(Symbol) ? default : default.normalize_matching
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
## @see #normalize_matching
|
140
|
+
def normalize_matching!(default = :pattern)
|
141
|
+
replace normalize_bool(default)
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
## Adds ?: to any parentheticals in a regular expression
|
146
|
+
## to avoid match groups
|
147
|
+
##
|
148
|
+
## @return [String] modified regular expression
|
149
|
+
##
|
150
|
+
def normalize_trigger
|
151
|
+
gsub(/\((?!\?:)/, '(?:').downcase
|
152
|
+
end
|
153
|
+
|
154
|
+
## @see #normalize_trigger
|
155
|
+
def normalize_trigger!
|
156
|
+
replace normalize_trigger
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
## Symbol helpers
|
162
|
+
##
|
163
|
+
class ::Symbol
|
164
|
+
def normalize_tag_sort(default = :name)
|
165
|
+
to_s.normalize_tag_sort
|
166
|
+
end
|
167
|
+
|
168
|
+
def normalize_bool(default = :and)
|
169
|
+
to_s.normalize_bool(default)
|
170
|
+
end
|
171
|
+
|
172
|
+
def normalize_age(default = :newest)
|
173
|
+
to_s.normalize_age(default)
|
174
|
+
end
|
175
|
+
|
176
|
+
def normalize_order(default = :asc)
|
177
|
+
to_s.normalize_order(default)
|
178
|
+
end
|
179
|
+
|
180
|
+
def normalize_case(default = :smart)
|
181
|
+
to_s.normalize_case(default)
|
182
|
+
end
|
183
|
+
|
184
|
+
def normalize_matching(default = :pattern)
|
185
|
+
to_s.normalize_matching(default)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/lib/doing/pager.rb
CHANGED
@@ -129,7 +129,7 @@ module Doing
|
|
129
129
|
self.template('dayone')
|
130
130
|
end
|
131
131
|
|
132
|
-
totals = opt[:totals] ? wwid.tag_times(format: :markdown,
|
132
|
+
totals = opt[:totals] ? wwid.tag_times(format: :markdown, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
133
133
|
|
134
134
|
case digest
|
135
135
|
when :day
|
@@ -70,7 +70,7 @@ module Doing
|
|
70
70
|
self.template('css')
|
71
71
|
end
|
72
72
|
|
73
|
-
totals = opt[:totals] ? wwid.tag_times(format: :html,
|
73
|
+
totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
74
74
|
engine = Haml::Engine.new(template)
|
75
75
|
Doing.logger.debug('HTML Export:', "#{items_out.count} items output to HTML")
|
76
76
|
@out = engine.render(Object.new,
|
@@ -91,7 +91,7 @@ module Doing
|
|
91
91
|
JSON.pretty_generate({
|
92
92
|
'section' => variables[:page_title],
|
93
93
|
'items' => items_out,
|
94
|
-
'timers' => wwid.tag_times(format: :json,
|
94
|
+
'timers' => wwid.tag_times(format: :json, sort_by: opt[:sort_tags], sort_order: opt[:tag_order])
|
95
95
|
})
|
96
96
|
elsif opt[:output] == 'timeline'
|
97
97
|
template = <<~EOTEMPLATE
|
@@ -72,7 +72,7 @@ module Doing
|
|
72
72
|
self.template(nil)
|
73
73
|
end
|
74
74
|
|
75
|
-
totals = opt[:totals] ? wwid.tag_times(format: :markdown,
|
75
|
+
totals = opt[:totals] ? wwid.tag_times(format: :markdown, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
76
76
|
|
77
77
|
mdx = MarkdownRenderer.new(variables[:page_title], all_items, totals)
|
78
78
|
Doing.logger.debug('Markdown Export:', "#{all_items.count} items output to Markdown")
|
@@ -17,6 +17,7 @@ module Doing
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.render(wwid, items, variables: {})
|
20
|
+
# Doing.logger.benchmark(:template_render, :start)
|
20
21
|
return if items.nil?
|
21
22
|
|
22
23
|
opt = variables[:options]
|
@@ -133,9 +134,10 @@ module Doing
|
|
133
134
|
# Doing.logger.debug('Template Export:', "#{items.count} items output to template #{opt[:template]}")
|
134
135
|
if opt[:totals]
|
135
136
|
out += wwid.tag_times(format: wwid.config['timer_format'].to_sym,
|
136
|
-
|
137
|
+
sort_by: opt[:sort_tags],
|
137
138
|
sort_order: opt[:tag_order])
|
138
139
|
end
|
140
|
+
# Doing.logger.benchmark(:template_render, :finish)
|
139
141
|
out
|
140
142
|
end
|
141
143
|
|
@@ -89,7 +89,7 @@ module Doing
|
|
89
89
|
|
90
90
|
wwid.content.concat(new_items)
|
91
91
|
|
92
|
-
new_items.map { |item| Hooks.trigger :post_entry_added, self, item
|
92
|
+
new_items.map { |item| Hooks.trigger :post_entry_added, self, item }
|
93
93
|
|
94
94
|
Doing.logger.info(%(Imported #{new_items.count} items to #{section}))
|
95
95
|
end
|
@@ -93,7 +93,7 @@ module Doing
|
|
93
93
|
wwid.content.add_section(item.section) unless wwid.content.section?(item.section)
|
94
94
|
Hooks.trigger :pre_entry_add, self, item
|
95
95
|
wwid.content.push(item)
|
96
|
-
Hooks.trigger :post_entry_added, self, item
|
96
|
+
Hooks.trigger :post_entry_added, self, item
|
97
97
|
end
|
98
98
|
|
99
99
|
Doing.logger.info('Imported:', "#{imported.count} items")
|
@@ -93,7 +93,7 @@ module Doing
|
|
93
93
|
|
94
94
|
wwid.content.concat(new_items)
|
95
95
|
|
96
|
-
new_items.map { |item| Hooks.trigger :post_entry_added, self, item
|
96
|
+
new_items.map { |item| Hooks.trigger :post_entry_added, self, item }
|
97
97
|
|
98
98
|
Doing.logger.info('Imported:', %(#{new_items.count} items to #{section}))
|
99
99
|
end
|
data/lib/doing/prompt.rb
CHANGED
@@ -8,6 +8,14 @@ module Doing
|
|
8
8
|
|
9
9
|
include Color
|
10
10
|
|
11
|
+
##
|
12
|
+
## Clear the terminal screen
|
13
|
+
##
|
14
|
+
def clear_screen(msg = nil)
|
15
|
+
puts "\e[H\e[2J" if STDOUT.tty?
|
16
|
+
puts msg if msg.good?
|
17
|
+
end
|
18
|
+
|
11
19
|
def force_answer
|
12
20
|
@force_answer ||= nil
|
13
21
|
end
|
@@ -99,9 +107,7 @@ module Doing
|
|
99
107
|
## @return (Bool) yes or no
|
100
108
|
##
|
101
109
|
def yn(question, default_response: false)
|
102
|
-
unless @force_answer.nil?
|
103
|
-
return @force_answer
|
104
|
-
end
|
110
|
+
return @force_answer == :yes ? true : false unless @force_answer.nil?
|
105
111
|
|
106
112
|
$stdin.reopen('/dev/tty')
|
107
113
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doing
|
4
|
+
## Tag and search highlighting
|
5
|
+
class ::String
|
6
|
+
## @param (see #highlight_tags)
|
7
|
+
def highlight_tags!(color = 'yellow', last_color: nil)
|
8
|
+
replace highlight_tags(color)
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
## Colorize @tags with ANSI escapes
|
13
|
+
##
|
14
|
+
## @param color [String] color (see #Color)
|
15
|
+
##
|
16
|
+
## @return [String] string with @tags highlighted
|
17
|
+
##
|
18
|
+
def highlight_tags(color = 'yellow', last_color: nil)
|
19
|
+
unless last_color
|
20
|
+
escapes = scan(/(\e\[[\d;]+m)[^\e]+@/)
|
21
|
+
color = color.split(' ') unless color.is_a?(Array)
|
22
|
+
tag_color = color.each_with_object([]) { |c, arr| arr << Doing::Color.send(c) }.join('')
|
23
|
+
last_color = if escapes.good?
|
24
|
+
(escapes.count > 1 ? escapes[-2..-1] : [escapes[-1]]).map { |v| v[0] }.join('')
|
25
|
+
else
|
26
|
+
Doing::Color.default
|
27
|
+
end
|
28
|
+
end
|
29
|
+
gsub(/(\s|m)(@[^ ("']+)/, "\\1#{tag_color}\\2#{last_color}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def highlight_search!(search, distance: nil, negate: false, case_type: nil)
|
33
|
+
replace highlight_search(search, distance: distance, negate: negate, case_type: case_type)
|
34
|
+
end
|
35
|
+
|
36
|
+
def highlight_search(search, distance: nil, negate: false, case_type: nil)
|
37
|
+
out = dup
|
38
|
+
prefs = Doing.config.settings['search'] || {}
|
39
|
+
matching = prefs.fetch('matching', 'pattern').normalize_matching
|
40
|
+
distance ||= prefs.fetch('distance', 3).to_i
|
41
|
+
case_type ||= prefs.fetch('case', 'smart').normalize_case
|
42
|
+
|
43
|
+
if search.rx? || matching == :fuzzy
|
44
|
+
rx = search.to_rx(distance: distance, case_type: case_type)
|
45
|
+
out.gsub!(rx) { |m| m.bgyellow.black }
|
46
|
+
else
|
47
|
+
query = search.strip.to_phrase_query
|
48
|
+
|
49
|
+
if query[:must].nil? && query[:must_not].nil?
|
50
|
+
query[:must] = query[:should]
|
51
|
+
query[:should] = []
|
52
|
+
end
|
53
|
+
qs = []
|
54
|
+
qs.concat(query[:must]) if query[:must]
|
55
|
+
qs.concat(query[:should]) if query[:should]
|
56
|
+
qs.each do |s|
|
57
|
+
rx = Regexp.new(s.wildcard_to_rx, ignore_case(s, case_type))
|
58
|
+
out.gsub!(rx) { |m| m.bgyellow.black }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
out
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the last escape sequence from a string.
|
65
|
+
#
|
66
|
+
# Actually returns all escape codes, with the assumption
|
67
|
+
# that the result of inserting them will generate the
|
68
|
+
# same color as was set at the end of the string.
|
69
|
+
# Because you can send modifiers like dark and bold
|
70
|
+
# separate from color codes, only using the last code
|
71
|
+
# may not render the same style.
|
72
|
+
#
|
73
|
+
# @return [String] All escape codes in string
|
74
|
+
#
|
75
|
+
def last_color
|
76
|
+
scan(/\e\[[\d;]+m/).join('')
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
## Remove color escape codes
|
81
|
+
##
|
82
|
+
## @return clean string
|
83
|
+
##
|
84
|
+
def uncolor
|
85
|
+
gsub(/\e\[[\d;]+m/, '')
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
## @see #uncolor
|
90
|
+
##
|
91
|
+
def uncolor!
|
92
|
+
replace uncolor
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doing
|
4
|
+
## Handling of search and regex strings
|
5
|
+
class ::String
|
6
|
+
##
|
7
|
+
## Determine whether case should be ignored for string
|
8
|
+
##
|
9
|
+
## @param search The search string
|
10
|
+
## @param case_type The case type, :smart,
|
11
|
+
## :sensitive, :ignore
|
12
|
+
##
|
13
|
+
## @return [Boolean] true if case should be ignored
|
14
|
+
##
|
15
|
+
def ignore_case(search, case_type)
|
16
|
+
(case_type == :smart && search !~ /[A-Z]/) || case_type == :ignore
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
## Test if line should be ignored
|
21
|
+
##
|
22
|
+
## @return [Boolean] line is empty or comment
|
23
|
+
##
|
24
|
+
def ignore?
|
25
|
+
line = self
|
26
|
+
line =~ /^#/ || line =~ /^\s*$/
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
## Determines if receiver is surrounded by slashes or starts with single quote
|
31
|
+
##
|
32
|
+
## @return [Boolean] True if regex, False otherwise.
|
33
|
+
##
|
34
|
+
def rx?
|
35
|
+
self =~ %r{(^/.*?/$|^')}
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
## Convert ? and * wildcards to regular expressions.
|
40
|
+
## Uses \S (non-whitespace) instead of . (any character)
|
41
|
+
##
|
42
|
+
## @return [String] Regular expression string
|
43
|
+
##
|
44
|
+
def wildcard_to_rx
|
45
|
+
gsub(/\?/, '\S').gsub(/\*/, '\S*?').gsub(/\]\]/, '--')
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
## Convert string to fuzzy regex. Characters in words
|
50
|
+
## can be separated by up to *distance* characters in
|
51
|
+
## haystack, spaces indicate unlimited distance.
|
52
|
+
##
|
53
|
+
## @example
|
54
|
+
## "this word".to_rx(3)
|
55
|
+
## # => /t.{0,3}h.{0,3}i.{0,3}s.{0,3}.*?w.{0,3}o.{0,3}r.{0,3}d/
|
56
|
+
##
|
57
|
+
## @param distance [Integer] Allowed distance
|
58
|
+
## between characters
|
59
|
+
## @param case_type The case type
|
60
|
+
##
|
61
|
+
## @return [Regexp] Regex pattern
|
62
|
+
##
|
63
|
+
def to_rx(distance: nil, case_type: nil)
|
64
|
+
distance ||= Doing.config.fetch('search', 'distance', 3).to_i
|
65
|
+
case_type ||= Doing.config.fetch('search', 'case', 'smart')&.normalize_case
|
66
|
+
case_sensitive = case case_type
|
67
|
+
when :smart
|
68
|
+
self =~ /[A-Z]/ ? true : false
|
69
|
+
when :sensitive
|
70
|
+
true
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
74
|
+
|
75
|
+
pattern = case dup.strip
|
76
|
+
when %r{^/.*?/$}
|
77
|
+
sub(%r{/(.*?)/}, '\1')
|
78
|
+
when /^'/
|
79
|
+
sub(/^'(.*?)'?$/, '\1')
|
80
|
+
else
|
81
|
+
split(/ +/).map do |w|
|
82
|
+
w.split('').join(".{0,#{distance}}").gsub(/\+/, '\+').wildcard_to_rx
|
83
|
+
end.join('.*?')
|
84
|
+
end
|
85
|
+
Regexp.new(pattern, !case_sensitive)
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_phrase_query
|
89
|
+
parser = PhraseParser::QueryParser.new
|
90
|
+
transformer = PhraseParser::QueryTransformer.new
|
91
|
+
parse_tree = parser.parse(self)
|
92
|
+
transformer.apply(parse_tree).to_elasticsearch
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_query
|
96
|
+
parser = BooleanTermParser::QueryParser.new
|
97
|
+
transformer = BooleanTermParser::QueryTransformer.new
|
98
|
+
parse_tree = parser.parse(self)
|
99
|
+
transformer.apply(parse_tree).to_elasticsearch
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
## Test string for truthiness (0, "f", "false", "n", "no" all return false, case insensitive, otherwise true)
|
104
|
+
##
|
105
|
+
## @return [Boolean] String is truthy
|
106
|
+
##
|
107
|
+
def truthy?
|
108
|
+
if self =~ /^(0|f(alse)?|n(o)?)$/i
|
109
|
+
false
|
110
|
+
else
|
111
|
+
true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
## Returns a bool representation of the string.
|
117
|
+
##
|
118
|
+
## @return [Boolean] Bool representation of the object.
|
119
|
+
##
|
120
|
+
def to_bool
|
121
|
+
case self
|
122
|
+
when /^[yt1]/i
|
123
|
+
true
|
124
|
+
else
|
125
|
+
false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|