reline 0.6.0 → 0.6.2

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: 8fb968cf5a8c86917a35ba517a627df5a5dc51cb9b75cef5fdd3183042cd3224
4
- data.tar.gz: b02916eae200fc26a65aafde67e450e97d26450d7a1b694db37d1dce8c43c7a4
3
+ metadata.gz: ec7aa526103ceb5eff3fa7dbbfff8c33f296f9cfb28ec4cc925b28e649dc18c8
4
+ data.tar.gz: 5c86a58b7769f390a81e9f6ca19a1e64eefad4ad05091544e5a01ef752b21983
5
5
  SHA512:
6
- metadata.gz: 7c35aff3260b914d6137b87ba0d7031ce80ddf10896191dc79302b6e79749d55590b997b7705eac4ec03046fa95f19d2ff3be4d6f566491c9f40e99813e221ff
7
- data.tar.gz: cd15de1099bcb91eb69881c20f969e6ece3e4d8548565d97d743e01d082d63aa4554381f86fbb7d29068ca84e8699ed1a91cc30a77e2b7aadb58d1d2338998c8
6
+ metadata.gz: f4e586ed0f631e402d0b4b58fb05f0eec016f1290624021e957de1ced06c15e8638dac34e91c72d4b53a83e2c6b5eb5aadec2b749271d47201773dd220c63827
7
+ data.tar.gz: 6dcd53d5a30722bb4884f79eb9c70f49ab267b5e285b231237d339a52efdaa597377d308a264ed2cef23802b27aabb38d5ccbfd0c2dce40d631e15a6da5ede68
data/lib/reline/config.rb CHANGED
@@ -177,6 +177,11 @@ class Reline::Config
177
177
  if_stack = []
178
178
 
179
179
  lines.each_with_index do |line, no|
180
+ # Even after encoding conversion, we need to verify the encoding is valid
181
+ # as some invalid byte sequences might pass through the conversion.
182
+ unless line.valid_encoding?
183
+ raise InvalidInputrc, "#{file}:#{no + 1}: can't be converted to the locale #{Reline.encoding_system_needs.name}"
184
+ end
180
185
  next if line.match(/\A\s*#/)
181
186
 
182
187
  no += 1
@@ -2,18 +2,6 @@ require 'io/console'
2
2
  require 'io/wait'
3
3
 
4
4
  class Reline::ANSI < Reline::IO
5
- CAPNAME_KEY_BINDINGS = {
6
- 'khome' => :ed_move_to_beg,
7
- 'kend' => :ed_move_to_end,
8
- 'kdch1' => :key_delete,
9
- 'kpp' => :ed_search_prev_history,
10
- 'knp' => :ed_search_next_history,
11
- 'kcuu1' => :ed_prev_history,
12
- 'kcud1' => :ed_next_history,
13
- 'kcuf1' => :ed_next_char,
14
- 'kcub1' => :ed_prev_char,
15
- }
16
-
17
5
  ANSI_CURSOR_KEY_BINDINGS = {
18
6
  # Up
19
7
  'A' => [:ed_prev_history, {}],
@@ -136,6 +124,10 @@ class Reline::ANSI < Reline::IO
136
124
  Reline.core.line_editor.handle_signal
137
125
  end
138
126
  c = @input.getbyte
127
+
128
+ # When "Escape non-ASCII Input with Control-V" is enabled in macOS Terminal.app,
129
+ # all non-ascii bytes are automatically escaped with `C-v`.
130
+ # "\xE3\x81\x82" (U+3042) becomes "\x16\xE3\x16\x81\x16\x82".
139
131
  (c == 0x16 && @input.tty? && @input.raw(min: 0, time: 0, &:getbyte)) || c
140
132
  rescue Errno::EIO
141
133
  # Maybe the I/O has been closed.
@@ -192,14 +184,14 @@ class Reline::ANSI < Reline::IO
192
184
  s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i]
193
185
  return s if s[0] > 0 && s[1] > 0
194
186
  [24, 80]
195
- rescue Errno::ENOTTY, Errno::ENODEV
187
+ rescue SystemCallError
196
188
  [24, 80]
197
189
  end
198
190
 
199
191
  def set_screen_size(rows, columns)
200
192
  @input.winsize = [rows, columns]
201
193
  self
202
- rescue Errno::ENOTTY, Errno::ENODEV
194
+ rescue SystemCallError
203
195
  self
204
196
  end
205
197
 
@@ -217,9 +209,7 @@ class Reline::ANSI < Reline::IO
217
209
  break
218
210
  end
219
211
  end
220
- buf.chars.reverse_each do |ch|
221
- stdin.ungetc ch
222
- end
212
+ @buf.concat buf.bytes
223
213
  end
224
214
  [match[:column].to_i - 1, match[:row].to_i - 1] if match
225
215
  end
@@ -309,6 +299,13 @@ class Reline::ANSI < Reline::IO
309
299
  # Signal.trap may raise an ArgumentError if the platform doesn't support the signal.
310
300
  end
311
301
 
302
+ def read_single_char(timeout_second)
303
+ # Disable intr to read `C-c` `C-z` `C-\` for quoted insert
304
+ @input.raw(intr: false) do
305
+ super
306
+ end
307
+ end
308
+
312
309
  def prep
313
310
  # Enable bracketed paste
314
311
  write "\e[?2004h" if Reline.core.config.enable_bracketed_paste && both_tty?
data/lib/reline/io.rb CHANGED
@@ -37,10 +37,10 @@ module Reline
37
37
  end
38
38
 
39
39
  # Read a single encoding valid character from the input.
40
- def read_single_char(keyseq_timeout)
40
+ def read_single_char(timeout_second)
41
41
  buffer = String.new(encoding: Encoding::ASCII_8BIT)
42
42
  loop do
43
- timeout = buffer.empty? ? Float::INFINITY : keyseq_timeout
43
+ timeout = buffer.empty? ? Float::INFINITY : timeout_second
44
44
  c = getc(timeout)
45
45
  return unless c
46
46
 
@@ -377,11 +377,11 @@ module Reline::KeyActor
377
377
  # 187 M-;
378
378
  nil,
379
379
  # 188 M-<
380
- nil,
380
+ :beginning_of_history,
381
381
  # 189 M-=
382
382
  nil,
383
383
  # 190 M->
384
- nil,
384
+ :end_of_history,
385
385
  # 191 M-?
386
386
  nil,
387
387
  # 192 M-@
@@ -56,8 +56,8 @@ class Reline::KeyStroke
56
56
  if func.is_a?(Array)
57
57
  # Perform simple macro expansion for single byte key bindings.
58
58
  # Multibyte key bindings and recursive macro expansion are not supported yet.
59
- marco = func.pack('c*').force_encoding(@encoding)
60
- keys = marco.chars.map do |c|
59
+ macro = func.pack('c*').force_encoding(@encoding)
60
+ keys = macro.chars.map do |c|
61
61
  f = key_mapping.get(c.bytes)
62
62
  Reline::Key.new(c, f.is_a?(Symbol) ? f : :ed_insert, false)
63
63
  end
@@ -250,8 +250,8 @@ class Reline::LineEditor
250
250
  @resized = false
251
251
  @cache = {}
252
252
  @rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
253
- @input_lines = [[[""], 0, 0]]
254
- @input_lines_position = 0
253
+ @undo_redo_history = [[[""], 0, 0]]
254
+ @undo_redo_index = 0
255
255
  @restoring = false
256
256
  @prev_action_state = NullActionState
257
257
  @next_action_state = NullActionState
@@ -470,14 +470,6 @@ class Reline::LineEditor
470
470
  end
471
471
  end
472
472
 
473
- def print_nomultiline_prompt
474
- Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
475
- # Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
476
- Reline::IOGate.write Reline::Unicode.strip_non_printing_start_end(@prompt) if @prompt && !@is_multiline
477
- ensure
478
- Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
479
- end
480
-
481
473
  def render
482
474
  wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
483
475
  new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line|
@@ -542,6 +534,7 @@ class Reline::LineEditor
542
534
  Reline::IOGate.show_cursor
543
535
  end
544
536
  Reline::IOGate.move_cursor_column new_cursor_x
537
+ new_cursor_y = new_cursor_y.clamp(0, screen_height - 1)
545
538
  Reline::IOGate.move_cursor_down new_cursor_y - cursor_y
546
539
  @rendered_screen.cursor_y = new_cursor_y
547
540
  ensure
@@ -911,28 +904,36 @@ class Reline::LineEditor
911
904
  )
912
905
  end
913
906
 
914
- private def run_for_operators(key, method_symbol, &block)
907
+ private def run_for_operators(key, method_symbol)
908
+ # Reject multibyte input (converted to ed_insert) in vi_command mode
909
+ return if method_symbol == :ed_insert && @config.editing_mode_is?(:vi_command) && !@waiting_proc
910
+
911
+ if ARGUMENT_DIGIT_METHODS.include?(method_symbol) && !@waiting_proc
912
+ wrap_method_call(method_symbol, key, false)
913
+ return
914
+ end
915
+
915
916
  if @vi_waiting_operator
916
- if VI_MOTIONS.include?(method_symbol)
917
+ if @waiting_proc || VI_MOTIONS.include?(method_symbol)
917
918
  old_byte_pointer = @byte_pointer
918
919
  @vi_arg = (@vi_arg || 1) * @vi_waiting_operator_arg
919
- block.(true)
920
+ wrap_method_call(method_symbol, key, true)
920
921
  unless @waiting_proc
921
922
  byte_pointer_diff = @byte_pointer - old_byte_pointer
922
923
  @byte_pointer = old_byte_pointer
923
- method_obj = method(@vi_waiting_operator)
924
- wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff)
924
+ __send__(@vi_waiting_operator, byte_pointer_diff)
925
925
  cleanup_waiting
926
926
  end
927
927
  else
928
928
  # Ignores operator when not motion is given.
929
- block.(false)
929
+ wrap_method_call(method_symbol, key, false)
930
930
  cleanup_waiting
931
931
  end
932
- @vi_arg = nil
933
932
  else
934
- block.(false)
933
+ wrap_method_call(method_symbol, key, false)
935
934
  end
935
+ @vi_arg = nil
936
+ @kill_ring.process
936
937
  end
937
938
 
938
939
  private def argumentable?(method_obj)
@@ -945,20 +946,23 @@ class Reline::LineEditor
945
946
  method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
946
947
  end
947
948
 
948
- def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
949
- if @config.editing_mode_is?(:emacs, :vi_insert) and @vi_waiting_operator.nil?
950
- not_insertion = method_symbol != :ed_insert
951
- process_insert(force: not_insertion)
949
+ def wrap_method_call(method_symbol, key, with_operator)
950
+ if @waiting_proc
951
+ @waiting_proc.call(key)
952
+ return
952
953
  end
954
+
955
+ return unless respond_to?(method_symbol, true)
956
+ method_obj = method(method_symbol)
953
957
  if @vi_arg and argumentable?(method_obj)
954
- if with_operator and inclusive?(method_obj)
955
- method_obj.(key, arg: @vi_arg, inclusive: true)
958
+ if inclusive?(method_obj)
959
+ method_obj.(key, arg: @vi_arg, inclusive: with_operator)
956
960
  else
957
961
  method_obj.(key, arg: @vi_arg)
958
962
  end
959
963
  else
960
- if with_operator and inclusive?(method_obj)
961
- method_obj.(key, inclusive: true)
964
+ if inclusive?(method_obj)
965
+ method_obj.(key, inclusive: with_operator)
962
966
  else
963
967
  method_obj.(key)
964
968
  end
@@ -984,50 +988,9 @@ class Reline::LineEditor
984
988
  cleanup_waiting unless VI_WAITING_ACCEPT_METHODS.include?(method_symbol) || VI_MOTIONS.include?(method_symbol)
985
989
  end
986
990
 
987
- if @waiting_proc
988
- old_byte_pointer = @byte_pointer
989
- @waiting_proc.call(key)
990
- if @vi_waiting_operator
991
- byte_pointer_diff = @byte_pointer - old_byte_pointer
992
- @byte_pointer = old_byte_pointer
993
- method_obj = method(@vi_waiting_operator)
994
- wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff)
995
- cleanup_waiting
996
- end
997
- @kill_ring.process
998
- return
999
- end
1000
-
1001
- # Reject multibyte input (converted to ed_insert) in vi_command mode
1002
- return if method_symbol == :ed_insert && @config.editing_mode_is?(:vi_command)
991
+ process_insert(force: method_symbol != :ed_insert)
1003
992
 
1004
- if method_symbol and respond_to?(method_symbol, true)
1005
- method_obj = method(method_symbol)
1006
- end
1007
- if @vi_arg
1008
- if ARGUMENT_DIGIT_METHODS.include?(method_symbol)
1009
- ed_argument_digit(key)
1010
- else
1011
- if argumentable?(method_obj)
1012
- run_for_operators(key, method_symbol) do |with_operator|
1013
- wrap_method_call(method_symbol, method_obj, key, with_operator)
1014
- end
1015
- elsif method_obj
1016
- wrap_method_call(method_symbol, method_obj, key)
1017
- end
1018
- @kill_ring.process
1019
- @vi_arg = nil
1020
- end
1021
- elsif method_obj
1022
- if method_symbol == :ed_argument_digit
1023
- wrap_method_call(method_symbol, method_obj, key)
1024
- else
1025
- run_for_operators(key, method_symbol) do |with_operator|
1026
- wrap_method_call(method_symbol, method_obj, key, with_operator)
1027
- end
1028
- end
1029
- @kill_ring.process
1030
- end
993
+ run_for_operators(key, method_symbol)
1031
994
  end
1032
995
 
1033
996
  def update(key)
@@ -1041,7 +1004,7 @@ class Reline::LineEditor
1041
1004
  end
1042
1005
 
1043
1006
  def input_key(key)
1044
- save_old_buffer
1007
+ old_buffer_of_lines = @buffer_of_lines.dup
1045
1008
  @config.reset_oneshot_key_bindings
1046
1009
  if key.char.nil?
1047
1010
  process_insert(force: true)
@@ -1049,11 +1012,8 @@ class Reline::LineEditor
1049
1012
  finish
1050
1013
  return
1051
1014
  end
1052
- @dialogs.each do |dialog|
1053
- if key.method_symbol == dialog.name
1054
- return
1055
- end
1056
- end
1015
+ return if @dialogs.any? { |dialog| dialog.name == key.method_symbol }
1016
+
1057
1017
  @completion_occurs = false
1058
1018
 
1059
1019
  process_key(key.char, key.method_symbol)
@@ -1069,7 +1029,9 @@ class Reline::LineEditor
1069
1029
  @completion_journey_state = nil
1070
1030
  end
1071
1031
 
1072
- push_input_lines unless @restoring
1032
+ modified = old_buffer_of_lines != @buffer_of_lines
1033
+
1034
+ push_undo_redo(modified) unless @restoring
1073
1035
  @restoring = false
1074
1036
 
1075
1037
  if @in_pasting
@@ -1077,7 +1039,6 @@ class Reline::LineEditor
1077
1039
  return
1078
1040
  end
1079
1041
 
1080
- modified = @old_buffer_of_lines != @buffer_of_lines
1081
1042
  if !@completion_occurs && modified && !@config.disable_completion && @config.autocompletion
1082
1043
  # Auto complete starts only when edited
1083
1044
  process_insert(force: true)
@@ -1086,26 +1047,17 @@ class Reline::LineEditor
1086
1047
  modified
1087
1048
  end
1088
1049
 
1089
- def save_old_buffer
1090
- @old_buffer_of_lines = @buffer_of_lines.dup
1091
- end
1092
-
1093
- def push_input_lines
1094
- if @old_buffer_of_lines == @buffer_of_lines
1095
- @input_lines[@input_lines_position] = [@buffer_of_lines.dup, @byte_pointer, @line_index]
1050
+ MAX_UNDO_REDO_HISTORY_SIZE = 100
1051
+ def push_undo_redo(modified)
1052
+ if modified
1053
+ @undo_redo_history = @undo_redo_history[0..@undo_redo_index]
1054
+ @undo_redo_history.push([@buffer_of_lines.dup, @byte_pointer, @line_index])
1055
+ if @undo_redo_history.size > MAX_UNDO_REDO_HISTORY_SIZE
1056
+ @undo_redo_history.shift
1057
+ end
1058
+ @undo_redo_index = @undo_redo_history.size - 1
1096
1059
  else
1097
- @input_lines = @input_lines[0..@input_lines_position]
1098
- @input_lines_position += 1
1099
- @input_lines.push([@buffer_of_lines.dup, @byte_pointer, @line_index])
1100
- end
1101
- trim_input_lines
1102
- end
1103
-
1104
- MAX_INPUT_LINES = 100
1105
- def trim_input_lines
1106
- if @input_lines.size > MAX_INPUT_LINES
1107
- @input_lines.shift
1108
- @input_lines_position -= 1
1060
+ @undo_redo_history[@undo_redo_index] = [@buffer_of_lines.dup, @byte_pointer, @line_index]
1109
1061
  end
1110
1062
  end
1111
1063
 
@@ -1188,7 +1140,7 @@ class Reline::LineEditor
1188
1140
  quote_characters = Reline.completer_quote_characters
1189
1141
  before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
1190
1142
  quote = nil
1191
- # Calcualte closing quote when cursor is at the end of the line
1143
+ # Calculate closing quote when cursor is at the end of the line
1192
1144
  if current_line.bytesize == @byte_pointer && !quote_characters.empty?
1193
1145
  escaped = false
1194
1146
  before.each do |c|
@@ -1413,9 +1365,16 @@ class Reline::LineEditor
1413
1365
 
1414
1366
  insert_text(str)
1415
1367
  end
1416
- alias_method :ed_digit, :ed_insert
1417
1368
  alias_method :self_insert, :ed_insert
1418
1369
 
1370
+ private def ed_digit(key)
1371
+ if @vi_arg
1372
+ ed_argument_digit(key)
1373
+ else
1374
+ ed_insert(key)
1375
+ end
1376
+ end
1377
+
1419
1378
  private def insert_raw_char(str, arg: 1)
1420
1379
  arg.times do
1421
1380
  if str == "\C-j" or str == "\C-m"
@@ -1461,7 +1420,14 @@ class Reline::LineEditor
1461
1420
  @byte_pointer = 0
1462
1421
  end
1463
1422
  alias_method :beginning_of_line, :ed_move_to_beg
1464
- alias_method :vi_zero, :ed_move_to_beg
1423
+
1424
+ private def vi_zero(key)
1425
+ if @vi_arg
1426
+ ed_argument_digit(key)
1427
+ else
1428
+ ed_move_to_beg(key)
1429
+ end
1430
+ end
1465
1431
 
1466
1432
  private def ed_move_to_end(key)
1467
1433
  @byte_pointer = current_line.bytesize
@@ -1680,6 +1646,16 @@ class Reline::LineEditor
1680
1646
  end
1681
1647
  alias_method :next_history, :ed_next_history
1682
1648
 
1649
+ private def ed_beginning_of_history(key)
1650
+ move_history(0, line: :end, cursor: :end)
1651
+ end
1652
+ alias_method :beginning_of_history, :ed_beginning_of_history
1653
+
1654
+ private def ed_end_of_history(key)
1655
+ move_history(Reline::HISTORY.size, line: :end, cursor: :end)
1656
+ end
1657
+ alias_method :end_of_history, :ed_end_of_history
1658
+
1683
1659
  private def ed_newline(key)
1684
1660
  process_insert(force: true)
1685
1661
  if @is_multiline
@@ -1691,17 +1667,10 @@ class Reline::LineEditor
1691
1667
  finish
1692
1668
  end
1693
1669
  else
1694
- if @line_index == (@buffer_of_lines.size - 1)
1695
- if confirm_multiline_termination
1696
- finish
1697
- else
1698
- key_newline(key)
1699
- end
1700
- else
1701
- # should check confirm_multiline_termination to finish?
1702
- @line_index = @buffer_of_lines.size - 1
1703
- @byte_pointer = current_line.bytesize
1670
+ if @line_index == @buffer_of_lines.size - 1 && confirm_multiline_termination
1704
1671
  finish
1672
+ else
1673
+ key_newline(key)
1705
1674
  end
1706
1675
  end
1707
1676
  else
@@ -1709,6 +1678,11 @@ class Reline::LineEditor
1709
1678
  end
1710
1679
  end
1711
1680
 
1681
+ private def ed_force_submit(_key)
1682
+ process_insert(force: true)
1683
+ finish
1684
+ end
1685
+
1712
1686
  private def em_delete_prev_char(key, arg: 1)
1713
1687
  arg.times do
1714
1688
  if @byte_pointer == 0 and @line_index > 0
@@ -2351,10 +2325,10 @@ class Reline::LineEditor
2351
2325
 
2352
2326
  private def move_undo_redo(direction)
2353
2327
  @restoring = true
2354
- return unless (0..@input_lines.size - 1).cover?(@input_lines_position + direction)
2328
+ return unless (0..@undo_redo_history.size - 1).cover?(@undo_redo_index + direction)
2355
2329
 
2356
- @input_lines_position += direction
2357
- buffer_of_lines, byte_pointer, line_index = @input_lines[@input_lines_position]
2330
+ @undo_redo_index += direction
2331
+ buffer_of_lines, byte_pointer, line_index = @undo_redo_history[@undo_redo_index]
2358
2332
  @buffer_of_lines = buffer_of_lines.dup
2359
2333
  @line_index = line_index
2360
2334
  @byte_pointer = byte_pointer
@@ -1,6 +1,6 @@
1
1
  class Reline::Unicode::EastAsianWidth
2
2
  # This is based on EastAsianWidth.txt
3
- # UNICODE_VERSION = '15.1.0'
3
+ # UNICODE_VERSION = '16.0.0'
4
4
 
5
5
  CHUNK_LAST, CHUNK_WIDTH = [
6
6
  [0x1f, 2],
@@ -129,7 +129,7 @@ class Reline::Unicode::EastAsianWidth
129
129
  [0x450, 1],
130
130
  [0x451, -1],
131
131
  [0x482, 1],
132
- [0x487, 0],
132
+ [0x489, 0],
133
133
  [0x590, 1],
134
134
  [0x5bd, 0],
135
135
  [0x5be, 1],
@@ -174,7 +174,7 @@ class Reline::Unicode::EastAsianWidth
174
174
  [0x82d, 0],
175
175
  [0x858, 1],
176
176
  [0x85b, 0],
177
- [0x897, 1],
177
+ [0x896, 1],
178
178
  [0x89f, 0],
179
179
  [0x8c9, 1],
180
180
  [0x8e1, 0],
@@ -356,6 +356,7 @@ class Reline::Unicode::EastAsianWidth
356
356
  [0x109d, 0],
357
357
  [0x10ff, 1],
358
358
  [0x115f, 2],
359
+ [0x11ff, 0],
359
360
  [0x135c, 1],
360
361
  [0x135f, 0],
361
362
  [0x1711, 1],
@@ -411,8 +412,6 @@ class Reline::Unicode::EastAsianWidth
411
412
  [0x1a7e, 1],
412
413
  [0x1a7f, 0],
413
414
  [0x1aaf, 1],
414
- [0x1abd, 0],
415
- [0x1abe, 1],
416
415
  [0x1ace, 0],
417
416
  [0x1aff, 1],
418
417
  [0x1b03, 0],
@@ -491,10 +490,6 @@ class Reline::Unicode::EastAsianWidth
491
490
  [0x20ab, 1],
492
491
  [0x20ac, -1],
493
492
  [0x20cf, 1],
494
- [0x20dc, 0],
495
- [0x20e0, 1],
496
- [0x20e1, 0],
497
- [0x20e4, 1],
498
493
  [0x20f0, 0],
499
494
  [0x2102, 1],
500
495
  [0x2103, -1],
@@ -646,6 +641,8 @@ class Reline::Unicode::EastAsianWidth
646
641
  [0x261c, -1],
647
642
  [0x261d, 1],
648
643
  [0x261e, -1],
644
+ [0x262f, 1],
645
+ [0x2637, 2],
649
646
  [0x263f, 1],
650
647
  [0x2640, -1],
651
648
  [0x2641, 1],
@@ -664,6 +661,8 @@ class Reline::Unicode::EastAsianWidth
664
661
  [0x266f, -1],
665
662
  [0x267e, 1],
666
663
  [0x267f, 2],
664
+ [0x2689, 1],
665
+ [0x268f, 2],
667
666
  [0x2692, 1],
668
667
  [0x2693, 2],
669
668
  [0x269d, 1],
@@ -753,19 +752,17 @@ class Reline::Unicode::EastAsianWidth
753
752
  [0x3130, 1],
754
753
  [0x318e, 2],
755
754
  [0x318f, 1],
756
- [0x31e3, 2],
755
+ [0x31e5, 2],
757
756
  [0x31ee, 1],
758
757
  [0x321e, 2],
759
758
  [0x321f, 1],
760
759
  [0x3247, 2],
761
760
  [0x324f, -1],
762
- [0x4dbf, 2],
763
- [0x4dff, 1],
764
761
  [0xa48c, 2],
765
762
  [0xa48f, 1],
766
763
  [0xa4c6, 2],
767
764
  [0xa66e, 1],
768
- [0xa66f, 0],
765
+ [0xa672, 0],
769
766
  [0xa673, 1],
770
767
  [0xa67d, 0],
771
768
  [0xa69d, 1],
@@ -838,6 +835,10 @@ class Reline::Unicode::EastAsianWidth
838
835
  [0xabed, 0],
839
836
  [0xabff, 1],
840
837
  [0xd7a3, 2],
838
+ [0xd7af, 1],
839
+ [0xd7c6, 0],
840
+ [0xd7ca, 1],
841
+ [0xd7fb, 0],
841
842
  [0xdfff, 1],
842
843
  [0xf8ff, -1],
843
844
  [0xfaff, 2],
@@ -879,9 +880,11 @@ class Reline::Unicode::EastAsianWidth
879
880
  [0x10ae6, 0],
880
881
  [0x10d23, 1],
881
882
  [0x10d27, 0],
883
+ [0x10d68, 1],
884
+ [0x10d6d, 0],
882
885
  [0x10eaa, 1],
883
886
  [0x10eac, 0],
884
- [0x10efc, 1],
887
+ [0x10efb, 1],
885
888
  [0x10eff, 0],
886
889
  [0x10f45, 1],
887
890
  [0x10f50, 0],
@@ -943,6 +946,16 @@ class Reline::Unicode::EastAsianWidth
943
946
  [0x1136c, 0],
944
947
  [0x1136f, 1],
945
948
  [0x11374, 0],
949
+ [0x113ba, 1],
950
+ [0x113c0, 0],
951
+ [0x113cd, 1],
952
+ [0x113ce, 0],
953
+ [0x113cf, 1],
954
+ [0x113d0, 0],
955
+ [0x113d1, 1],
956
+ [0x113d2, 0],
957
+ [0x113e0, 1],
958
+ [0x113e2, 0],
946
959
  [0x11437, 1],
947
960
  [0x1143f, 0],
948
961
  [0x11441, 1],
@@ -982,6 +995,8 @@ class Reline::Unicode::EastAsianWidth
982
995
  [0x116b6, 1],
983
996
  [0x116b7, 0],
984
997
  [0x1171c, 1],
998
+ [0x1171d, 0],
999
+ [0x1171e, 1],
985
1000
  [0x1171f, 0],
986
1001
  [0x11721, 1],
987
1002
  [0x11725, 0],
@@ -1059,10 +1074,16 @@ class Reline::Unicode::EastAsianWidth
1059
1074
  [0x11f40, 0],
1060
1075
  [0x11f41, 1],
1061
1076
  [0x11f42, 0],
1077
+ [0x11f59, 1],
1078
+ [0x11f5a, 0],
1062
1079
  [0x1343f, 1],
1063
1080
  [0x13440, 0],
1064
1081
  [0x13446, 1],
1065
1082
  [0x13455, 0],
1083
+ [0x1611d, 1],
1084
+ [0x16129, 0],
1085
+ [0x1612c, 1],
1086
+ [0x1612f, 0],
1066
1087
  [0x16aef, 1],
1067
1088
  [0x16af4, 0],
1068
1089
  [0x16b2f, 1],
@@ -1080,7 +1101,7 @@ class Reline::Unicode::EastAsianWidth
1080
1101
  [0x187f7, 2],
1081
1102
  [0x187ff, 1],
1082
1103
  [0x18cd5, 2],
1083
- [0x18cff, 1],
1104
+ [0x18cfe, 1],
1084
1105
  [0x18d08, 2],
1085
1106
  [0x1afef, 1],
1086
1107
  [0x1aff3, 2],
@@ -1116,6 +1137,10 @@ class Reline::Unicode::EastAsianWidth
1116
1137
  [0x1d1ad, 0],
1117
1138
  [0x1d241, 1],
1118
1139
  [0x1d244, 0],
1140
+ [0x1d2ff, 1],
1141
+ [0x1d356, 2],
1142
+ [0x1d35f, 1],
1143
+ [0x1d376, 2],
1119
1144
  [0x1d9ff, 1],
1120
1145
  [0x1da36, 0],
1121
1146
  [0x1da3a, 1],
@@ -1148,6 +1173,8 @@ class Reline::Unicode::EastAsianWidth
1148
1173
  [0x1e2ef, 0],
1149
1174
  [0x1e4eb, 1],
1150
1175
  [0x1e4ef, 0],
1176
+ [0x1e5ed, 1],
1177
+ [0x1e5ef, 0],
1151
1178
  [0x1e8cf, 1],
1152
1179
  [0x1e8d6, 0],
1153
1180
  [0x1e943, 1],
@@ -1241,15 +1268,13 @@ class Reline::Unicode::EastAsianWidth
1241
1268
  [0x1fa6f, 1],
1242
1269
  [0x1fa7c, 2],
1243
1270
  [0x1fa7f, 1],
1244
- [0x1fa88, 2],
1245
- [0x1fa8f, 1],
1246
- [0x1fabd, 2],
1247
- [0x1fabe, 1],
1248
- [0x1fac5, 2],
1271
+ [0x1fa89, 2],
1272
+ [0x1fa8e, 1],
1273
+ [0x1fac6, 2],
1249
1274
  [0x1facd, 1],
1250
- [0x1fadb, 2],
1251
- [0x1fadf, 1],
1252
- [0x1fae8, 2],
1275
+ [0x1fadc, 2],
1276
+ [0x1fade, 1],
1277
+ [0x1fae9, 2],
1253
1278
  [0x1faef, 1],
1254
1279
  [0x1faf8, 2],
1255
1280
  [0x1ffff, 1],
@@ -28,12 +28,12 @@ class Reline::Unicode
28
28
  0x19 => '^Y',
29
29
  0x1A => '^Z', # C-z
30
30
  0x1B => '^[', # C-[ C-3
31
+ 0x1C => '^\\', # C-\
31
32
  0x1D => '^]', # C-]
32
33
  0x1E => '^^', # C-~ C-6
33
34
  0x1F => '^_', # C-_ C-7
34
35
  0x7F => '^?', # C-? C-8
35
36
  }
36
- EscapedChars = EscapedPairs.keys.map(&:chr)
37
37
 
38
38
  NON_PRINTING_START = "\1"
39
39
  NON_PRINTING_END = "\2"
@@ -61,7 +61,7 @@ class Reline::Unicode
61
61
 
62
62
  # This code is essentially doing the same thing as
63
63
  # `str.encode(utf8, **replace_options).encode(encoding, **replace_options)`
64
- # but also avoids unneccesary irreversible encoding conversion.
64
+ # but also avoids unnecessary irreversible encoding conversion.
65
65
  converted.gsub(/\X/) do |c|
66
66
  c.encode(Encoding::UTF_8)
67
67
  c
@@ -72,26 +72,32 @@ class Reline::Unicode
72
72
 
73
73
  require 'reline/unicode/east_asian_width'
74
74
 
75
+ def self.east_asian_width(ord)
76
+ chunk_index = EastAsianWidth::CHUNK_LAST.bsearch_index { |o| ord <= o }
77
+ size = EastAsianWidth::CHUNK_WIDTH[chunk_index]
78
+ size == -1 ? Reline.ambiguous_width : size
79
+ end
80
+
75
81
  def self.get_mbchar_width(mbchar)
76
82
  ord = mbchar.ord
77
83
  if ord <= 0x1F # in EscapedPairs
78
84
  return 2
79
- elsif ord <= 0x7E # printable ASCII chars
85
+ elsif mbchar.length == 1 && ord <= 0x7E # printable ASCII chars
80
86
  return 1
81
87
  end
88
+
82
89
  utf8_mbchar = mbchar.encode(Encoding::UTF_8)
83
- ord = utf8_mbchar.ord
84
- chunk_index = EastAsianWidth::CHUNK_LAST.bsearch_index { |o| ord <= o }
85
- size = EastAsianWidth::CHUNK_WIDTH[chunk_index]
86
- if size == -1
87
- Reline.ambiguous_width
88
- elsif size == 1 && utf8_mbchar.size >= 2
89
- second_char_ord = utf8_mbchar[1].ord
90
- # Halfwidth Dakuten Handakuten
91
- # Only these two character has Letter Modifier category and can be combined in a single grapheme cluster
92
- (second_char_ord == 0xFF9E || second_char_ord == 0xFF9F) ? 2 : 1
93
- else
94
- size
90
+ zwj = false
91
+ utf8_mbchar.chars.sum do |c|
92
+ if zwj
93
+ zwj = false
94
+ 0
95
+ elsif c.ord == 0x200D # Zero Width Joiner
96
+ zwj = true
97
+ 0
98
+ else
99
+ east_asian_width(c.ord)
100
+ end
95
101
  end
96
102
  end
97
103
 
@@ -214,7 +220,7 @@ class Reline::Unicode
214
220
  next
215
221
  elsif padding && !cover_begin && prev_width < start_col && start_col < total_width
216
222
  # Add preceding padding. This padding might have background color.
217
- chunk << ' '
223
+ chunk << ' ' * (total_width - start_col)
218
224
  chunk_start_col ||= start_col
219
225
  chunk_end_col = total_width
220
226
  next
@@ -228,7 +234,7 @@ class Reline::Unicode
228
234
  # Current character exceeds end_col
229
235
  if padding && end_col < total_width
230
236
  # Add succeeding padding. This padding might have background color.
231
- chunk << ' '
237
+ chunk << ' ' * (end_col - prev_width)
232
238
  chunk_start_col ||= prev_width
233
239
  chunk_end_col = end_col
234
240
  end
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.6.0'
2
+ VERSION = '0.6.2'
3
3
  end
data/lib/reline.rb CHANGED
@@ -12,10 +12,10 @@ require 'rbconfig'
12
12
 
13
13
  module Reline
14
14
  # NOTE: For making compatible with the rb-readline gem
15
- FILENAME_COMPLETION_PROC = nil
16
- USERNAME_COMPLETION_PROC = nil
15
+ FILENAME_COMPLETION_PROC = nil # :nodoc:
16
+ USERNAME_COMPLETION_PROC = nil # :nodoc:
17
17
 
18
- class ConfigEncodingConversionError < StandardError; end
18
+ class ConfigEncodingConversionError < StandardError; end # :nodoc:
19
19
 
20
20
  # EOF key: { char: nil, method_symbol: nil }
21
21
  # Other key: { char: String, method_symbol: Symbol }
@@ -24,8 +24,8 @@ module Reline
24
24
  def match?(sym)
25
25
  method_symbol && method_symbol == sym
26
26
  end
27
- end
28
- CursorPos = Struct.new(:x, :y)
27
+ end # :nodoc:
28
+ CursorPos = Struct.new(:x, :y) # :nodoc:
29
29
  DialogRenderInfo = Struct.new(
30
30
  :pos,
31
31
  :contents,
@@ -35,7 +35,7 @@ module Reline
35
35
  :height,
36
36
  :scrollbar,
37
37
  keyword_init: true
38
- )
38
+ ) # :nodoc:
39
39
 
40
40
  class Core
41
41
  ATTR_READER_NAMES = %i(
@@ -244,8 +244,8 @@ module Reline
244
244
  height: [15, preferred_dialog_height].min,
245
245
  face: :completion_dialog
246
246
  )
247
- }
248
- Reline::DEFAULT_DIALOG_CONTEXT = Array.new
247
+ } # :nodoc:
248
+ Reline::DEFAULT_DIALOG_CONTEXT = Array.new # :nodoc:
249
249
 
250
250
  def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
251
251
  @mutex.synchronize do
@@ -324,8 +324,6 @@ module Reline
324
324
  line_editor.auto_indent_proc = auto_indent_proc
325
325
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
326
326
 
327
- # Readline calls pre_input_hook just after printing the first prompt.
328
- line_editor.print_nomultiline_prompt
329
327
  pre_input_hook&.call
330
328
 
331
329
  unless Reline::IOGate.dumb?
@@ -341,15 +339,16 @@ module Reline
341
339
  line_editor.set_signal_handlers
342
340
  loop do
343
341
  read_io(config.keyseq_timeout) { |inputs|
344
- line_editor.set_pasting_state(io_gate.in_pasting?)
345
342
  inputs.each do |key|
346
343
  case key.method_symbol
347
344
  when :bracketed_paste_start
348
345
  # io_gate is Reline::ANSI because the key :bracketed_paste_start is only assigned in Reline::ANSI
349
346
  key = Reline::Key.new(io_gate.read_bracketed_paste, :insert_multiline_text)
350
347
  when :quoted_insert, :ed_quoted_insert
351
- key = Reline::Key.new(io_gate.read_single_char(config.keyseq_timeout), :insert_raw_char)
348
+ char = io_gate.read_single_char(config.keyseq_timeout.fdiv(1000))
349
+ key = Reline::Key.new(char || '', :insert_raw_char)
352
350
  end
351
+ line_editor.set_pasting_state(io_gate.in_pasting?)
353
352
  line_editor.update(key)
354
353
  end
355
354
  }
@@ -357,7 +356,6 @@ module Reline
357
356
  line_editor.render_finished
358
357
  break
359
358
  else
360
- line_editor.set_pasting_state(io_gate.in_pasting?)
361
359
  line_editor.rerender
362
360
  end
363
361
  end
@@ -440,6 +438,17 @@ module Reline
440
438
  }
441
439
  def_single_delegators :core, :input=, :output=
442
440
  def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
441
+
442
+ ##
443
+ # :singleton-method: readmultiline
444
+ # :call-seq:
445
+ # readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination) -> string or nil
446
+ def_single_delegators :core, :readmultiline
447
+
448
+ ##
449
+ # :singleton-method: readline
450
+ # :call-seq:
451
+ # readline(prompt = '', add_hist = false) -> string or nil
443
452
  def_single_delegators :core, :readline
444
453
  def_single_delegators :core, :completion_case_fold, :completion_case_fold=
445
454
  def_single_delegators :core, :completion_quote_character
@@ -475,11 +484,10 @@ module Reline
475
484
  def_single_delegators :core, :dialog_proc
476
485
  def_single_delegators :core, :autocompletion, :autocompletion=
477
486
 
478
- def_single_delegators :core, :readmultiline
479
487
  def_instance_delegators self, :readmultiline
480
488
  private :readmultiline
481
489
 
482
- def self.encoding_system_needs
490
+ def self.encoding_system_needs # :nodoc:
483
491
  self.core.encoding
484
492
  end
485
493
 
@@ -512,7 +520,7 @@ end
512
520
  Reline::IOGate = Reline::IO.decide_io_gate
513
521
 
514
522
  # Deprecated
515
- Reline::GeneralIO = Reline::Dumb.new
523
+ Reline::GeneralIO = Reline::Dumb.new # :nodoc:
516
524
 
517
525
  Reline::Face.load_initial_configs
518
526
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-16 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: io-console
@@ -62,7 +61,6 @@ metadata:
62
61
  bug_tracker_uri: https://github.com/ruby/reline/issues
63
62
  changelog_uri: https://github.com/ruby/reline/releases
64
63
  source_code_uri: https://github.com/ruby/reline
65
- post_install_message:
66
64
  rdoc_options: []
67
65
  require_paths:
68
66
  - lib
@@ -77,8 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
75
  - !ruby/object:Gem::Version
78
76
  version: '0'
79
77
  requirements: []
80
- rubygems_version: 3.5.22
81
- signing_key:
78
+ rubygems_version: 3.6.7
82
79
  specification_version: 4
83
80
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.
84
81
  test_files: []