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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +17 -21
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/CHANGELOG.md +325 -102
  6. data/Dockerfile +5 -5
  7. data/Dockerfile-2.6 +5 -5
  8. data/Dockerfile-2.7 +5 -4
  9. data/Dockerfile-3.0 +5 -4
  10. data/Gemfile.lock +1 -1
  11. data/README.md +1 -1
  12. data/Rakefile +3 -3
  13. data/bin/commands/add_section.rb +15 -0
  14. data/bin/commands/again.rb +57 -0
  15. data/bin/commands/archive.rb +55 -0
  16. data/bin/commands/cancel.rb +60 -0
  17. data/bin/commands/changes.rb +73 -0
  18. data/bin/commands/choose.rb +9 -0
  19. data/bin/commands/colors.rb +21 -0
  20. data/bin/commands/commands.rb +89 -0
  21. data/bin/commands/commands_accepting.rb +76 -0
  22. data/bin/commands/completion.rb +27 -0
  23. data/bin/commands/config.rb +245 -0
  24. data/bin/commands/done.rb +235 -0
  25. data/bin/commands/finish.rb +126 -0
  26. data/bin/commands/flag.rb +90 -0
  27. data/bin/commands/grep.rb +108 -0
  28. data/bin/commands/import.rb +71 -0
  29. data/bin/commands/install_fzf.rb +17 -0
  30. data/bin/commands/last.rb +81 -0
  31. data/bin/commands/meanwhile.rb +76 -0
  32. data/bin/commands/note.rb +91 -0
  33. data/bin/commands/now.rb +145 -0
  34. data/bin/commands/on.rb +65 -0
  35. data/bin/commands/open.rb +53 -0
  36. data/bin/commands/plugins.rb +23 -0
  37. data/bin/commands/recent.rb +77 -0
  38. data/bin/commands/redo.rb +26 -0
  39. data/bin/commands/reset.rb +73 -0
  40. data/bin/commands/rotate.rb +42 -0
  41. data/bin/commands/sections.rb +11 -0
  42. data/bin/commands/select.rb +105 -0
  43. data/bin/commands/show.rb +185 -0
  44. data/bin/commands/since.rb +63 -0
  45. data/bin/commands/tag.rb +149 -0
  46. data/bin/commands/tag_dir.rb +29 -0
  47. data/bin/commands/tags.rb +66 -0
  48. data/bin/commands/template.rb +61 -0
  49. data/bin/commands/today.rb +64 -0
  50. data/bin/commands/undo.rb +49 -0
  51. data/bin/commands/view.rb +201 -0
  52. data/bin/commands/views.rb +11 -0
  53. data/bin/commands/yesterday.rb +72 -0
  54. data/bin/doing +241 -3662
  55. data/docs/doc/Array.html +13 -449
  56. data/docs/doc/BooleanTermParser/Clause.html +5 -5
  57. data/docs/doc/BooleanTermParser/Operator.html +4 -4
  58. data/docs/doc/BooleanTermParser/Query.html +8 -8
  59. data/docs/doc/BooleanTermParser/QueryParser.html +2 -2
  60. data/docs/doc/BooleanTermParser/QueryTransformer.html +2 -2
  61. data/docs/doc/BooleanTermParser.html +1 -1
  62. data/docs/doc/Doing/Color.html +65 -59
  63. data/docs/doc/Doing/Completion.html +2 -2
  64. data/docs/doc/Doing/Configuration.html +49 -16
  65. data/docs/doc/Doing/Errors/DoingNoTraceError.html +2 -2
  66. data/docs/doc/Doing/Errors/DoingRuntimeError.html +2 -2
  67. data/docs/doc/Doing/Errors/DoingStandardError.html +2 -2
  68. data/docs/doc/Doing/Errors/EmptyInput.html +2 -2
  69. data/docs/doc/Doing/Errors/NoResults.html +2 -2
  70. data/docs/doc/Doing/Errors/PluginException.html +3 -3
  71. data/docs/doc/Doing/Errors/UserCancelled.html +2 -2
  72. data/docs/doc/Doing/Errors/WrongCommand.html +2 -2
  73. data/docs/doc/Doing/Errors.html +1 -1
  74. data/docs/doc/Doing/Hooks.html +6 -6
  75. data/docs/doc/Doing/Item.html +50 -16
  76. data/docs/doc/Doing/Items.html +10 -10
  77. data/docs/doc/Doing/LogAdapter.html +24 -24
  78. data/docs/doc/Doing/Note.html +7 -7
  79. data/docs/doc/Doing/Pager.html +4 -4
  80. data/docs/doc/Doing/Plugins.html +7 -7
  81. data/docs/doc/Doing/Prompt.html +59 -14
  82. data/docs/doc/Doing/Section.html +6 -6
  83. data/docs/doc/Doing/TemplateString.html +8 -8
  84. data/docs/doc/Doing/Types.html +46 -1
  85. data/docs/doc/Doing/Util/Backup.html +10 -10
  86. data/docs/doc/Doing/Util.html +15 -15
  87. data/docs/doc/Doing/WWID.html +73 -61
  88. data/docs/doc/Doing.html +3 -3
  89. data/docs/doc/FalseClass.html +235 -0
  90. data/docs/doc/GLI/Commands/Help.html +3 -3
  91. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +17 -17
  92. data/docs/doc/GLI/Commands.html +1 -1
  93. data/docs/doc/GLI.html +1 -1
  94. data/docs/doc/Hash.html +45 -11
  95. data/docs/doc/Numeric.html +5 -5
  96. data/docs/doc/Object.html +203 -0
  97. data/docs/doc/PhraseParser/Operator.html +4 -4
  98. data/docs/doc/PhraseParser/PhraseClause.html +5 -5
  99. data/docs/doc/PhraseParser/Query.html +10 -10
  100. data/docs/doc/PhraseParser/QueryParser.html +2 -2
  101. data/docs/doc/PhraseParser/QueryTransformer.html +2 -2
  102. data/docs/doc/PhraseParser/TermClause.html +5 -5
  103. data/docs/doc/PhraseParser.html +1 -1
  104. data/docs/doc/Status.html +7 -7
  105. data/docs/doc/String.html +306 -3111
  106. data/docs/doc/Symbol.html +45 -11
  107. data/docs/doc/Time.html +6 -6
  108. data/docs/doc/TrueClass.html +235 -0
  109. data/docs/doc/_index.html +37 -19
  110. data/docs/doc/class_list.html +1 -1
  111. data/docs/doc/file.README.html +2 -2
  112. data/docs/doc/index.html +2 -2
  113. data/docs/doc/method_list.html +240 -576
  114. data/docs/doc/top-level-namespace.html +2 -2
  115. data/doing.rdoc +297 -169
  116. data/example_plugin.rb +2 -2
  117. data/lib/completion/_doing.zsh +35 -31
  118. data/lib/completion/doing.bash +30 -19
  119. data/lib/completion/doing.fish +81 -67
  120. data/lib/doing/array/array.rb +4 -0
  121. data/lib/doing/array/nested_hash.rb +17 -0
  122. data/lib/doing/{array.rb → array/tags.rb} +7 -25
  123. data/lib/doing/changelog/change.rb +26 -11
  124. data/lib/doing/changelog/changes.rb +16 -4
  125. data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
  126. data/lib/doing/chronify/chronify.rb +5 -0
  127. data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
  128. data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
  129. data/lib/doing/colors.rb +115 -54
  130. data/lib/doing/configuration.rb +9 -6
  131. data/lib/doing/good.rb +72 -0
  132. data/lib/doing/hash.rb +4 -0
  133. data/lib/doing/help_monkey_patch.rb +6 -5
  134. data/lib/doing/hooks.rb +3 -3
  135. data/lib/doing/item.rb +19 -15
  136. data/lib/doing/items.rb +2 -2
  137. data/lib/doing/log_adapter.rb +35 -2
  138. data/lib/doing/normalize.rb +188 -0
  139. data/lib/doing/pager.rb +1 -0
  140. data/lib/doing/plugins/export/dayone_export.rb +1 -1
  141. data/lib/doing/plugins/export/html_export.rb +1 -1
  142. data/lib/doing/plugins/export/json_export.rb +1 -1
  143. data/lib/doing/plugins/export/markdown_export.rb +1 -1
  144. data/lib/doing/plugins/export/template_export.rb +3 -1
  145. data/lib/doing/plugins/import/calendar_import.rb +1 -1
  146. data/lib/doing/plugins/import/doing_import.rb +1 -1
  147. data/lib/doing/plugins/import/timing_import.rb +1 -1
  148. data/lib/doing/prompt.rb +9 -3
  149. data/lib/doing/string/highlight.rb +95 -0
  150. data/lib/doing/string/query.rb +129 -0
  151. data/lib/doing/string/string.rb +12 -0
  152. data/lib/doing/string/tags.rb +164 -0
  153. data/lib/doing/string/transform.rb +168 -0
  154. data/lib/doing/string/truncate.rb +75 -0
  155. data/lib/doing/string/url.rb +82 -0
  156. data/lib/doing/template_string.rb +2 -24
  157. data/lib/doing/types.rb +9 -0
  158. data/lib/doing/util.rb +20 -16
  159. data/lib/doing/version.rb +1 -1
  160. data/lib/doing/wwid.rb +91 -51
  161. data/lib/doing.rb +5 -6
  162. data/lib/examples/commands/wiki.rb +6 -7
  163. data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
  164. data/lib/helpers/threaded_tests.rb +69 -79
  165. data/lib/helpers/threaded_tests_string.rb +50 -0
  166. data/scripts/deploy.rb +107 -0
  167. data/scripts/runtests.sh +4 -0
  168. metadata +65 -8
  169. data/lib/doing/string.rb +0 -765
  170. 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
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'pathname'
3
4
 
4
5
  module Doing
@@ -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, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
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, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
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, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order])
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, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
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
- sort_by_name: opt[:sort_tags],
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.dup }
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.dup
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.dup }
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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ::String
4
+ include Doing::Color
5
+ end
6
+
7
+ require_relative 'highlight'
8
+ require_relative 'query'
9
+ require_relative 'tags'
10
+ require_relative 'transform'
11
+ require_relative 'truncate'
12
+ require_relative 'url'