reline 0.5.10 → 0.5.12

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.
@@ -36,7 +36,6 @@ class Reline::LineEditor
36
36
 
37
37
  module CompletionState
38
38
  NORMAL = :normal
39
- COMPLETION = :completion
40
39
  MENU = :menu
41
40
  MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
42
41
  PERFECT_MATCH = :perfect_match
@@ -72,17 +71,21 @@ class Reline::LineEditor
72
71
 
73
72
  MINIMUM_SCROLLBAR_HEIGHT = 1
74
73
 
75
- def initialize(config, encoding)
74
+ def initialize(config)
76
75
  @config = config
77
76
  @completion_append_character = ''
78
77
  @screen_size = [0, 0] # Should be initialized with actual winsize in LineEditor#reset
79
- reset_variables(encoding: encoding)
78
+ reset_variables
80
79
  end
81
80
 
82
81
  def io_gate
83
82
  Reline::IOGate
84
83
  end
85
84
 
85
+ def encoding
86
+ io_gate.encoding
87
+ end
88
+
86
89
  def set_pasting_state(in_pasting)
87
90
  # While pasting, text to be inserted is stored to @continuous_insertion_buffer.
88
91
  # After pasting, this buffer should be force inserted.
@@ -136,9 +139,9 @@ class Reline::LineEditor
136
139
  end
137
140
  end
138
141
 
139
- def reset(prompt = '', encoding:)
142
+ def reset(prompt = '')
140
143
  @screen_size = Reline::IOGate.get_screen_size
141
- reset_variables(prompt, encoding: encoding)
144
+ reset_variables(prompt)
142
145
  @rendered_screen.base_y = Reline::IOGate.cursor_pos.y
143
146
  if ENV.key?('RELINE_ALT_SCROLLBAR')
144
147
  @full_block = '::'
@@ -150,7 +153,7 @@ class Reline::LineEditor
150
153
  @upper_half_block = '▀'
151
154
  @lower_half_block = '▄'
152
155
  @block_elem_width = 1
153
- elsif @encoding == Encoding::UTF_8
156
+ elsif encoding == Encoding::UTF_8
154
157
  @full_block = '█'
155
158
  @upper_half_block = '▀'
156
159
  @lower_half_block = '▄'
@@ -219,10 +222,9 @@ class Reline::LineEditor
219
222
  @eof
220
223
  end
221
224
 
222
- def reset_variables(prompt = '', encoding:)
225
+ def reset_variables(prompt = '')
223
226
  @prompt = prompt.gsub("\n", "\\n")
224
227
  @mark_pointer = nil
225
- @encoding = encoding
226
228
  @is_multiline = false
227
229
  @finished = false
228
230
  @history_pointer = nil
@@ -239,7 +241,7 @@ class Reline::LineEditor
239
241
  @searching_prompt = nil
240
242
  @just_cursor_moving = false
241
243
  @eof = false
242
- @continuous_insertion_buffer = String.new(encoding: @encoding)
244
+ @continuous_insertion_buffer = String.new(encoding: encoding)
243
245
  @scroll_partial_screen = 0
244
246
  @drop_terminate_spaces = false
245
247
  @in_pasting = false
@@ -259,11 +261,10 @@ class Reline::LineEditor
259
261
 
260
262
  def reset_line
261
263
  @byte_pointer = 0
262
- @buffer_of_lines = [String.new(encoding: @encoding)]
264
+ @buffer_of_lines = [String.new(encoding: encoding)]
263
265
  @line_index = 0
264
266
  @cache.clear
265
267
  @line_backup_in_history = nil
266
- @multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
267
268
  end
268
269
 
269
270
  def multiline_on
@@ -275,7 +276,7 @@ class Reline::LineEditor
275
276
  end
276
277
 
277
278
  private def insert_new_line(cursor_line, next_line)
278
- @buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
279
+ @buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: encoding))
279
280
  @buffer_of_lines[@line_index] = cursor_line
280
281
  @line_index += 1
281
282
  @byte_pointer = 0
@@ -297,8 +298,8 @@ class Reline::LineEditor
297
298
  end
298
299
  end
299
300
 
300
- private def split_by_width(str, max_width, offset: 0)
301
- Reline::Unicode.split_by_width(str, max_width, @encoding, offset: offset)
301
+ private def split_line_by_width(str, max_width, offset: 0)
302
+ Reline::Unicode.split_line_by_width(str, max_width, encoding, offset: offset)
302
303
  end
303
304
 
304
305
  def current_byte_pointer_cursor
@@ -388,8 +389,8 @@ class Reline::LineEditor
388
389
  if (cached = cached_wraps[[prompt, line]])
389
390
  next cached
390
391
  end
391
- *wrapped_prompts, code_line_prompt = split_by_width(prompt, width).first.compact
392
- wrapped_lines = split_by_width(line, width, offset: calculate_width(code_line_prompt, true)).first.compact
392
+ *wrapped_prompts, code_line_prompt = split_line_by_width(prompt, width)
393
+ wrapped_lines = split_line_by_width(line, width, offset: calculate_width(code_line_prompt, true))
393
394
  wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
394
395
  end
395
396
  end
@@ -437,7 +438,7 @@ class Reline::LineEditor
437
438
  def wrapped_cursor_position
438
439
  prompt_width = calculate_width(prompt_list[@line_index], true)
439
440
  line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
440
- wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact
441
+ wrapped_line_before_cursor = split_line_by_width(' ' * prompt_width + line_before_cursor, screen_width)
441
442
  wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
442
443
  wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
443
444
  [wrapped_cursor_x, wrapped_cursor_y]
@@ -461,16 +462,19 @@ class Reline::LineEditor
461
462
  def render_finished
462
463
  render_differential([], 0, 0)
463
464
  lines = @buffer_of_lines.size.times.map do |i|
464
- line = prompt_list[i] + modified_lines[i]
465
- wrapped_lines, = split_by_width(line, screen_width)
465
+ line = Reline::Unicode.strip_non_printing_start_end(prompt_list[i]) + modified_lines[i]
466
+ wrapped_lines = split_line_by_width(line, screen_width)
466
467
  wrapped_lines.last.empty? ? "#{line} " : line
467
468
  end
468
469
  @output.puts lines.map { |l| "#{l}\r\n" }.join
469
470
  end
470
471
 
471
472
  def print_nomultiline_prompt
473
+ Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
472
474
  # Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
473
- @output.write @prompt if @prompt && !@is_multiline
475
+ @output.write Reline::Unicode.strip_non_printing_start_end(@prompt) if @prompt && !@is_multiline
476
+ ensure
477
+ Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
474
478
  end
475
479
 
476
480
  def render
@@ -506,6 +510,7 @@ class Reline::LineEditor
506
510
  # by calculating the difference from the previous render.
507
511
 
508
512
  private def render_differential(new_lines, new_cursor_x, new_cursor_y)
513
+ Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
509
514
  rendered_lines = @rendered_screen.lines
510
515
  cursor_y = @rendered_screen.cursor_y
511
516
  if new_lines != rendered_lines
@@ -536,6 +541,8 @@ class Reline::LineEditor
536
541
  Reline::IOGate.move_cursor_column new_cursor_x
537
542
  Reline::IOGate.move_cursor_down new_cursor_y - cursor_y
538
543
  @rendered_screen.cursor_y = new_cursor_y
544
+ ensure
545
+ Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
539
546
  end
540
547
 
541
548
  private def clear_rendered_screen_cache
@@ -570,8 +577,9 @@ class Reline::LineEditor
570
577
  @context
571
578
  end
572
579
 
573
- def retrieve_completion_block(set_completion_quote_character = false)
574
- @line_editor.retrieve_completion_block(set_completion_quote_character)
580
+ def retrieve_completion_block(_unused = false)
581
+ preposing, target, postposing, _quote = @line_editor.retrieve_completion_block
582
+ [preposing, target, postposing]
575
583
  end
576
584
 
577
585
  def call_completion_proc_with_checking_args(pre, target, post)
@@ -791,98 +799,73 @@ class Reline::LineEditor
791
799
  @config.editing_mode
792
800
  end
793
801
 
794
- private def menu(_target, list)
802
+ private def menu(list)
795
803
  @menu_info = MenuInfo.new(list)
796
804
  end
797
805
 
798
- private def complete_internal_proc(list, is_menu)
799
- preposing, target, postposing = retrieve_completion_block
800
- list = list.select { |i|
801
- if i and not Encoding.compatible?(target.encoding, i.encoding)
802
- raise Encoding::CompatibilityError, "#{target.encoding.name} is not compatible with #{i.encoding.name}"
806
+ private def filter_normalize_candidates(target, list)
807
+ target = target.downcase if @config.completion_ignore_case
808
+ list.select do |item|
809
+ next unless item
810
+
811
+ unless Encoding.compatible?(target.encoding, item.encoding)
812
+ # Crash with Encoding::CompatibilityError is required by readline-ext/test/readline/test_readline.rb
813
+ # TODO: fix the test
814
+ raise Encoding::CompatibilityError, "#{target.encoding.name} is not compatible with #{item.encoding.name}"
803
815
  end
816
+
804
817
  if @config.completion_ignore_case
805
- i&.downcase&.start_with?(target.downcase)
818
+ item.downcase.start_with?(target)
806
819
  else
807
- i&.start_with?(target)
808
- end
809
- }.uniq
810
- if is_menu
811
- menu(target, list)
812
- return nil
813
- end
814
- completed = list.inject { |memo, item|
815
- begin
816
- memo_mbchars = memo.unicode_normalize.grapheme_clusters
817
- item_mbchars = item.unicode_normalize.grapheme_clusters
818
- rescue Encoding::CompatibilityError
819
- memo_mbchars = memo.grapheme_clusters
820
- item_mbchars = item.grapheme_clusters
821
- end
822
- size = [memo_mbchars.size, item_mbchars.size].min
823
- result = +''
824
- size.times do |i|
825
- if @config.completion_ignore_case
826
- if memo_mbchars[i].casecmp?(item_mbchars[i])
827
- result << memo_mbchars[i]
828
- else
829
- break
830
- end
831
- else
832
- if memo_mbchars[i] == item_mbchars[i]
833
- result << memo_mbchars[i]
834
- else
835
- break
836
- end
837
- end
820
+ item.start_with?(target)
838
821
  end
839
- result
840
- }
841
- [target, preposing, completed, postposing]
822
+ end.map do |item|
823
+ item.unicode_normalize
824
+ rescue Encoding::CompatibilityError
825
+ item
826
+ end.uniq
842
827
  end
843
828
 
844
- private def perform_completion(list, just_show_list)
829
+ private def perform_completion(preposing, target, postposing, quote, list)
830
+ candidates = filter_normalize_candidates(target, list)
831
+
845
832
  case @completion_state
846
- when CompletionState::NORMAL
847
- @completion_state = CompletionState::COMPLETION
848
833
  when CompletionState::PERFECT_MATCH
849
- @dig_perfect_match_proc&.(@perfect_matched)
850
- end
851
- if just_show_list
852
- is_menu = true
853
- elsif @completion_state == CompletionState::MENU
854
- is_menu = true
855
- elsif @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
856
- is_menu = true
857
- else
858
- is_menu = false
859
- end
860
- result = complete_internal_proc(list, is_menu)
861
- if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
834
+ if @dig_perfect_match_proc
835
+ @dig_perfect_match_proc.call(@perfect_matched)
836
+ return
837
+ end
838
+ when CompletionState::MENU
839
+ menu(candidates)
840
+ return
841
+ when CompletionState::MENU_WITH_PERFECT_MATCH
842
+ menu(candidates)
862
843
  @completion_state = CompletionState::PERFECT_MATCH
844
+ return
863
845
  end
864
- return if result.nil?
865
- target, preposing, completed, postposing = result
866
- return if completed.nil?
867
- if target <= completed and (@completion_state == CompletionState::COMPLETION)
868
- if list.include?(completed)
869
- if list.one?
870
- @completion_state = CompletionState::PERFECT_MATCH
871
- else
872
- @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
873
- perform_completion(list, true) if @config.show_all_if_ambiguous
874
- end
875
- @perfect_matched = completed
846
+
847
+ completed = Reline::Unicode.common_prefix(candidates, ignore_case: @config.completion_ignore_case)
848
+ return if completed.empty?
849
+
850
+ append_character = ''
851
+ if candidates.include?(completed)
852
+ if candidates.one?
853
+ append_character = quote || completion_append_character.to_s
854
+ @completion_state = CompletionState::PERFECT_MATCH
855
+ elsif @config.show_all_if_ambiguous
856
+ menu(candidates)
857
+ @completion_state = CompletionState::PERFECT_MATCH
876
858
  else
877
- @completion_state = CompletionState::MENU
878
- perform_completion(list, true) if @config.show_all_if_ambiguous
879
- end
880
- if not just_show_list and target < completed
881
- @buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
882
- line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n")[@line_index] || String.new(encoding: @encoding)
883
- @byte_pointer = line_to_pointer.bytesize
859
+ @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
884
860
  end
861
+ @perfect_matched = completed
862
+ else
863
+ @completion_state = CompletionState::MENU
864
+ menu(candidates) if @config.show_all_if_ambiguous
885
865
  end
866
+ @buffer_of_lines[@line_index] = (preposing + completed + append_character + postposing).split("\n")[@line_index] || String.new(encoding: encoding)
867
+ line_to_pointer = (preposing + completed + append_character).split("\n")[@line_index] || String.new(encoding: encoding)
868
+ @byte_pointer = line_to_pointer.bytesize
886
869
  end
887
870
 
888
871
  def dialog_proc_scope_completion_journey_data
@@ -911,8 +894,8 @@ class Reline::LineEditor
911
894
  end
912
895
 
913
896
  private def retrieve_completion_journey_state
914
- preposing, target, postposing = retrieve_completion_block
915
- list = call_completion_proc
897
+ preposing, target, postposing, quote = retrieve_completion_block
898
+ list = call_completion_proc(preposing, target, postposing, quote)
916
899
  return unless list.is_a?(Array)
917
900
 
918
901
  candidates = list.select{ |item| item.start_with?(target) }
@@ -1052,20 +1035,11 @@ class Reline::LineEditor
1052
1035
  end
1053
1036
 
1054
1037
  private def normal_char(key)
1055
- @multibyte_buffer << key.combined_char
1056
- if @multibyte_buffer.size > 1
1057
- if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding?
1058
- process_key(@multibyte_buffer.dup.force_encoding(@encoding), nil)
1059
- @multibyte_buffer.clear
1060
- else
1061
- # invalid
1062
- return
1063
- end
1064
- else # single byte
1065
- return if key.char >= 128 # maybe, first byte of multi byte
1038
+ if key.char < 0x80
1066
1039
  method_symbol = @config.editing_mode.get_method(key.combined_char)
1067
1040
  process_key(key.combined_char, method_symbol)
1068
- @multibyte_buffer.clear
1041
+ else
1042
+ process_key(key.char.chr(encoding), nil)
1069
1043
  end
1070
1044
  if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
1071
1045
  byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
@@ -1162,9 +1136,8 @@ class Reline::LineEditor
1162
1136
  end
1163
1137
  end
1164
1138
 
1165
- def call_completion_proc
1166
- result = retrieve_completion_block(true)
1167
- pre, target, post = result
1139
+ def call_completion_proc(pre, target, post, quote)
1140
+ Reline.core.instance_variable_set(:@completion_quote_character, quote)
1168
1141
  result = call_completion_proc_with_checking_args(pre, target, post)
1169
1142
  Reline.core.instance_variable_set(:@completion_quote_character, nil)
1170
1143
  result
@@ -1240,72 +1213,32 @@ class Reline::LineEditor
1240
1213
  process_auto_indent
1241
1214
  end
1242
1215
 
1243
- def retrieve_completion_block(set_completion_quote_character = false)
1244
- if Reline.completer_word_break_characters.empty?
1245
- word_break_regexp = nil
1246
- else
1247
- word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
1248
- end
1249
- if Reline.completer_quote_characters.empty?
1250
- quote_characters_regexp = nil
1251
- else
1252
- quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
1253
- end
1254
- before = current_line.byteslice(0, @byte_pointer)
1255
- rest = nil
1256
- break_pointer = nil
1216
+ def retrieve_completion_block
1217
+ quote_characters = Reline.completer_quote_characters
1218
+ before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
1257
1219
  quote = nil
1258
- closing_quote = nil
1259
- escaped_quote = nil
1260
- i = 0
1261
- while i < @byte_pointer do
1262
- slice = current_line.byteslice(i, @byte_pointer - i)
1263
- unless slice.valid_encoding?
1264
- i += 1
1265
- next
1266
- end
1267
- if quote and slice.start_with?(closing_quote)
1268
- quote = nil
1269
- i += 1
1270
- rest = nil
1271
- elsif quote and slice.start_with?(escaped_quote)
1272
- # skip
1273
- i += 2
1274
- elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
1275
- rest = $'
1276
- quote = $&
1277
- closing_quote = /(?!\\)#{Regexp.escape(quote)}/
1278
- escaped_quote = /\\#{Regexp.escape(quote)}/
1279
- i += 1
1280
- break_pointer = i - 1
1281
- elsif word_break_regexp and not quote and slice =~ word_break_regexp
1282
- rest = $'
1283
- i += 1
1284
- before = current_line.byteslice(i, @byte_pointer - i)
1285
- break_pointer = i
1286
- else
1287
- i += 1
1288
- end
1289
- end
1290
- postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
1291
- if rest
1292
- preposing = current_line.byteslice(0, break_pointer)
1293
- target = rest
1294
- if set_completion_quote_character and quote
1295
- Reline.core.instance_variable_set(:@completion_quote_character, quote)
1296
- if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
1297
- insert_text(quote)
1220
+ # Calcualte closing quote when cursor is at the end of the line
1221
+ if current_line.bytesize == @byte_pointer && !quote_characters.empty?
1222
+ escaped = false
1223
+ before.each do |c|
1224
+ if escaped
1225
+ escaped = false
1226
+ next
1227
+ elsif c == '\\'
1228
+ escaped = true
1229
+ elsif quote
1230
+ quote = nil if c == quote
1231
+ elsif quote_characters.include?(c)
1232
+ quote = c
1298
1233
  end
1299
1234
  end
1300
- else
1301
- preposing = ''
1302
- if break_pointer
1303
- preposing = current_line.byteslice(0, break_pointer)
1304
- else
1305
- preposing = ''
1306
- end
1307
- target = before
1308
1235
  end
1236
+
1237
+ word_break_characters = quote_characters + Reline.completer_word_break_characters
1238
+ break_index = before.rindex { |c| word_break_characters.include?(c) || quote_characters.include?(c) } || -1
1239
+ preposing = before.take(break_index + 1).join
1240
+ target = before.drop(break_index + 1).join
1241
+ postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
1309
1242
  lines = whole_lines
1310
1243
  if @line_index > 0
1311
1244
  preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
@@ -1313,7 +1246,7 @@ class Reline::LineEditor
1313
1246
  if (lines.size - 1) > @line_index
1314
1247
  postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
1315
1248
  end
1316
- [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
1249
+ [preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding), quote&.encode(encoding)]
1317
1250
  end
1318
1251
 
1319
1252
  def confirm_multiline_termination
@@ -1325,7 +1258,7 @@ class Reline::LineEditor
1325
1258
  save_old_buffer
1326
1259
  pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer)
1327
1260
  post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..)
1328
- lines = (pre + text.gsub(/\r\n?/, "\n") + post).split("\n", -1)
1261
+ lines = (pre + Reline::Unicode.safe_encode(text, encoding).gsub(/\r\n?/, "\n") + post).split("\n", -1)
1329
1262
  lines << '' if lines.empty?
1330
1263
  @buffer_of_lines[@line_index, 1] = lines
1331
1264
  @line_index += lines.size - 1
@@ -1370,7 +1303,7 @@ class Reline::LineEditor
1370
1303
  last += current_line.bytesize if last < 0
1371
1304
  first += current_line.bytesize if first < 0
1372
1305
  range = range.exclude_end? ? first...last : first..last
1373
- line = current_line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(@encoding)
1306
+ line = current_line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(encoding)
1374
1307
  set_current_line(line)
1375
1308
  else
1376
1309
  set_current_line(current_line.byteslice(0, start))
@@ -1444,10 +1377,11 @@ class Reline::LineEditor
1444
1377
  @completion_occurs = move_completed_list(:down)
1445
1378
  else
1446
1379
  @completion_journey_state = nil
1447
- result = call_completion_proc
1380
+ pre, target, post, quote = retrieve_completion_block
1381
+ result = call_completion_proc(pre, target, post, quote)
1448
1382
  if result.is_a?(Array)
1449
1383
  @completion_occurs = true
1450
- perform_completion(result, false)
1384
+ perform_completion(pre, target, post, quote, result)
1451
1385
  end
1452
1386
  end
1453
1387
  end
@@ -1566,7 +1500,7 @@ class Reline::LineEditor
1566
1500
  alias_method :backward_char, :ed_prev_char
1567
1501
 
1568
1502
  private def vi_first_print(key)
1569
- @byte_pointer, = Reline::Unicode.vi_first_print(current_line)
1503
+ @byte_pointer = Reline::Unicode.vi_first_print(current_line)
1570
1504
  end
1571
1505
 
1572
1506
  private def ed_move_to_beg(key)
@@ -1581,8 +1515,7 @@ class Reline::LineEditor
1581
1515
  alias_method :end_of_line, :ed_move_to_end
1582
1516
 
1583
1517
  private def generate_searcher(search_key)
1584
- search_word = String.new(encoding: @encoding)
1585
- multibyte_buf = String.new(encoding: 'ASCII-8BIT')
1518
+ search_word = String.new(encoding: encoding)
1586
1519
  hit_pointer = nil
1587
1520
  lambda do |key|
1588
1521
  search_again = false
@@ -1597,11 +1530,7 @@ class Reline::LineEditor
1597
1530
  search_again = true if search_key == key
1598
1531
  search_key = key
1599
1532
  else
1600
- multibyte_buf << key
1601
- if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
1602
- search_word << multibyte_buf.dup.force_encoding(@encoding)
1603
- multibyte_buf.clear
1604
- end
1533
+ search_word << key
1605
1534
  end
1606
1535
  hit = nil
1607
1536
  if not search_word.empty? and @line_backup_in_history&.include?(search_word)
@@ -1665,57 +1594,29 @@ class Reline::LineEditor
1665
1594
  end
1666
1595
 
1667
1596
  private def incremental_search_history(key)
1668
- unless @history_pointer
1669
- @line_backup_in_history = whole_buffer
1670
- end
1597
+ backup = @buffer_of_lines.dup, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history
1671
1598
  searcher = generate_searcher(key)
1672
1599
  @searching_prompt = "(reverse-i-search)`': "
1673
1600
  termination_keys = ["\C-j".ord]
1674
- termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
1601
+ termination_keys.concat(@config.isearch_terminators.chars.map(&:ord)) if @config.isearch_terminators
1675
1602
  @waiting_proc = ->(k) {
1676
- case k
1677
- when *termination_keys
1678
- if @history_pointer
1679
- buffer = Reline::HISTORY[@history_pointer]
1680
- else
1681
- buffer = @line_backup_in_history
1682
- end
1683
- @buffer_of_lines = buffer.split("\n")
1684
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1685
- @line_index = @buffer_of_lines.size - 1
1603
+ chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
1604
+ if k == "\C-g".ord
1605
+ # cancel search and restore buffer
1606
+ @buffer_of_lines, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history = backup
1686
1607
  @searching_prompt = nil
1687
1608
  @waiting_proc = nil
1688
- @byte_pointer = 0
1689
- when "\C-g".ord
1690
- @buffer_of_lines = @line_backup_in_history.split("\n")
1691
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1692
- @line_index = @buffer_of_lines.size - 1
1693
- move_history(nil, line: :end, cursor: :end, save_buffer: false)
1609
+ elsif !termination_keys.include?(k) && (chr.match?(/[[:print:]]/) || k == "\C-h".ord || k == "\C-?".ord || k == "\C-r".ord || k == "\C-s".ord)
1610
+ search_word, prompt_name, hit_pointer = searcher.call(k)
1611
+ Reline.last_incremental_search = search_word
1612
+ @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1613
+ @searching_prompt += ': ' unless @is_multiline
1614
+ move_history(hit_pointer, line: :end, cursor: :end) if hit_pointer
1615
+ else
1616
+ # terminaton_keys and other keys will terminalte
1617
+ move_history(@history_pointer, line: :end, cursor: :start)
1694
1618
  @searching_prompt = nil
1695
1619
  @waiting_proc = nil
1696
- @byte_pointer = 0
1697
- else
1698
- chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
1699
- if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
1700
- search_word, prompt_name, hit_pointer = searcher.call(k)
1701
- Reline.last_incremental_search = search_word
1702
- @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1703
- @searching_prompt += ': ' unless @is_multiline
1704
- move_history(hit_pointer, line: :end, cursor: :end, save_buffer: false) if hit_pointer
1705
- else
1706
- if @history_pointer
1707
- line = Reline::HISTORY[@history_pointer]
1708
- else
1709
- line = @line_backup_in_history
1710
- end
1711
- @line_backup_in_history = whole_buffer
1712
- @buffer_of_lines = line.split("\n")
1713
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1714
- @line_index = @buffer_of_lines.size - 1
1715
- @searching_prompt = nil
1716
- @waiting_proc = nil
1717
- @byte_pointer = 0
1718
- end
1719
1620
  end
1720
1621
  }
1721
1622
  end
@@ -1770,14 +1671,14 @@ class Reline::LineEditor
1770
1671
  end
1771
1672
  alias_method :history_search_forward, :ed_search_next_history
1772
1673
 
1773
- private def move_history(history_pointer, line:, cursor:, save_buffer: true)
1674
+ private def move_history(history_pointer, line:, cursor:)
1774
1675
  history_pointer ||= Reline::HISTORY.size
1775
1676
  return if history_pointer < 0 || history_pointer > Reline::HISTORY.size
1776
1677
  old_history_pointer = @history_pointer || Reline::HISTORY.size
1777
1678
  if old_history_pointer == Reline::HISTORY.size
1778
- @line_backup_in_history = save_buffer ? whole_buffer : ''
1679
+ @line_backup_in_history = whole_buffer
1779
1680
  else
1780
- Reline::HISTORY[old_history_pointer] = whole_buffer if save_buffer
1681
+ Reline::HISTORY[old_history_pointer] = whole_buffer
1781
1682
  end
1782
1683
  if history_pointer == Reline::HISTORY.size
1783
1684
  buf = @line_backup_in_history
@@ -1787,7 +1688,7 @@ class Reline::LineEditor
1787
1688
  @history_pointer = history_pointer
1788
1689
  end
1789
1690
  @buffer_of_lines = buf.split("\n")
1790
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1691
+ @buffer_of_lines = [String.new(encoding: encoding)] if @buffer_of_lines.empty?
1791
1692
  @line_index = line == :start ? 0 : line == :end ? @buffer_of_lines.size - 1 : line
1792
1693
  @byte_pointer = cursor == :start ? 0 : cursor == :end ? current_line.bytesize : cursor
1793
1694
  end
@@ -1939,9 +1840,11 @@ class Reline::LineEditor
1939
1840
  if current_line.empty? or @byte_pointer < current_line.bytesize
1940
1841
  em_delete(key)
1941
1842
  elsif !@config.autocompletion # show completed list
1942
- result = call_completion_proc
1843
+ pre, target, post, quote = retrieve_completion_block
1844
+ result = call_completion_proc(pre, target, post, quote)
1943
1845
  if result.is_a?(Array)
1944
- perform_completion(result, true)
1846
+ candidates = filter_normalize_candidates(target, result)
1847
+ menu(candidates)
1945
1848
  end
1946
1849
  end
1947
1850
  end
@@ -1973,7 +1876,7 @@ class Reline::LineEditor
1973
1876
 
1974
1877
  private def em_next_word(key)
1975
1878
  if current_line.bytesize > @byte_pointer
1976
- byte_size, _ = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1879
+ byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1977
1880
  @byte_pointer += byte_size
1978
1881
  end
1979
1882
  end
@@ -1981,7 +1884,7 @@ class Reline::LineEditor
1981
1884
 
1982
1885
  private def ed_prev_word(key)
1983
1886
  if @byte_pointer > 0
1984
- byte_size, _ = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
1887
+ byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
1985
1888
  @byte_pointer -= byte_size
1986
1889
  end
1987
1890
  end
@@ -1989,7 +1892,7 @@ class Reline::LineEditor
1989
1892
 
1990
1893
  private def em_delete_next_word(key)
1991
1894
  if current_line.bytesize > @byte_pointer
1992
- byte_size, _ = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1895
+ byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1993
1896
  line, word = byteslice!(current_line, @byte_pointer, byte_size)
1994
1897
  set_current_line(line)
1995
1898
  @kill_ring.append(word)
@@ -1999,7 +1902,7 @@ class Reline::LineEditor
1999
1902
 
2000
1903
  private def ed_delete_prev_word(key)
2001
1904
  if @byte_pointer > 0
2002
- byte_size, _ = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
1905
+ byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
2003
1906
  line, word = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
2004
1907
  set_current_line(line, @byte_pointer - byte_size)
2005
1908
  @kill_ring.append(word, true)
@@ -2039,7 +1942,7 @@ class Reline::LineEditor
2039
1942
 
2040
1943
  private def em_capitol_case(key)
2041
1944
  if current_line.bytesize > @byte_pointer
2042
- byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(current_line, @byte_pointer)
1945
+ byte_size, new_str = Reline::Unicode.em_forward_word_with_capitalization(current_line, @byte_pointer)
2043
1946
  before = current_line.byteslice(0, @byte_pointer)
2044
1947
  after = current_line.byteslice((@byte_pointer + byte_size)..-1)
2045
1948
  set_current_line(before + new_str + after, @byte_pointer + new_str.bytesize)
@@ -2049,7 +1952,7 @@ class Reline::LineEditor
2049
1952
 
2050
1953
  private def em_lower_case(key)
2051
1954
  if current_line.bytesize > @byte_pointer
2052
- byte_size, = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1955
+ byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
2053
1956
  part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
2054
1957
  mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
2055
1958
  }.join
@@ -2062,7 +1965,7 @@ class Reline::LineEditor
2062
1965
 
2063
1966
  private def em_upper_case(key)
2064
1967
  if current_line.bytesize > @byte_pointer
2065
- byte_size, = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1968
+ byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
2066
1969
  part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
2067
1970
  mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
2068
1971
  }.join
@@ -2075,7 +1978,7 @@ class Reline::LineEditor
2075
1978
 
2076
1979
  private def em_kill_region(key)
2077
1980
  if @byte_pointer > 0
2078
- byte_size, _ = Reline::Unicode.em_big_backward_word(current_line, @byte_pointer)
1981
+ byte_size = Reline::Unicode.em_big_backward_word(current_line, @byte_pointer)
2079
1982
  line, deleted = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
2080
1983
  set_current_line(line, @byte_pointer - byte_size)
2081
1984
  @kill_ring.append(deleted, true)
@@ -2106,7 +2009,7 @@ class Reline::LineEditor
2106
2009
 
2107
2010
  private def vi_next_word(key, arg: 1)
2108
2011
  if current_line.bytesize > @byte_pointer
2109
- byte_size, _ = Reline::Unicode.vi_forward_word(current_line, @byte_pointer, @drop_terminate_spaces)
2012
+ byte_size = Reline::Unicode.vi_forward_word(current_line, @byte_pointer, @drop_terminate_spaces)
2110
2013
  @byte_pointer += byte_size
2111
2014
  end
2112
2015
  arg -= 1
@@ -2115,7 +2018,7 @@ class Reline::LineEditor
2115
2018
 
2116
2019
  private def vi_prev_word(key, arg: 1)
2117
2020
  if @byte_pointer > 0
2118
- byte_size, _ = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
2021
+ byte_size = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
2119
2022
  @byte_pointer -= byte_size
2120
2023
  end
2121
2024
  arg -= 1
@@ -2124,7 +2027,7 @@ class Reline::LineEditor
2124
2027
 
2125
2028
  private def vi_end_word(key, arg: 1, inclusive: false)
2126
2029
  if current_line.bytesize > @byte_pointer
2127
- byte_size, _ = Reline::Unicode.vi_forward_end_word(current_line, @byte_pointer)
2030
+ byte_size = Reline::Unicode.vi_forward_end_word(current_line, @byte_pointer)
2128
2031
  @byte_pointer += byte_size
2129
2032
  end
2130
2033
  arg -= 1
@@ -2139,7 +2042,7 @@ class Reline::LineEditor
2139
2042
 
2140
2043
  private def vi_next_big_word(key, arg: 1)
2141
2044
  if current_line.bytesize > @byte_pointer
2142
- byte_size, _ = Reline::Unicode.vi_big_forward_word(current_line, @byte_pointer)
2045
+ byte_size = Reline::Unicode.vi_big_forward_word(current_line, @byte_pointer)
2143
2046
  @byte_pointer += byte_size
2144
2047
  end
2145
2048
  arg -= 1
@@ -2148,7 +2051,7 @@ class Reline::LineEditor
2148
2051
 
2149
2052
  private def vi_prev_big_word(key, arg: 1)
2150
2053
  if @byte_pointer > 0
2151
- byte_size, _ = Reline::Unicode.vi_big_backward_word(current_line, @byte_pointer)
2054
+ byte_size = Reline::Unicode.vi_big_backward_word(current_line, @byte_pointer)
2152
2055
  @byte_pointer -= byte_size
2153
2056
  end
2154
2057
  arg -= 1
@@ -2157,7 +2060,7 @@ class Reline::LineEditor
2157
2060
 
2158
2061
  private def vi_end_big_word(key, arg: 1, inclusive: false)
2159
2062
  if current_line.bytesize > @byte_pointer
2160
- byte_size, _ = Reline::Unicode.vi_big_forward_end_word(current_line, @byte_pointer)
2063
+ byte_size = Reline::Unicode.vi_big_forward_end_word(current_line, @byte_pointer)
2161
2064
  @byte_pointer += byte_size
2162
2065
  end
2163
2066
  arg -= 1
@@ -2312,7 +2215,7 @@ class Reline::LineEditor
2312
2215
  }
2313
2216
  system("#{ENV['EDITOR']} #{path}")
2314
2217
  @buffer_of_lines = File.read(path).split("\n")
2315
- @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
2218
+ @buffer_of_lines = [String.new(encoding: encoding)] if @buffer_of_lines.empty?
2316
2219
  @line_index = 0
2317
2220
  finish
2318
2221
  end
@@ -2396,9 +2299,9 @@ class Reline::LineEditor
2396
2299
 
2397
2300
  private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
2398
2301
  if key.instance_of?(String)
2399
- inputed_char = key
2302
+ inputted_char = key
2400
2303
  else
2401
- inputed_char = key.chr
2304
+ inputted_char = key.chr
2402
2305
  end
2403
2306
  prev_total = nil
2404
2307
  total = nil
@@ -2410,7 +2313,7 @@ class Reline::LineEditor
2410
2313
  width = Reline::Unicode.get_mbchar_width(mbchar)
2411
2314
  total = [mbchar.bytesize, width]
2412
2315
  else
2413
- if inputed_char == mbchar
2316
+ if inputted_char == mbchar
2414
2317
  arg -= 1
2415
2318
  if arg.zero?
2416
2319
  found = true
@@ -2448,9 +2351,9 @@ class Reline::LineEditor
2448
2351
 
2449
2352
  private def search_prev_char(key, arg, need_next_char = false)
2450
2353
  if key.instance_of?(String)
2451
- inputed_char = key
2354
+ inputted_char = key
2452
2355
  else
2453
- inputed_char = key.chr
2356
+ inputted_char = key.chr
2454
2357
  end
2455
2358
  prev_total = nil
2456
2359
  total = nil
@@ -2462,7 +2365,7 @@ class Reline::LineEditor
2462
2365
  width = Reline::Unicode.get_mbchar_width(mbchar)
2463
2366
  total = [mbchar.bytesize, width]
2464
2367
  else
2465
- if inputed_char == mbchar
2368
+ if inputted_char == mbchar
2466
2369
  arg -= 1
2467
2370
  if arg.zero?
2468
2371
  found = true