doing 2.1.24 → 2.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardoc/checksums +17 -21
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +325 -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 +73 -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 +297 -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 +16 -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/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
|