reline 0.5.10 → 0.5.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -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