reline 0.5.11 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2e17ec5d6de83746c38c5e0764f7fa1ecda40f01c18b9e8cdf769e4bf2a155e
4
- data.tar.gz: 7ce06466fae4c8115bb507141998d322595b9e924dfc1ad3001871265088a680
3
+ metadata.gz: 14c0b8843530985ab335eb7dbb365d72708214a67b5a858d75d0aebe605050c3
4
+ data.tar.gz: ba60692647a18e520d1b8ed0fff6ac44b11048b3c8f2f2050a5651955db21c89
5
5
  SHA512:
6
- metadata.gz: 31e90ffea6ac235aad5c2c243fbd41163142f94e044ed9f4980860263fab97154857483755dcacd259fa9b578b1c29be5b878cd7f883f18e7b92380cb5865ef3
7
- data.tar.gz: 54b91a893c536b3e6b5c984c0fc2b053b8978ab586ae361446fb603c4eecfb0240a99c45b25e4996ff58d9ef15fa4e33bab5cef9cb4aef3dc7eab85998af3711
6
+ metadata.gz: 242efcf7910a3981d12f7238df77b20d8c0a4fffb735bc59d53f8662e8fbb149d53c8081adf03bbb2b237f576a6f942ba66f01d6b46e1de29617fb29edb89a58
7
+ data.tar.gz: 9d80fef9e52a79300e6163b9dd5141fe412fc3caa32f108f9158108d54abef6ec23b93fc018afd5efdf374c38713e0973afe06adbbe148cae457ca45dd7024b7
@@ -29,10 +29,6 @@ class Reline::ANSI < Reline::IO
29
29
  'H' => [:ed_move_to_beg, {}],
30
30
  }
31
31
 
32
- if Reline::Terminfo.enabled?
33
- Reline::Terminfo.setupterm(0, 2)
34
- end
35
-
36
32
  def initialize
37
33
  @input = STDIN
38
34
  @output = STDOUT
@@ -42,16 +38,15 @@ class Reline::ANSI < Reline::IO
42
38
 
43
39
  def encoding
44
40
  @input.external_encoding || Encoding.default_external
41
+ rescue IOError
42
+ # STDIN.external_encoding raises IOError in Ruby <= 3.0 when STDIN is closed
43
+ Encoding.default_external
45
44
  end
46
45
 
47
- def set_default_key_bindings(config, allow_terminfo: true)
46
+ def set_default_key_bindings(config)
48
47
  set_bracketed_paste_key_bindings(config)
49
48
  set_default_key_bindings_ansi_cursor(config)
50
- if allow_terminfo && Reline::Terminfo.enabled?
51
- set_default_key_bindings_terminfo(config)
52
- else
53
- set_default_key_bindings_comprehensive_list(config)
54
- end
49
+ set_default_key_bindings_comprehensive_list(config)
55
50
  {
56
51
  [27, 91, 90] => :completion_journey_up, # S-Tab
57
52
  }.each_pair do |key, func|
@@ -98,23 +93,6 @@ class Reline::ANSI < Reline::IO
98
93
  end
99
94
  end
100
95
 
101
- def set_default_key_bindings_terminfo(config)
102
- key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
103
- begin
104
- key_code = Reline::Terminfo.tigetstr(capname)
105
- [ key_code.bytes, key_binding ]
106
- rescue Reline::Terminfo::TerminfoError
107
- # capname is undefined
108
- end
109
- end.compact.to_h
110
-
111
- key_bindings.each_pair do |key, func|
112
- config.add_default_key_binding_by_keymap(:emacs, key, func)
113
- config.add_default_key_binding_by_keymap(:vi_insert, key, func)
114
- config.add_default_key_binding_by_keymap(:vi_command, key, func)
115
- end
116
- end
117
-
118
96
  def set_default_key_bindings_comprehensive_list(config)
119
97
  {
120
98
  # xterm
@@ -281,27 +259,11 @@ class Reline::ANSI < Reline::IO
281
259
  end
282
260
 
283
261
  def hide_cursor
284
- seq = "\e[?25l"
285
- if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
286
- begin
287
- seq = Reline::Terminfo.tigetstr('civis')
288
- rescue Reline::Terminfo::TerminfoError
289
- # civis is undefined
290
- end
291
- end
292
- @output.write seq
262
+ @output.write "\e[?25l"
293
263
  end
294
264
 
295
265
  def show_cursor
296
- seq = "\e[?25h"
297
- if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
298
- begin
299
- seq = Reline::Terminfo.tigetstr('cnorm')
300
- rescue Reline::Terminfo::TerminfoError
301
- # cnorm is undefined
302
- end
303
- end
304
- @output.write seq
266
+ @output.write "\e[?25h"
305
267
  end
306
268
 
307
269
  def erase_after_cursor
@@ -21,8 +21,11 @@ class Reline::Dumb < Reline::IO
21
21
  elsif RUBY_PLATFORM =~ /mswin|mingw/
22
22
  Encoding::UTF_8
23
23
  else
24
- @input.external_encoding || Encoding::default_external
24
+ @input.external_encoding || Encoding.default_external
25
25
  end
26
+ rescue IOError
27
+ # STDIN.external_encoding raises IOError in Ruby <= 3.0 when STDIN is closed
28
+ Encoding.default_external
26
29
  end
27
30
 
28
31
  def set_default_key_bindings(_)
@@ -3,8 +3,11 @@ class Reline::KeyStroke
3
3
  CSI_PARAMETER_BYTES_RANGE = 0x30..0x3f
4
4
  CSI_INTERMEDIATE_BYTES_RANGE = (0x20..0x2f)
5
5
 
6
- def initialize(config)
6
+ attr_accessor :encoding
7
+
8
+ def initialize(config, encoding)
7
9
  @config = config
10
+ @encoding = encoding
8
11
  end
9
12
 
10
13
  # Input exactly matches to a key sequence
@@ -21,7 +24,7 @@ class Reline::KeyStroke
21
24
  matched = key_mapping.get(input)
22
25
 
23
26
  # FIXME: Workaround for single byte. remove this after MAPPING is merged into KeyActor.
24
- matched ||= input.size == 1
27
+ matched ||= input.size == 1 && input[0] < 0x80
25
28
  matching ||= input == [ESC_BYTE]
26
29
 
27
30
  if matching && matched
@@ -32,10 +35,14 @@ class Reline::KeyStroke
32
35
  MATCHED
33
36
  elsif input[0] == ESC_BYTE
34
37
  match_unknown_escape_sequence(input, vi_mode: @config.editing_mode_is?(:vi_insert, :vi_command))
35
- elsif input.size == 1
36
- MATCHED
37
38
  else
38
- UNMATCHED
39
+ s = input.pack('c*').force_encoding(@encoding)
40
+ if s.valid_encoding?
41
+ s.size == 1 ? MATCHED : UNMATCHED
42
+ else
43
+ # Invalid string is MATCHING (part of valid string) or MATCHED (invalid bytes to be ignored)
44
+ MATCHING_MATCHED
45
+ end
39
46
  end
40
47
  end
41
48
 
@@ -45,6 +52,7 @@ class Reline::KeyStroke
45
52
  bytes = input.take(i)
46
53
  status = match_status(bytes)
47
54
  matched_bytes = bytes if status == MATCHED || status == MATCHING_MATCHED
55
+ break if status == MATCHED || status == UNMATCHED
48
56
  end
49
57
  return [[], []] unless matched_bytes
50
58
 
@@ -53,12 +61,15 @@ class Reline::KeyStroke
53
61
  keys = func.map { |c| Reline::Key.new(c, c, false) }
54
62
  elsif func
55
63
  keys = [Reline::Key.new(func, func, false)]
56
- elsif matched_bytes.size == 1
57
- keys = [Reline::Key.new(matched_bytes.first, matched_bytes.first, false)]
58
64
  elsif matched_bytes.size == 2 && matched_bytes[0] == ESC_BYTE
59
65
  keys = [Reline::Key.new(matched_bytes[1], matched_bytes[1] | 0b10000000, true)]
60
66
  else
61
- keys = []
67
+ s = matched_bytes.pack('c*').force_encoding(@encoding)
68
+ if s.valid_encoding? && s.size == 1
69
+ keys = [Reline::Key.new(s.ord, s.ord, false)]
70
+ else
71
+ keys = []
72
+ end
62
73
  end
63
74
 
64
75
  [keys, input.drop(matched_bytes.size)]
@@ -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
@@ -266,7 +265,6 @@ class Reline::LineEditor
266
265
  @line_index = 0
267
266
  @cache.clear
268
267
  @line_backup_in_history = nil
269
- @multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
270
268
  end
271
269
 
272
270
  def multiline_on
@@ -300,8 +298,8 @@ class Reline::LineEditor
300
298
  end
301
299
  end
302
300
 
303
- private def split_by_width(str, max_width, offset: 0)
304
- 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)
305
303
  end
306
304
 
307
305
  def current_byte_pointer_cursor
@@ -391,8 +389,8 @@ class Reline::LineEditor
391
389
  if (cached = cached_wraps[[prompt, line]])
392
390
  next cached
393
391
  end
394
- *wrapped_prompts, code_line_prompt = split_by_width(prompt, width).first.compact
395
- 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))
396
394
  wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
397
395
  end
398
396
  end
@@ -440,7 +438,7 @@ class Reline::LineEditor
440
438
  def wrapped_cursor_position
441
439
  prompt_width = calculate_width(prompt_list[@line_index], true)
442
440
  line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
443
- 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)
444
442
  wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
445
443
  wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
446
444
  [wrapped_cursor_x, wrapped_cursor_y]
@@ -465,7 +463,7 @@ class Reline::LineEditor
465
463
  render_differential([], 0, 0)
466
464
  lines = @buffer_of_lines.size.times.map do |i|
467
465
  line = Reline::Unicode.strip_non_printing_start_end(prompt_list[i]) + modified_lines[i]
468
- wrapped_lines, = split_by_width(line, screen_width)
466
+ wrapped_lines = split_line_by_width(line, screen_width)
469
467
  wrapped_lines.last.empty? ? "#{line} " : line
470
468
  end
471
469
  @output.puts lines.map { |l| "#{l}\r\n" }.join
@@ -579,8 +577,9 @@ class Reline::LineEditor
579
577
  @context
580
578
  end
581
579
 
582
- def retrieve_completion_block(set_completion_quote_character = false)
583
- @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]
584
583
  end
585
584
 
586
585
  def call_completion_proc_with_checking_args(pre, target, post)
@@ -800,105 +799,73 @@ class Reline::LineEditor
800
799
  @config.editing_mode
801
800
  end
802
801
 
803
- private def menu(_target, list)
802
+ private def menu(list)
804
803
  @menu_info = MenuInfo.new(list)
805
804
  end
806
805
 
807
- private def complete_internal_proc(list, is_menu)
808
- preposing, target, postposing = retrieve_completion_block
809
- candidates = list.select { |i|
810
- if i and not Encoding.compatible?(target.encoding, i.encoding)
811
- 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}"
812
815
  end
816
+
813
817
  if @config.completion_ignore_case
814
- i&.downcase&.start_with?(target.downcase)
818
+ item.downcase.start_with?(target)
815
819
  else
816
- i&.start_with?(target)
820
+ item.start_with?(target)
817
821
  end
818
- }.uniq
819
- if is_menu
820
- menu(target, candidates)
821
- return nil
822
- end
823
- completed = candidates.inject { |memo, item|
824
- begin
825
- memo_mbchars = memo.unicode_normalize.grapheme_clusters
826
- item_mbchars = item.unicode_normalize.grapheme_clusters
827
- rescue Encoding::CompatibilityError
828
- memo_mbchars = memo.grapheme_clusters
829
- item_mbchars = item.grapheme_clusters
830
- end
831
- size = [memo_mbchars.size, item_mbchars.size].min
832
- result = +''
833
- size.times do |i|
834
- if @config.completion_ignore_case
835
- if memo_mbchars[i].casecmp?(item_mbchars[i])
836
- result << memo_mbchars[i]
837
- else
838
- break
839
- end
840
- else
841
- if memo_mbchars[i] == item_mbchars[i]
842
- result << memo_mbchars[i]
843
- else
844
- break
845
- end
846
- end
847
- end
848
- result
849
- }
850
-
851
- [target, preposing, completed, postposing, candidates]
822
+ end.map do |item|
823
+ item.unicode_normalize
824
+ rescue Encoding::CompatibilityError
825
+ item
826
+ end.uniq
852
827
  end
853
828
 
854
- 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
+
855
832
  case @completion_state
856
- when CompletionState::NORMAL
857
- @completion_state = CompletionState::COMPLETION
858
833
  when CompletionState::PERFECT_MATCH
859
834
  if @dig_perfect_match_proc
860
- @dig_perfect_match_proc.(@perfect_matched)
861
- else
862
- @completion_state = CompletionState::COMPLETION
835
+ @dig_perfect_match_proc.call(@perfect_matched)
836
+ return
863
837
  end
864
- end
865
- if just_show_list
866
- is_menu = true
867
- elsif @completion_state == CompletionState::MENU
868
- is_menu = true
869
- elsif @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
870
- is_menu = true
871
- else
872
- is_menu = false
873
- end
874
- result = complete_internal_proc(list, is_menu)
875
- if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
838
+ when CompletionState::MENU
839
+ menu(candidates)
840
+ return
841
+ when CompletionState::MENU_WITH_PERFECT_MATCH
842
+ menu(candidates)
876
843
  @completion_state = CompletionState::PERFECT_MATCH
844
+ return
877
845
  end
878
- return if result.nil?
879
- target, preposing, completed, postposing, candidates = result
880
- return if completed.nil?
881
- if target <= completed and (@completion_state == CompletionState::COMPLETION)
882
- append_character = ''
883
- if candidates.include?(completed)
884
- if candidates.one?
885
- append_character = completion_append_character.to_s
886
- @completion_state = CompletionState::PERFECT_MATCH
887
- else
888
- @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
889
- perform_completion(candidates, true) if @config.show_all_if_ambiguous
890
- end
891
- @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
892
858
  else
893
- @completion_state = CompletionState::MENU
894
- perform_completion(candidates, true) if @config.show_all_if_ambiguous
895
- end
896
- unless just_show_list
897
- @buffer_of_lines[@line_index] = (preposing + completed + append_character + postposing).split("\n")[@line_index] || String.new(encoding: encoding)
898
- line_to_pointer = (preposing + completed + append_character).split("\n")[@line_index] || String.new(encoding: encoding)
899
- @byte_pointer = line_to_pointer.bytesize
859
+ @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
900
860
  end
861
+ @perfect_matched = completed
862
+ else
863
+ @completion_state = CompletionState::MENU
864
+ menu(candidates) if @config.show_all_if_ambiguous
901
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
902
869
  end
903
870
 
904
871
  def dialog_proc_scope_completion_journey_data
@@ -927,8 +894,8 @@ class Reline::LineEditor
927
894
  end
928
895
 
929
896
  private def retrieve_completion_journey_state
930
- preposing, target, postposing = retrieve_completion_block
931
- list = call_completion_proc
897
+ preposing, target, postposing, quote = retrieve_completion_block
898
+ list = call_completion_proc(preposing, target, postposing, quote)
932
899
  return unless list.is_a?(Array)
933
900
 
934
901
  candidates = list.select{ |item| item.start_with?(target) }
@@ -1068,20 +1035,11 @@ class Reline::LineEditor
1068
1035
  end
1069
1036
 
1070
1037
  private def normal_char(key)
1071
- @multibyte_buffer << key.combined_char
1072
- if @multibyte_buffer.size > 1
1073
- if @multibyte_buffer.dup.force_encoding(encoding).valid_encoding?
1074
- process_key(@multibyte_buffer.dup.force_encoding(encoding), nil)
1075
- @multibyte_buffer.clear
1076
- else
1077
- # invalid
1078
- return
1079
- end
1080
- else # single byte
1081
- return if key.char >= 128 # maybe, first byte of multi byte
1038
+ if key.char < 0x80
1082
1039
  method_symbol = @config.editing_mode.get_method(key.combined_char)
1083
1040
  process_key(key.combined_char, method_symbol)
1084
- @multibyte_buffer.clear
1041
+ else
1042
+ process_key(key.char.chr(encoding), nil)
1085
1043
  end
1086
1044
  if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
1087
1045
  byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
@@ -1178,9 +1136,8 @@ class Reline::LineEditor
1178
1136
  end
1179
1137
  end
1180
1138
 
1181
- def call_completion_proc
1182
- result = retrieve_completion_block(true)
1183
- pre, target, post = result
1139
+ def call_completion_proc(pre, target, post, quote)
1140
+ Reline.core.instance_variable_set(:@completion_quote_character, quote)
1184
1141
  result = call_completion_proc_with_checking_args(pre, target, post)
1185
1142
  Reline.core.instance_variable_set(:@completion_quote_character, nil)
1186
1143
  result
@@ -1256,72 +1213,32 @@ class Reline::LineEditor
1256
1213
  process_auto_indent
1257
1214
  end
1258
1215
 
1259
- def retrieve_completion_block(set_completion_quote_character = false)
1260
- if Reline.completer_word_break_characters.empty?
1261
- word_break_regexp = nil
1262
- else
1263
- word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
1264
- end
1265
- if Reline.completer_quote_characters.empty?
1266
- quote_characters_regexp = nil
1267
- else
1268
- quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
1269
- end
1270
- before = current_line.byteslice(0, @byte_pointer)
1271
- rest = nil
1272
- 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
1273
1219
  quote = nil
1274
- closing_quote = nil
1275
- escaped_quote = nil
1276
- i = 0
1277
- while i < @byte_pointer do
1278
- slice = current_line.byteslice(i, @byte_pointer - i)
1279
- unless slice.valid_encoding?
1280
- i += 1
1281
- next
1282
- end
1283
- if quote and slice.start_with?(closing_quote)
1284
- quote = nil
1285
- i += 1
1286
- rest = nil
1287
- elsif quote and slice.start_with?(escaped_quote)
1288
- # skip
1289
- i += 2
1290
- elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
1291
- rest = $'
1292
- quote = $&
1293
- closing_quote = /(?!\\)#{Regexp.escape(quote)}/
1294
- escaped_quote = /\\#{Regexp.escape(quote)}/
1295
- i += 1
1296
- break_pointer = i - 1
1297
- elsif word_break_regexp and not quote and slice =~ word_break_regexp
1298
- rest = $'
1299
- i += 1
1300
- before = current_line.byteslice(i, @byte_pointer - i)
1301
- break_pointer = i
1302
- else
1303
- i += 1
1304
- end
1305
- end
1306
- postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
1307
- if rest
1308
- preposing = current_line.byteslice(0, break_pointer)
1309
- target = rest
1310
- if set_completion_quote_character and quote
1311
- Reline.core.instance_variable_set(:@completion_quote_character, quote)
1312
- if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
1313
- 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
1314
1233
  end
1315
1234
  end
1316
- else
1317
- preposing = ''
1318
- if break_pointer
1319
- preposing = current_line.byteslice(0, break_pointer)
1320
- else
1321
- preposing = ''
1322
- end
1323
- target = before
1324
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)
1325
1242
  lines = whole_lines
1326
1243
  if @line_index > 0
1327
1244
  preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
@@ -1329,7 +1246,7 @@ class Reline::LineEditor
1329
1246
  if (lines.size - 1) > @line_index
1330
1247
  postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
1331
1248
  end
1332
- [preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding)]
1249
+ [preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding), quote&.encode(encoding)]
1333
1250
  end
1334
1251
 
1335
1252
  def confirm_multiline_termination
@@ -1460,10 +1377,11 @@ class Reline::LineEditor
1460
1377
  @completion_occurs = move_completed_list(:down)
1461
1378
  else
1462
1379
  @completion_journey_state = nil
1463
- result = call_completion_proc
1380
+ pre, target, post, quote = retrieve_completion_block
1381
+ result = call_completion_proc(pre, target, post, quote)
1464
1382
  if result.is_a?(Array)
1465
1383
  @completion_occurs = true
1466
- perform_completion(result, false)
1384
+ perform_completion(pre, target, post, quote, result)
1467
1385
  end
1468
1386
  end
1469
1387
  end
@@ -1582,7 +1500,7 @@ class Reline::LineEditor
1582
1500
  alias_method :backward_char, :ed_prev_char
1583
1501
 
1584
1502
  private def vi_first_print(key)
1585
- @byte_pointer, = Reline::Unicode.vi_first_print(current_line)
1503
+ @byte_pointer = Reline::Unicode.vi_first_print(current_line)
1586
1504
  end
1587
1505
 
1588
1506
  private def ed_move_to_beg(key)
@@ -1598,7 +1516,6 @@ class Reline::LineEditor
1598
1516
 
1599
1517
  private def generate_searcher(search_key)
1600
1518
  search_word = String.new(encoding: encoding)
1601
- multibyte_buf = String.new(encoding: 'ASCII-8BIT')
1602
1519
  hit_pointer = nil
1603
1520
  lambda do |key|
1604
1521
  search_again = false
@@ -1613,11 +1530,7 @@ class Reline::LineEditor
1613
1530
  search_again = true if search_key == key
1614
1531
  search_key = key
1615
1532
  else
1616
- multibyte_buf << key
1617
- if multibyte_buf.dup.force_encoding(encoding).valid_encoding?
1618
- search_word << multibyte_buf.dup.force_encoding(encoding)
1619
- multibyte_buf.clear
1620
- end
1533
+ search_word << key
1621
1534
  end
1622
1535
  hit = nil
1623
1536
  if not search_word.empty? and @line_backup_in_history&.include?(search_word)
@@ -1927,9 +1840,11 @@ class Reline::LineEditor
1927
1840
  if current_line.empty? or @byte_pointer < current_line.bytesize
1928
1841
  em_delete(key)
1929
1842
  elsif !@config.autocompletion # show completed list
1930
- result = call_completion_proc
1843
+ pre, target, post, quote = retrieve_completion_block
1844
+ result = call_completion_proc(pre, target, post, quote)
1931
1845
  if result.is_a?(Array)
1932
- perform_completion(result, true)
1846
+ candidates = filter_normalize_candidates(target, result)
1847
+ menu(candidates)
1933
1848
  end
1934
1849
  end
1935
1850
  end
@@ -1961,7 +1876,7 @@ class Reline::LineEditor
1961
1876
 
1962
1877
  private def em_next_word(key)
1963
1878
  if current_line.bytesize > @byte_pointer
1964
- byte_size, _ = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1879
+ byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1965
1880
  @byte_pointer += byte_size
1966
1881
  end
1967
1882
  end
@@ -1969,7 +1884,7 @@ class Reline::LineEditor
1969
1884
 
1970
1885
  private def ed_prev_word(key)
1971
1886
  if @byte_pointer > 0
1972
- byte_size, _ = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
1887
+ byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
1973
1888
  @byte_pointer -= byte_size
1974
1889
  end
1975
1890
  end
@@ -1977,7 +1892,7 @@ class Reline::LineEditor
1977
1892
 
1978
1893
  private def em_delete_next_word(key)
1979
1894
  if current_line.bytesize > @byte_pointer
1980
- byte_size, _ = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1895
+ byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1981
1896
  line, word = byteslice!(current_line, @byte_pointer, byte_size)
1982
1897
  set_current_line(line)
1983
1898
  @kill_ring.append(word)
@@ -1987,7 +1902,7 @@ class Reline::LineEditor
1987
1902
 
1988
1903
  private def ed_delete_prev_word(key)
1989
1904
  if @byte_pointer > 0
1990
- byte_size, _ = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
1905
+ byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
1991
1906
  line, word = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
1992
1907
  set_current_line(line, @byte_pointer - byte_size)
1993
1908
  @kill_ring.append(word, true)
@@ -2027,7 +1942,7 @@ class Reline::LineEditor
2027
1942
 
2028
1943
  private def em_capitol_case(key)
2029
1944
  if current_line.bytesize > @byte_pointer
2030
- 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)
2031
1946
  before = current_line.byteslice(0, @byte_pointer)
2032
1947
  after = current_line.byteslice((@byte_pointer + byte_size)..-1)
2033
1948
  set_current_line(before + new_str + after, @byte_pointer + new_str.bytesize)
@@ -2037,7 +1952,7 @@ class Reline::LineEditor
2037
1952
 
2038
1953
  private def em_lower_case(key)
2039
1954
  if current_line.bytesize > @byte_pointer
2040
- byte_size, = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1955
+ byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
2041
1956
  part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
2042
1957
  mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
2043
1958
  }.join
@@ -2050,7 +1965,7 @@ class Reline::LineEditor
2050
1965
 
2051
1966
  private def em_upper_case(key)
2052
1967
  if current_line.bytesize > @byte_pointer
2053
- byte_size, = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
1968
+ byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
2054
1969
  part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
2055
1970
  mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
2056
1971
  }.join
@@ -2063,7 +1978,7 @@ class Reline::LineEditor
2063
1978
 
2064
1979
  private def em_kill_region(key)
2065
1980
  if @byte_pointer > 0
2066
- 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)
2067
1982
  line, deleted = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
2068
1983
  set_current_line(line, @byte_pointer - byte_size)
2069
1984
  @kill_ring.append(deleted, true)
@@ -2094,7 +2009,7 @@ class Reline::LineEditor
2094
2009
 
2095
2010
  private def vi_next_word(key, arg: 1)
2096
2011
  if current_line.bytesize > @byte_pointer
2097
- 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)
2098
2013
  @byte_pointer += byte_size
2099
2014
  end
2100
2015
  arg -= 1
@@ -2103,7 +2018,7 @@ class Reline::LineEditor
2103
2018
 
2104
2019
  private def vi_prev_word(key, arg: 1)
2105
2020
  if @byte_pointer > 0
2106
- byte_size, _ = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
2021
+ byte_size = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
2107
2022
  @byte_pointer -= byte_size
2108
2023
  end
2109
2024
  arg -= 1
@@ -2112,7 +2027,7 @@ class Reline::LineEditor
2112
2027
 
2113
2028
  private def vi_end_word(key, arg: 1, inclusive: false)
2114
2029
  if current_line.bytesize > @byte_pointer
2115
- 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)
2116
2031
  @byte_pointer += byte_size
2117
2032
  end
2118
2033
  arg -= 1
@@ -2127,7 +2042,7 @@ class Reline::LineEditor
2127
2042
 
2128
2043
  private def vi_next_big_word(key, arg: 1)
2129
2044
  if current_line.bytesize > @byte_pointer
2130
- 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)
2131
2046
  @byte_pointer += byte_size
2132
2047
  end
2133
2048
  arg -= 1
@@ -2136,7 +2051,7 @@ class Reline::LineEditor
2136
2051
 
2137
2052
  private def vi_prev_big_word(key, arg: 1)
2138
2053
  if @byte_pointer > 0
2139
- 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)
2140
2055
  @byte_pointer -= byte_size
2141
2056
  end
2142
2057
  arg -= 1
@@ -2145,7 +2060,7 @@ class Reline::LineEditor
2145
2060
 
2146
2061
  private def vi_end_big_word(key, arg: 1, inclusive: false)
2147
2062
  if current_line.bytesize > @byte_pointer
2148
- 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)
2149
2064
  @byte_pointer += byte_size
2150
2065
  end
2151
2066
  arg -= 1
@@ -121,9 +121,14 @@ class Reline::Unicode
121
121
  end
122
122
  end
123
123
 
124
- def self.split_by_width(str, max_width, encoding = str.encoding, offset: 0)
124
+ # This method is used by IRB
125
+ def self.split_by_width(str, max_width)
126
+ lines = split_line_by_width(str, max_width)
127
+ [lines, lines.size]
128
+ end
129
+
130
+ def self.split_line_by_width(str, max_width, encoding = str.encoding, offset: 0)
125
131
  lines = [String.new(encoding: encoding)]
126
- height = 1
127
132
  width = offset
128
133
  rest = str.encode(Encoding::UTF_8)
129
134
  in_zero_width = false
@@ -151,9 +156,7 @@ class Reline::Unicode
151
156
  mbchar_width = get_mbchar_width(gc)
152
157
  if (width += mbchar_width) > max_width
153
158
  width = mbchar_width
154
- lines << nil
155
159
  lines << seq.dup
156
- height += 1
157
160
  end
158
161
  end
159
162
  lines.last << gc
@@ -161,11 +164,9 @@ class Reline::Unicode
161
164
  end
162
165
  # The cursor moves to next line in first
163
166
  if width == max_width
164
- lines << nil
165
167
  lines << String.new(encoding: encoding)
166
- height += 1
167
168
  end
168
- [lines, height]
169
+ lines
169
170
  end
170
171
 
171
172
  def self.strip_non_printing_start_end(prompt)
@@ -261,27 +262,23 @@ class Reline::Unicode
261
262
  end
262
263
 
263
264
  def self.em_forward_word(line, byte_pointer)
264
- width = 0
265
265
  byte_size = 0
266
266
  while line.bytesize > (byte_pointer + byte_size)
267
267
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
268
268
  mbchar = line.byteslice(byte_pointer + byte_size, size)
269
269
  break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
270
- width += get_mbchar_width(mbchar)
271
270
  byte_size += size
272
271
  end
273
272
  while line.bytesize > (byte_pointer + byte_size)
274
273
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
275
274
  mbchar = line.byteslice(byte_pointer + byte_size, size)
276
275
  break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
277
- width += get_mbchar_width(mbchar)
278
276
  byte_size += size
279
277
  end
280
- [byte_size, width]
278
+ byte_size
281
279
  end
282
280
 
283
281
  def self.em_forward_word_with_capitalization(line, byte_pointer)
284
- width = 0
285
282
  byte_size = 0
286
283
  new_str = String.new
287
284
  while line.bytesize > (byte_pointer + byte_size)
@@ -289,7 +286,6 @@ class Reline::Unicode
289
286
  mbchar = line.byteslice(byte_pointer + byte_size, size)
290
287
  break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
291
288
  new_str += mbchar
292
- width += get_mbchar_width(mbchar)
293
289
  byte_size += size
294
290
  end
295
291
  first = true
@@ -303,50 +299,43 @@ class Reline::Unicode
303
299
  else
304
300
  new_str += mbchar.downcase
305
301
  end
306
- width += get_mbchar_width(mbchar)
307
302
  byte_size += size
308
303
  end
309
- [byte_size, width, new_str]
304
+ [byte_size, new_str]
310
305
  end
311
306
 
312
307
  def self.em_backward_word(line, byte_pointer)
313
- width = 0
314
308
  byte_size = 0
315
309
  while 0 < (byte_pointer - byte_size)
316
310
  size = get_prev_mbchar_size(line, byte_pointer - byte_size)
317
311
  mbchar = line.byteslice(byte_pointer - byte_size - size, size)
318
312
  break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
319
- width += get_mbchar_width(mbchar)
320
313
  byte_size += size
321
314
  end
322
315
  while 0 < (byte_pointer - byte_size)
323
316
  size = get_prev_mbchar_size(line, byte_pointer - byte_size)
324
317
  mbchar = line.byteslice(byte_pointer - byte_size - size, size)
325
318
  break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
326
- width += get_mbchar_width(mbchar)
327
319
  byte_size += size
328
320
  end
329
- [byte_size, width]
321
+ byte_size
330
322
  end
331
323
 
332
324
  def self.em_big_backward_word(line, byte_pointer)
333
- width = 0
334
325
  byte_size = 0
335
326
  while 0 < (byte_pointer - byte_size)
336
327
  size = get_prev_mbchar_size(line, byte_pointer - byte_size)
337
328
  mbchar = line.byteslice(byte_pointer - byte_size - size, size)
338
329
  break if mbchar =~ /\S/
339
- width += get_mbchar_width(mbchar)
340
330
  byte_size += size
341
331
  end
342
332
  while 0 < (byte_pointer - byte_size)
343
333
  size = get_prev_mbchar_size(line, byte_pointer - byte_size)
344
334
  mbchar = line.byteslice(byte_pointer - byte_size - size, size)
345
335
  break if mbchar =~ /\s/
346
- width += get_mbchar_width(mbchar)
347
336
  byte_size += size
348
337
  end
349
- [byte_size, width]
338
+ byte_size
350
339
  end
351
340
 
352
341
  def self.ed_transpose_words(line, byte_pointer)
@@ -451,73 +440,61 @@ class Reline::Unicode
451
440
  end
452
441
 
453
442
  def self.vi_big_forward_word(line, byte_pointer)
454
- width = 0
455
443
  byte_size = 0
456
444
  while (line.bytesize - 1) > (byte_pointer + byte_size)
457
445
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
458
446
  mbchar = line.byteslice(byte_pointer + byte_size, size)
459
447
  break if mbchar =~ /\s/
460
- width += get_mbchar_width(mbchar)
461
448
  byte_size += size
462
449
  end
463
450
  while (line.bytesize - 1) > (byte_pointer + byte_size)
464
451
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
465
452
  mbchar = line.byteslice(byte_pointer + byte_size, size)
466
453
  break if mbchar =~ /\S/
467
- width += get_mbchar_width(mbchar)
468
454
  byte_size += size
469
455
  end
470
- [byte_size, width]
456
+ byte_size
471
457
  end
472
458
 
473
459
  def self.vi_big_forward_end_word(line, byte_pointer)
474
460
  if (line.bytesize - 1) > byte_pointer
475
461
  size = get_next_mbchar_size(line, byte_pointer)
476
- mbchar = line.byteslice(byte_pointer, size)
477
- width = get_mbchar_width(mbchar)
478
462
  byte_size = size
479
463
  else
480
- return [0, 0]
464
+ return 0
481
465
  end
482
466
  while (line.bytesize - 1) > (byte_pointer + byte_size)
483
467
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
484
468
  mbchar = line.byteslice(byte_pointer + byte_size, size)
485
469
  break if mbchar =~ /\S/
486
- width += get_mbchar_width(mbchar)
487
470
  byte_size += size
488
471
  end
489
- prev_width = width
490
472
  prev_byte_size = byte_size
491
473
  while line.bytesize > (byte_pointer + byte_size)
492
474
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
493
475
  mbchar = line.byteslice(byte_pointer + byte_size, size)
494
476
  break if mbchar =~ /\s/
495
- prev_width = width
496
477
  prev_byte_size = byte_size
497
- width += get_mbchar_width(mbchar)
498
478
  byte_size += size
499
479
  end
500
- [prev_byte_size, prev_width]
480
+ prev_byte_size
501
481
  end
502
482
 
503
483
  def self.vi_big_backward_word(line, byte_pointer)
504
- width = 0
505
484
  byte_size = 0
506
485
  while 0 < (byte_pointer - byte_size)
507
486
  size = get_prev_mbchar_size(line, byte_pointer - byte_size)
508
487
  mbchar = line.byteslice(byte_pointer - byte_size - size, size)
509
488
  break if mbchar =~ /\S/
510
- width += get_mbchar_width(mbchar)
511
489
  byte_size += size
512
490
  end
513
491
  while 0 < (byte_pointer - byte_size)
514
492
  size = get_prev_mbchar_size(line, byte_pointer - byte_size)
515
493
  mbchar = line.byteslice(byte_pointer - byte_size - size, size)
516
494
  break if mbchar =~ /\s/
517
- width += get_mbchar_width(mbchar)
518
495
  byte_size += size
519
496
  end
520
- [byte_size, width]
497
+ byte_size
521
498
  end
522
499
 
523
500
  def self.vi_forward_word(line, byte_pointer, drop_terminate_spaces = false)
@@ -531,10 +508,9 @@ class Reline::Unicode
531
508
  else
532
509
  started_by = :non_word_printable
533
510
  end
534
- width = get_mbchar_width(mbchar)
535
511
  byte_size = size
536
512
  else
537
- return [0, 0]
513
+ return 0
538
514
  end
539
515
  while line.bytesize > (byte_pointer + byte_size)
540
516
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
@@ -547,18 +523,16 @@ class Reline::Unicode
547
523
  when :non_word_printable
548
524
  break if mbchar =~ /\w|\s/
549
525
  end
550
- width += get_mbchar_width(mbchar)
551
526
  byte_size += size
552
527
  end
553
- return [byte_size, width] if drop_terminate_spaces
528
+ return byte_size if drop_terminate_spaces
554
529
  while line.bytesize > (byte_pointer + byte_size)
555
530
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
556
531
  mbchar = line.byteslice(byte_pointer + byte_size, size)
557
532
  break if mbchar =~ /\S/
558
- width += get_mbchar_width(mbchar)
559
533
  byte_size += size
560
534
  end
561
- [byte_size, width]
535
+ byte_size
562
536
  end
563
537
 
564
538
  def self.vi_forward_end_word(line, byte_pointer)
@@ -572,10 +546,9 @@ class Reline::Unicode
572
546
  else
573
547
  started_by = :non_word_printable
574
548
  end
575
- width = get_mbchar_width(mbchar)
576
549
  byte_size = size
577
550
  else
578
- return [0, 0]
551
+ return 0
579
552
  end
580
553
  if (line.bytesize - 1) > (byte_pointer + byte_size)
581
554
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
@@ -587,13 +560,11 @@ class Reline::Unicode
587
560
  else
588
561
  second = :non_word_printable
589
562
  end
590
- second_width = get_mbchar_width(mbchar)
591
563
  second_byte_size = size
592
564
  else
593
- return [byte_size, width]
565
+ return byte_size
594
566
  end
595
567
  if second == :space
596
- width += second_width
597
568
  byte_size += second_byte_size
598
569
  while (line.bytesize - 1) > (byte_pointer + byte_size)
599
570
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
@@ -606,7 +577,6 @@ class Reline::Unicode
606
577
  end
607
578
  break
608
579
  end
609
- width += get_mbchar_width(mbchar)
610
580
  byte_size += size
611
581
  end
612
582
  else
@@ -614,12 +584,10 @@ class Reline::Unicode
614
584
  when [:word, :non_word_printable], [:non_word_printable, :word]
615
585
  started_by = second
616
586
  else
617
- width += second_width
618
587
  byte_size += second_byte_size
619
588
  started_by = second
620
589
  end
621
590
  end
622
- prev_width = width
623
591
  prev_byte_size = byte_size
624
592
  while line.bytesize > (byte_pointer + byte_size)
625
593
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
@@ -630,16 +598,13 @@ class Reline::Unicode
630
598
  when :non_word_printable
631
599
  break if mbchar =~ /[\w\s]/
632
600
  end
633
- prev_width = width
634
601
  prev_byte_size = byte_size
635
- width += get_mbchar_width(mbchar)
636
602
  byte_size += size
637
603
  end
638
- [prev_byte_size, prev_width]
604
+ prev_byte_size
639
605
  end
640
606
 
641
607
  def self.vi_backward_word(line, byte_pointer)
642
- width = 0
643
608
  byte_size = 0
644
609
  while 0 < (byte_pointer - byte_size)
645
610
  size = get_prev_mbchar_size(line, byte_pointer - byte_size)
@@ -652,7 +617,6 @@ class Reline::Unicode
652
617
  end
653
618
  break
654
619
  end
655
- width += get_mbchar_width(mbchar)
656
620
  byte_size += size
657
621
  end
658
622
  while 0 < (byte_pointer - byte_size)
@@ -664,14 +628,25 @@ class Reline::Unicode
664
628
  when :non_word_printable
665
629
  break if mbchar =~ /[\w\s]/
666
630
  end
667
- width += get_mbchar_width(mbchar)
668
631
  byte_size += size
669
632
  end
670
- [byte_size, width]
633
+ byte_size
634
+ end
635
+
636
+ def self.common_prefix(list, ignore_case: false)
637
+ return '' if list.empty?
638
+
639
+ common_prefix_gcs = list.first.grapheme_clusters
640
+ list.each do |item|
641
+ gcs = item.grapheme_clusters
642
+ common_prefix_gcs = common_prefix_gcs.take_while.with_index do |gc, i|
643
+ ignore_case ? gc.casecmp?(gcs[i]) : gc == gcs[i]
644
+ end
645
+ end
646
+ common_prefix_gcs.join
671
647
  end
672
648
 
673
649
  def self.vi_first_print(line)
674
- width = 0
675
650
  byte_size = 0
676
651
  while (line.bytesize - 1) > byte_size
677
652
  size = get_next_mbchar_size(line, byte_size)
@@ -679,9 +654,8 @@ class Reline::Unicode
679
654
  if mbchar =~ /\S/
680
655
  break
681
656
  end
682
- width += get_mbchar_width(mbchar)
683
657
  byte_size += size
684
658
  end
685
- [byte_size, width]
659
+ byte_size
686
660
  end
687
661
  end
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.5.11'
2
+ VERSION = '0.5.12'
3
3
  end
data/lib/reline.rb CHANGED
@@ -6,7 +6,6 @@ require 'reline/key_actor'
6
6
  require 'reline/key_stroke'
7
7
  require 'reline/line_editor'
8
8
  require 'reline/history'
9
- require 'reline/terminfo'
10
9
  require 'reline/io'
11
10
  require 'reline/face'
12
11
  require 'rbconfig'
@@ -308,6 +307,7 @@ module Reline
308
307
  otio = io_gate.prep
309
308
 
310
309
  may_req_ambiguous_char_width
310
+ key_stroke.encoding = encoding
311
311
  line_editor.reset(prompt)
312
312
  if multiline
313
313
  line_editor.multiline_on
@@ -486,7 +486,7 @@ module Reline
486
486
  def self.core
487
487
  @core ||= Core.new { |core|
488
488
  core.config = Reline::Config.new
489
- core.key_stroke = Reline::KeyStroke.new(core.config)
489
+ core.key_stroke = Reline::KeyStroke.new(core.config, core.encoding)
490
490
  core.line_editor = Reline::LineEditor.new(core.config)
491
491
 
492
492
  core.basic_word_break_characters = " \t\n`><=;|&{("
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.11
4
+ version: 0.5.12
5
5
  platform: ruby
6
+ original_platform: ''
6
7
  authors:
7
8
  - aycabta
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2024-11-08 00:00:00.000000000 Z
11
+ date: 2024-11-28 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: io-console
@@ -50,7 +51,6 @@ files:
50
51
  - lib/reline/key_stroke.rb
51
52
  - lib/reline/kill_ring.rb
52
53
  - lib/reline/line_editor.rb
53
- - lib/reline/terminfo.rb
54
54
  - lib/reline/unicode.rb
55
55
  - lib/reline/unicode/east_asian_width.rb
56
56
  - lib/reline/version.rb
@@ -1,158 +0,0 @@
1
- begin
2
- # Ignore warning `Add fiddle to your Gemfile or gemspec` in Ruby 3.4.
3
- # terminfo.rb and ansi.rb supports fiddle unavailable environment.
4
- verbose, $VERBOSE = $VERBOSE, nil
5
- require 'fiddle'
6
- require 'fiddle/import'
7
- rescue LoadError
8
- module Reline::Terminfo
9
- def self.curses_dl
10
- false
11
- end
12
- end
13
- ensure
14
- $VERBOSE = verbose
15
- end
16
-
17
- module Reline::Terminfo
18
- extend Fiddle::Importer
19
-
20
- class TerminfoError < StandardError; end
21
-
22
- def self.curses_dl_files
23
- case RUBY_PLATFORM
24
- when /mingw/, /mswin/
25
- # aren't supported
26
- []
27
- when /cygwin/
28
- %w[cygncursesw-10.dll cygncurses-10.dll]
29
- when /darwin/
30
- %w[libncursesw.dylib libcursesw.dylib libncurses.dylib libcurses.dylib]
31
- else
32
- %w[libncursesw.so libcursesw.so libncurses.so libcurses.so]
33
- end
34
- end
35
-
36
- @curses_dl = false
37
- def self.curses_dl
38
- return @curses_dl unless @curses_dl == false
39
- if Fiddle.const_defined?(:TYPE_VARIADIC)
40
- curses_dl_files.each do |curses_name|
41
- result = Fiddle::Handle.new(curses_name)
42
- rescue Fiddle::DLError
43
- next
44
- else
45
- @curses_dl = result
46
- break
47
- end
48
- end
49
- @curses_dl = nil if @curses_dl == false
50
- @curses_dl
51
- end
52
- end if not Reline.const_defined?(:Terminfo) or not Reline::Terminfo.respond_to?(:curses_dl)
53
-
54
- module Reline::Terminfo
55
- dlload curses_dl
56
- #extern 'int setupterm(char *term, int fildes, int *errret)'
57
- @setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
58
- #extern 'char *tigetstr(char *capname)'
59
- @tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP)
60
- begin
61
- #extern 'char *tiparm(const char *str, ...)'
62
- @tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
63
- rescue Fiddle::DLError
64
- # OpenBSD lacks tiparm
65
- #extern 'char *tparm(const char *str, ...)'
66
- @tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
67
- end
68
- begin
69
- #extern 'int tigetflag(char *str)'
70
- @tigetflag = Fiddle::Function.new(curses_dl['tigetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
71
- rescue Fiddle::DLError
72
- # OpenBSD lacks tigetflag
73
- #extern 'int tgetflag(char *str)'
74
- @tigetflag = Fiddle::Function.new(curses_dl['tgetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
75
- end
76
- begin
77
- #extern 'int tigetnum(char *str)'
78
- @tigetnum = Fiddle::Function.new(curses_dl['tigetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
79
- rescue Fiddle::DLError
80
- # OpenBSD lacks tigetnum
81
- #extern 'int tgetnum(char *str)'
82
- @tigetnum = Fiddle::Function.new(curses_dl['tgetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
83
- end
84
-
85
- def self.setupterm(term, fildes)
86
- errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE)
87
- ret = @setupterm.(term, fildes, errret_int)
88
- case ret
89
- when 0 # OK
90
- @term_supported = true
91
- when -1 # ERR
92
- @term_supported = false
93
- end
94
- end
95
-
96
- class StringWithTiparm < String
97
- def tiparm(*args) # for method chain
98
- Reline::Terminfo.tiparm(self, *args)
99
- end
100
- end
101
-
102
- def self.tigetstr(capname)
103
- raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
104
- capability = @tigetstr.(capname)
105
- case capability.to_i
106
- when 0, -1
107
- raise TerminfoError, "can't find capability: #{capname}"
108
- end
109
- StringWithTiparm.new(capability.to_s)
110
- end
111
-
112
- def self.tiparm(str, *args)
113
- new_args = []
114
- args.each do |a|
115
- new_args << Fiddle::TYPE_INT << a
116
- end
117
- @tiparm.(str, *new_args).to_s
118
- end
119
-
120
- def self.tigetflag(capname)
121
- raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
122
- flag = @tigetflag.(capname).to_i
123
- case flag
124
- when -1
125
- raise TerminfoError, "not boolean capability: #{capname}"
126
- when 0
127
- raise TerminfoError, "can't find capability: #{capname}"
128
- end
129
- flag
130
- end
131
-
132
- def self.tigetnum(capname)
133
- raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
134
- num = @tigetnum.(capname).to_i
135
- case num
136
- when -2
137
- raise TerminfoError, "not numeric capability: #{capname}"
138
- when -1
139
- raise TerminfoError, "can't find capability: #{capname}"
140
- end
141
- num
142
- end
143
-
144
- # NOTE: This means Fiddle and curses are enabled.
145
- def self.enabled?
146
- true
147
- end
148
-
149
- def self.term_supported?
150
- @term_supported
151
- end
152
- end if Reline::Terminfo.curses_dl
153
-
154
- module Reline::Terminfo
155
- def self.enabled?
156
- false
157
- end
158
- end unless Reline::Terminfo.curses_dl