yap-shell 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23d1c68c8e4cb9e3531fef656445654b3e5fd0d7
4
- data.tar.gz: 59e2297008fecdfe423b9c97d1e897bd138f9e31
3
+ metadata.gz: 70c4b7a17173c5d2f16dd2d318ad7fca64121c2b
4
+ data.tar.gz: f17c759ac10afbb5ed4d9bac3c47cdfc2e8590f8
5
5
  SHA512:
6
- metadata.gz: 764180407fb136d9495b713805d7b1ac372405faa4a4505bffaa1b4bc29e84151172547f560c31e60c66463cd993fe9ca446cb4cd8828c43da5abf18e2027acc
7
- data.tar.gz: d41da1cc4a590125e529a746c3d182a9da4764e9f0f7da37a37f62b87e5eb1075883556c302ed4b07a6e8f81afe2164d5d07509864f060d193ca17fdbb89b799
6
+ metadata.gz: e998bdac5eccd88ff29438bed0dc970371453a11e300049988b328174940a179c94d3f3ce90e061500ff3c87022517baa7380ae7d88d41304c31c234d092fea9
7
+ data.tar.gz: 6c7b6164c54f5dc7a3a1cca6a2a78d15dd7a31af58782cb7a7f4b0ff57cce86d3893aa920d668d3f054602a48e80be5b7ef331ca1720af0ca637d766c2ee3e69
data/Gemfile CHANGED
@@ -6,4 +6,4 @@ gemspec
6
6
  # gem 'yap-shell-parser', git: 'git@github.com:zdennis/yap-shell-parser.git'
7
7
  # gem "rawline", git: 'git@github.com:zdennis/rawline.git', branch: 'issues/input-issues'
8
8
  # gem "rawline", path: '/Users/zdennis/source/opensource_projects/rawline'
9
- # gem "pry-byebug"
9
+ gem "pry-byebug"
@@ -0,0 +1,16 @@
1
+ # History Addon
2
+
3
+ The History addon is provides primitive history capabilities:
4
+
5
+ * Loads history from ~/.yap/history when yap is loaded
6
+ * Saves history to ~/.yap/history when yap exits
7
+ * Saving is an append-only operation
8
+ * ~/.yap/history is in YAML format
9
+
10
+ ## Shell Functions
11
+
12
+ The history addon provides the `history` shell function that prints the contents of the history.
13
+
14
+ ## Limitations
15
+
16
+ The history addon currently does not support any kind of advanced history operations.
@@ -1,101 +1,55 @@
1
- require 'forwardable'
2
- require 'term/ansicolor'
3
- require 'ostruct'
4
-
5
1
  class History < Addon
6
- require 'history/buffer'
7
- require 'history/events'
8
-
9
- Color = Object.extend Term::ANSIColor
10
-
11
- class << self
12
- attr_accessor :history_item_formatter, :ignore_history_item
13
- end
14
-
15
- self.ignore_history_item = ->(item:) do
16
- item.command == "exit"
17
- end
18
-
19
- self.history_item_formatter = ->(item:, options:{}) do
20
- if item.duration
21
- sprintf(
22
- "%#{options[:max_position_width]}d %-s %s",
23
- item.position,
24
- item.command,
25
- Color.negative(Color.intense_black(item.duration))
26
- )
27
- else
28
- sprintf("%#{options[:max_position_width]}d %-s", item.position, item.command)
29
- end
30
- end
2
+ attr_reader :file
3
+ attr_reader :position
31
4
 
32
5
  def initialize_world(world)
33
6
  @world = world
34
- load_history
35
-
36
- world.editor.bind(:ctrl_h) { show_history(@world.editor) }
37
7
 
38
- world.func(:history) do |args:, stdin:, stdout:, stderr:|
39
- first_arg = args.first
40
- case first_arg
41
- when String
42
- if first_arg.start_with?("/")
43
- regex = first_arg.gsub(/(^\|\/$)/, '')
44
- ignore_history_item = ->(item:, options:{}) do
45
- item.command !~ /#{regex}/
46
- end
47
- else
48
- ignore_history_item = ->(item:, options:{}) do
49
- item.command !~ /#{first_arg}/
50
- end
51
- end
52
- show_history(world.editor, redraw_prompt:false, ignore_history_item:ignore_history_item)
53
- else
54
- show_history(world.editor, redraw_prompt:false)
55
- end
56
- end
57
- end
8
+ @file = world.configuration.path_for('history')
9
+ @position = 0
58
10
 
59
- def show_history(editor, redraw_prompt:true, ignore_history_item:nil, history_item_formatter:nil)
60
- editor.puts @history
61
- end
11
+ load_history
62
12
 
63
- def executing(command:, started_at:)
64
- # raise "Cannot acknowledge execution beginning of a command when no group has been started!" unless history.last
65
- end
13
+ world.func(:history) do |args:, stdin:, stdout:, stderr:|
14
+ history_length = @world.editor.history.length
15
+ first_arg = args.first.to_i
16
+ size = first_arg > 0 ? first_arg + 1 : history_length
66
17
 
67
- def executed(command:, stopped_at:)
68
- # raise "Cannot complete execution of a command when no command has been started!" unless history.last
18
+ # start from -2 since we don't want to include the current history
19
+ # command being run.
20
+ stdout.puts @world.editor.history[history_length-size..-2]
21
+ end
69
22
  end
70
23
 
71
24
  def save
72
- File.open(history_file, "a") do |file|
25
+ File.open(file, "a") do |file|
73
26
  # Don't write the YAML header because we're going to append to the
74
27
  # history file, not overwrite. YAML works fine without it.
75
- file.write @world.editor.history.to_yaml(@history_start_position..-1).gsub(/^---.*/, '')
28
+ unwritten_history = @world.editor.history.to_a[@position..-1]
29
+ if unwritten_history.any?
30
+ contents = unwritten_history
31
+ .each_with_object([]) { |line, arr| arr << line unless line == arr.last }
32
+ .map { |str| str.respond_to?(:without_ansi) ? str.without_ansi : str }
33
+ .to_yaml
34
+ .gsub(/^---.*?^/m, '')
35
+ file.write contents
36
+ end
76
37
  end
77
38
  end
78
39
 
79
40
  private
80
41
 
81
- def history
82
- @history
83
- end
84
-
85
- def history_file
86
- @history_file ||= File.expand_path('~') + '/.yap-history'
87
- end
88
-
89
42
  def load_history
90
- @world.editor.history = @history = History::Buffer.new(Float::INFINITY)
91
- @history_start_position = 0
92
-
93
43
  at_exit { save }
94
44
 
95
- return unless File.exists?(history_file) && File.readable?(history_file)
45
+ if File.exists?(file) && File.readable?(file)
46
+ history = YAML.load_file(file) || []
47
+
48
+ # History starts at the end of the history loaded from file.
49
+ @position = history.length
96
50
 
97
- history_elements = YAML.load_file(history_file) || []
98
- @history_start_position = history_elements.length
99
- @world.editor.history.replace(history_elements)
51
+ # Rely on the builtin history for now.
52
+ @world.editor.history.replace(history)
53
+ end
100
54
  end
101
55
  end
@@ -1,4 +1,6 @@
1
1
  class KeyboardMacros < Addon
2
+ require 'keyboard_macros/cycle'
3
+
2
4
  DEFAULT_TRIGGER_KEY = :ctrl_g
3
5
  DEFAULT_CANCEL_KEY = " "
4
6
  DEFAULT_TIMEOUT_IN_MS = 500
@@ -35,7 +37,8 @@ class KeyboardMacros < Addon
35
37
  configuration = Configuration.new(
36
38
  keymap: world.editor.terminal.keys,
37
39
  trigger_key: trigger_key,
38
- cancellation: Cancellation.new(cancel_key: cancel_key, &cancel_blk)
40
+ cancellation: Cancellation.new(cancel_key: cancel_key, &cancel_blk),
41
+ editor: world.editor,
39
42
  )
40
43
 
41
44
  blk.call configuration if blk
@@ -43,7 +46,8 @@ class KeyboardMacros < Addon
43
46
  world.unbind(trigger_key)
44
47
  world.bind(trigger_key) do
45
48
  begin
46
- @stack << configuration
49
+ @previous_result = nil
50
+ @stack << OpenStruct.new(configuration: configuration)
47
51
  configuration.start.call if configuration.start
48
52
  world.editor.keyboard_input_processors.push(self)
49
53
  world.editor.input.wait_timeout_in_seconds = 0.1
@@ -55,28 +59,47 @@ class KeyboardMacros < Addon
55
59
  @configurations << configuration
56
60
  end
57
61
 
62
+ def cycle(name, &cycle_thru_blk)
63
+ @cycles ||= {}
64
+ if block_given?
65
+ cycle = KeyboardMacros::Cycle.new(
66
+ cycle_proc: cycle_thru_blk,
67
+ on_cycle_proc: -> (old_value, new_value) {
68
+ @world.editor.delete_n_characters(old_value.to_s.length)
69
+ process_result(new_value)
70
+ }
71
+ )
72
+ @cycles[name] = cycle
73
+ else
74
+ @cycles.fetch(name)
75
+ end
76
+ end
77
+
58
78
  #
59
79
  # InputProcessor Methods
60
80
  #
61
81
 
62
82
  def read_bytes(bytes)
63
- configuration = @stack.last
64
- bytes.each do |byte|
83
+ if @stack.last
84
+ current_definition = @stack.last
85
+ configuration = current_definition.configuration
86
+ end
87
+
88
+ bytes.each_with_index do |byte, i|
65
89
  definition = configuration[byte]
66
90
  if !definition
67
91
  cancel_processing if cancel_on_unknown_sequences
68
92
  break
69
93
  end
94
+
70
95
  configuration = definition.configuration
71
- if configuration
72
- configuration.start.call if configuration.start
73
- @stack << configuration
74
- end
96
+ configuration.start.call if configuration.start
97
+ @stack << definition
75
98
 
76
99
  result = definition.process
77
100
 
78
101
  if result =~ /\n$/
79
- world.editor.write result.chomp
102
+ world.editor.write result.chomp, add_to_line_history: false
80
103
  world.editor.event_loop.clear @event_id if @event_id
81
104
  cancel_processing
82
105
  world.editor.newline # add_to_history
@@ -84,18 +107,31 @@ class KeyboardMacros < Addon
84
107
  break
85
108
  end
86
109
 
87
- @stack.pop if definition.fragment?
110
+ if i == bytes.length - 1
111
+ while @stack.last && @stack.last.fragment?
112
+ @stack.pop
113
+ end
114
+ end
88
115
 
89
116
  if @event_id
90
117
  world.editor.event_loop.clear @event_id
91
118
  @event_id = queue_up_remove_input_processor
92
119
  end
93
- world.editor.write result if result.is_a?(String)
120
+
121
+ process_result(result)
94
122
  end
95
123
  end
96
124
 
97
125
  private
98
126
 
127
+ def process_result(result)
128
+ if result.is_a?(String)
129
+ $z.puts "WRITING: #{result}"
130
+ @world.editor.write result, add_to_line_history: false
131
+ @previous_result = result
132
+ end
133
+ end
134
+
99
135
  def queue_up_remove_input_processor(&blk)
100
136
  return unless @timeout_in_ms
101
137
 
@@ -111,8 +147,8 @@ class KeyboardMacros < Addon
111
147
 
112
148
  def cancel_processing
113
149
  @event_id = nil
114
- @stack.reverse.each do |configuration|
115
- configuration.stop.call if configuration.stop
150
+ @stack.reverse.each do |definition|
151
+ definition.configuration.stop.call if definition.configuration.stop
116
152
  end
117
153
  @stack.clear
118
154
  if world.editor.keyboard_input_processors.last == self
@@ -137,13 +173,15 @@ class KeyboardMacros < Addon
137
173
  class Configuration
138
174
  attr_reader :cancellation, :trigger_key, :keymap
139
175
 
140
- def initialize(cancellation: nil, keymap: {}, trigger_key: nil)
176
+ def initialize(cancellation: nil, editor:, keymap: {}, trigger_key: nil)
141
177
  @cancellation = cancellation
178
+ @editor = editor
142
179
  @keymap = keymap
143
180
  @trigger_key = trigger_key
144
181
  @storage = {}
145
182
  @on_start_blk = nil
146
183
  @on_stop_blk = nil
184
+ @cycles = {}
147
185
 
148
186
  if @cancellation
149
187
  define @cancellation.cancel_key, -> { @cancellation.call }
@@ -160,13 +198,25 @@ class KeyboardMacros < Addon
160
198
  @on_stop_blk
161
199
  end
162
200
 
163
- def fragment(sequence, result)
164
- define(sequence, result).tap do |definition|
165
- definition.fragment!
201
+ def cycle(name, &cycle_thru_blk)
202
+ if block_given?
203
+ cycle = KeyboardMacros::Cycle.new(
204
+ cycle_proc: cycle_thru_blk,
205
+ on_cycle_proc: -> (old_value, new_value) {
206
+ @editor.delete_n_characters(old_value.to_s.length)
207
+ }
208
+ )
209
+ @cycles[name] = cycle
210
+ else
211
+ @cycles.fetch(name)
166
212
  end
167
213
  end
168
214
 
169
- def define(sequence, result, &blk)
215
+ def fragment(sequence, result)
216
+ define(sequence, result, fragment: true)
217
+ end
218
+
219
+ def define(sequence, result=nil, fragment: false, &blk)
170
220
  unless result.respond_to?(:call)
171
221
  string_result = result
172
222
  result = -> { string_result }
@@ -178,6 +228,7 @@ class KeyboardMacros < Addon
178
228
  self,
179
229
  sequence.bytes,
180
230
  result,
231
+ fragment: fragment,
181
232
  &blk
182
233
  )
183
234
  when Symbol
@@ -187,10 +238,11 @@ class KeyboardMacros < Addon
187
238
  fail "Cannot bind unknown sequence #{sequence.inspect}"
188
239
  },
189
240
  result,
241
+ fragment: fragment,
190
242
  &blk
191
243
  )
192
244
  when Regexp
193
- define_sequence_for_regex(sequence, result, &blk)
245
+ define_sequence_for_regex(sequence, result, fragment: fragment, &blk)
194
246
  else
195
247
  raise NotImplementedError, <<-EOT.gsub(/^\s*/, '')
196
248
  Don't know how to define macro for sequence: #{sequence.inspect}
@@ -206,67 +258,87 @@ class KeyboardMacros < Addon
206
258
  @storage[key] = definition
207
259
  end
208
260
 
261
+ def inspect
262
+ str = @storage.map{ |k,v| "#{k}=#{v.inspect}" }.join("\n ")
263
+ num_items = @storage.reduce(0) { |s, arr| s + arr.length }
264
+ "<Configuration num_items=#{num_items} stored_keys=#{str}>"
265
+ end
266
+
209
267
  private
210
268
 
211
- def define_sequence_for_regex(regex, result, &blk)
269
+ def define_sequence_for_regex(regex, result, fragment: false, &blk)
212
270
  @storage[regex] = Definition.new(
213
271
  configuration: Configuration.new(
214
272
  cancellation: @cancellation,
215
- keymap: @keymap
273
+ keymap: @keymap,
274
+ editor: @editor
216
275
  ),
276
+ fragment: fragment,
217
277
  sequence: regex,
218
278
  result: result,
219
279
  &blk
220
280
  )
221
281
  end
222
282
 
223
- def recursively_define_sequence_for_bytes(configuration, bytes, result, &blk)
283
+ def recursively_define_sequence_for_bytes(configuration, bytes, result, fragment: false, &blk)
224
284
  byte, rest = bytes[0], bytes[1..-1]
225
285
  if rest.any?
226
- definition = Definition.new(
227
- configuration: Configuration.new(
228
- cancellation: @cancellation,
229
- keymap: @keymap
230
- ),
231
- sequence: byte,
232
- result: nil,
233
- &blk
234
- )
286
+ definition = if configuration[byte]
287
+ configuration[byte]
288
+ else
289
+ Definition.new(
290
+ configuration: Configuration.new(
291
+ cancellation: @cancellation,
292
+ keymap: @keymap,
293
+ editor: @editor
294
+ ),
295
+ fragment: fragment,
296
+ sequence: byte,
297
+ result: nil
298
+ )
299
+ end
300
+ blk.call(definition.configuration) if blk
235
301
  configuration[byte] = definition
236
302
  recursively_define_sequence_for_bytes(
237
303
  definition.configuration,
238
304
  rest,
239
305
  result,
306
+ fragment: fragment,
240
307
  &blk
241
308
  )
242
309
  else
243
- configuration[byte] = Definition.new(
244
- configuration: Configuration.new(keymap: @keymap),
310
+ definition = Definition.new(
311
+ configuration: Configuration.new(
312
+ keymap: @keymap,
313
+ editor: @editor
314
+ ),
315
+ fragment: fragment,
245
316
  sequence: byte,
246
- result: result,
247
- &blk
317
+ result: result
248
318
  )
319
+ configuration[byte] = definition
320
+ blk.call(definition.configuration) if blk
321
+ definition
249
322
  end
250
323
  end
251
324
  end
252
325
 
253
326
  class Definition
254
- attr_reader :bytes, :configuration, :result, :sequence
327
+ attr_reader :configuration, :result, :sequence
255
328
 
256
- def initialize(configuration: nil, sequence:, result: nil, &blk)
257
- @fragment = false
329
+ def initialize(configuration: nil, fragment: false, sequence:, result: nil)
330
+ @fragment = fragment
258
331
  @configuration = configuration
259
332
  @sequence = sequence
260
333
  @result = result
261
- blk.call(@configuration) if blk
262
334
  end
263
335
 
264
- def fragment?
265
- @fragment
336
+ def inspect
337
+ "<Definition fragment=#{@fragment.inspect} sequence=#{@sequence.inspect} result=#{@result.inspect} configuration=#{@configuration.inspect}>"
266
338
  end
267
339
 
268
- def fragment!
269
- @fragment = true
340
+ def fragment?
341
+ @fragment
270
342
  end
271
343
 
272
344
  def matches?(byte)
@@ -0,0 +1,38 @@
1
+ class KeyboardMacros
2
+ class Cycle
3
+ def initialize(cycle_proc:, on_cycle_proc: nil)
4
+ @cycle_proc = cycle_proc
5
+ @on_cycle_proc = on_cycle_proc
6
+ @previous_result = nil
7
+ reset
8
+ end
9
+
10
+ def next
11
+ @index = -1 if @index >= cycle_values.length - 1
12
+ on_cycle cycle_values[@index += 1]
13
+ end
14
+
15
+ def previous
16
+ @index = cycle_values.length if @index < 0
17
+ on_cycle cycle_values[@index -= 1]
18
+ end
19
+
20
+ def reset
21
+ @index = -1
22
+ @previous_result = nil
23
+ @cycle_values = nil
24
+ end
25
+
26
+ private
27
+
28
+ def cycle_values
29
+ @cycle_values ||= @cycle_proc.call
30
+ end
31
+
32
+ def on_cycle(new_value)
33
+ @on_cycle_proc.call(@previous_result, new_value) if @on_cycle_proc
34
+ @previous_result = new_value
35
+ new_value
36
+ end
37
+ end
38
+ end
@@ -22,7 +22,7 @@ class PromptUpdates < Addon
22
22
  private
23
23
 
24
24
  def determine_branch
25
- `git branch`.scan(/\*\s*(.*)$/).flatten.first
25
+ `git branch 2>/dev/null`.scan(/\*\s*(.*)$/).flatten.first
26
26
  end
27
27
 
28
28
  end
@@ -0,0 +1,151 @@
1
+ class TabCompletion
2
+ class BasicCompletion
3
+ class << self
4
+ attr_accessor :priority
5
+ end
6
+ self.priority = 1
7
+
8
+ attr_reader :world
9
+
10
+ def initialize(world:, word_break_characters:, path:nil)
11
+ @world = world
12
+ @word_break_characters = word_break_characters
13
+ path ||= @world.env["PATH"]
14
+ @paths = path.split(":")
15
+ end
16
+
17
+ def completions_for(word, words, word_index)
18
+ completions_by_name = {}
19
+ if looking_for_command?(word, words, word_index)
20
+ # Lowest Priority
21
+ completions_by_name.merge! command_completion_matches_for(word, words)
22
+
23
+ # Low Priority
24
+ completions_by_name.merge! builtin_completion_matches_for(word, words)
25
+
26
+ # Medium Priority
27
+ completions_by_name.merge! executable_filename_completion_matches_for(word, words)
28
+
29
+ # High Priority
30
+ completions_by_name.merge! shell_command_completion_matches_for(word, words)
31
+
32
+ # Highest Priority
33
+ completions_by_name.merge! alias_completion_matches_for(word, words)
34
+ else
35
+ completions_by_name.merge! filename_completion_matches_for(word, words)
36
+ end
37
+ completions_by_name.merge! environment_variable_completions_for(word, words)
38
+ completions_by_name.values
39
+ end
40
+
41
+ private
42
+
43
+ def looking_for_command?(word, words, word_index)
44
+ return false unless word_index
45
+ return true if word_index == 0
46
+ return true if words[word_index - 1] =~ /[;&]/
47
+ false
48
+ end
49
+
50
+ def alias_completion_matches_for(word, words)
51
+ @world.aliases.names.each_with_object({}) do |name, result|
52
+ if name =~ /^#{Regexp.escape(word)}/
53
+ result[name] ||= CompletionResult.new(
54
+ type: :alias,
55
+ text: name
56
+ )
57
+ end
58
+ end
59
+ end
60
+
61
+ def builtin_completion_matches_for(word, words)
62
+ @world.builtins.each_with_object({}) do |builtin, result|
63
+ if builtin =~ /^#{Regexp.escape(word)}/
64
+ result[builtin] ||= CompletionResult.new(
65
+ type: :builtin,
66
+ text: builtin
67
+ )
68
+ end
69
+ end
70
+ end
71
+
72
+ def command_completion_matches_for(word, words)
73
+ @paths.each_with_object({}) do |path, matches|
74
+ glob = File.join(path, "#{word}*")
75
+ arr = Dir[glob].select { |path| File.executable?(path) && File.file?(path) }
76
+ arr.map { |path| File.basename(path) }.uniq.each do |command|
77
+ matches[command] = CompletionResult.new(type: :command, text: command)
78
+ end
79
+ end
80
+ end
81
+
82
+ def environment_variable_completions_for(word, words)
83
+ return {} unless word =~ /^\$/
84
+ prefix, word_sans_prefix = word[0], word[1..-1]
85
+ @world.env.keys.each_with_object({}) do |env_var, result|
86
+ if env_var =~ /^#{Regexp.escape(word_sans_prefix)}/
87
+ result[env_var] ||= CompletionResult.new(
88
+ type: :env_var,
89
+ text: prefix + env_var
90
+ )
91
+ end
92
+ end
93
+ end
94
+
95
+ def executable_filename_completion_matches_for(word, words)
96
+ glob = "#{word}*"
97
+ glob.gsub!("~", world.env["HOME"])
98
+ Dir.glob(glob, File::FNM_CASEFOLD).each_with_object({}) do |path, result|
99
+ text = path.gsub(filtered_work_break_characters_rgx, '\\\\\1')
100
+ descriptive_text = File.basename(text)
101
+ if !File.directory?(path) && File.executable?(path)
102
+ result[path] = CompletionResult.new(
103
+ type: :command,
104
+ text: text,
105
+ descriptive_text: descriptive_text
106
+ )
107
+ end
108
+ end
109
+ end
110
+
111
+ def shell_command_completion_matches_for(word, words)
112
+ @world.shell_commands.each_with_object({}) do |shell_command, result|
113
+ if shell_command =~ /^#{Regexp.escape(word)}/
114
+ result[shell_command] ||= CompletionResult.new(
115
+ type: :shell_command,
116
+ text: shell_command
117
+ )
118
+ end
119
+ end
120
+ end
121
+
122
+ def filename_completion_matches_for(word, line)
123
+ glob = "#{word}*"
124
+ glob.gsub!("~", world.env["HOME"])
125
+ Dir.glob(glob, File::FNM_CASEFOLD).each_with_object({}) do |path, result|
126
+ text = path.gsub(filtered_work_break_characters_rgx, '\\\\\1')
127
+ descriptive_text = File.basename(text)
128
+ result[path] = if File.directory?(path)
129
+ CompletionResult.new(type: :directory, text: text, descriptive_text: descriptive_text)
130
+ elsif File.symlink?(path)
131
+ CompletionResult.new(type: :symlink, text: text, descriptive_text: descriptive_text)
132
+ elsif File.file?(path) && File.executable?(path)
133
+ CompletionResult.new(type: :command, text: text, descriptive_text: descriptive_text)
134
+ else
135
+ CompletionResult.new(type: :file, text: text, descriptive_text: descriptive_text)
136
+ end
137
+ end
138
+ end
139
+
140
+ # Remove file separator and the back-slash from word break characters when determining
141
+ # the pre-word-context
142
+ def filtered_word_break_characters
143
+ @word_break_characters.sub(File::Separator, "").sub('\\', '')
144
+ end
145
+
146
+ def filtered_work_break_characters_rgx
147
+ /([#{Regexp.escape(filtered_word_break_characters)}])/
148
+ end
149
+
150
+ end
151
+ end