reline 0.5.12 → 0.6.1
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 +4 -4
- data/lib/reline/io/ansi.rb +44 -36
- data/lib/reline/io/dumb.rb +11 -0
- data/lib/reline/io/windows.rb +11 -1
- data/lib/reline/io.rb +14 -0
- data/lib/reline/key_actor/base.rb +10 -4
- data/lib/reline/key_actor/emacs.rb +96 -96
- data/lib/reline/key_actor/vi_command.rb +182 -182
- data/lib/reline/key_actor/vi_insert.rb +137 -137
- data/lib/reline/key_stroke.rb +10 -11
- data/lib/reline/line_editor.rb +153 -250
- data/lib/reline/unicode/east_asian_width.rb +41 -15
- data/lib/reline/unicode.rb +102 -348
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +34 -25
- metadata +3 -4
data/lib/reline/line_editor.rb
CHANGED
@@ -13,7 +13,6 @@ class Reline::LineEditor
|
|
13
13
|
attr_accessor :prompt_proc
|
14
14
|
attr_accessor :auto_indent_proc
|
15
15
|
attr_accessor :dig_perfect_match_proc
|
16
|
-
attr_writer :output
|
17
16
|
|
18
17
|
VI_MOTIONS = %i{
|
19
18
|
ed_prev_char
|
@@ -251,9 +250,9 @@ class Reline::LineEditor
|
|
251
250
|
@resized = false
|
252
251
|
@cache = {}
|
253
252
|
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
|
254
|
-
@
|
255
|
-
@
|
256
|
-
@
|
253
|
+
@undo_redo_history = [[[""], 0, 0]]
|
254
|
+
@undo_redo_index = 0
|
255
|
+
@restoring = false
|
257
256
|
@prev_action_state = NullActionState
|
258
257
|
@next_action_state = NullActionState
|
259
258
|
reset_line
|
@@ -414,7 +413,7 @@ class Reline::LineEditor
|
|
414
413
|
# do nothing
|
415
414
|
elsif level == :blank
|
416
415
|
Reline::IOGate.move_cursor_column base_x
|
417
|
-
|
416
|
+
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{' ' * width}"
|
418
417
|
else
|
419
418
|
x, w, content = new_items[level]
|
420
419
|
cover_begin = base_x != 0 && new_levels[base_x - 1] == level
|
@@ -424,7 +423,7 @@ class Reline::LineEditor
|
|
424
423
|
content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
|
425
424
|
end
|
426
425
|
Reline::IOGate.move_cursor_column x + pos
|
427
|
-
|
426
|
+
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{content}#{Reline::IOGate.reset_color_sequence}"
|
428
427
|
end
|
429
428
|
base_x += width
|
430
429
|
end
|
@@ -437,7 +436,7 @@ class Reline::LineEditor
|
|
437
436
|
# Calculate cursor position in word wrapped content.
|
438
437
|
def wrapped_cursor_position
|
439
438
|
prompt_width = calculate_width(prompt_list[@line_index], true)
|
440
|
-
line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
|
439
|
+
line_before_cursor = Reline::Unicode.escape_for_print(whole_lines[@line_index].byteslice(0, @byte_pointer))
|
441
440
|
wrapped_line_before_cursor = split_line_by_width(' ' * prompt_width + line_before_cursor, screen_width)
|
442
441
|
wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
|
443
442
|
wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
|
@@ -460,19 +459,21 @@ class Reline::LineEditor
|
|
460
459
|
end
|
461
460
|
|
462
461
|
def render_finished
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
462
|
+
Reline::IOGate.buffered_output do
|
463
|
+
render_differential([], 0, 0)
|
464
|
+
lines = @buffer_of_lines.size.times.map do |i|
|
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)
|
467
|
+
wrapped_lines.last.empty? ? "#{line} " : line
|
468
|
+
end
|
469
|
+
Reline::IOGate.write lines.map { |l| "#{l}\r\n" }.join
|
468
470
|
end
|
469
|
-
@output.puts lines.map { |l| "#{l}\r\n" }.join
|
470
471
|
end
|
471
472
|
|
472
473
|
def print_nomultiline_prompt
|
473
474
|
Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
|
474
475
|
# Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
|
475
|
-
|
476
|
+
Reline::IOGate.write Reline::Unicode.strip_non_printing_start_end(@prompt) if @prompt && !@is_multiline
|
476
477
|
ensure
|
477
478
|
Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
|
478
479
|
end
|
@@ -503,7 +504,9 @@ class Reline::LineEditor
|
|
503
504
|
end
|
504
505
|
end
|
505
506
|
|
506
|
-
|
507
|
+
Reline::IOGate.buffered_output do
|
508
|
+
render_differential new_lines, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top
|
509
|
+
end
|
507
510
|
end
|
508
511
|
|
509
512
|
# Reflects lines to be rendered and new cursor position to the screen
|
@@ -539,6 +542,7 @@ class Reline::LineEditor
|
|
539
542
|
Reline::IOGate.show_cursor
|
540
543
|
end
|
541
544
|
Reline::IOGate.move_cursor_column new_cursor_x
|
545
|
+
new_cursor_y = new_cursor_y.clamp(0, screen_height - 1)
|
542
546
|
Reline::IOGate.move_cursor_down new_cursor_y - cursor_y
|
543
547
|
@rendered_screen.cursor_y = new_cursor_y
|
544
548
|
ensure
|
@@ -807,11 +811,11 @@ class Reline::LineEditor
|
|
807
811
|
target = target.downcase if @config.completion_ignore_case
|
808
812
|
list.select do |item|
|
809
813
|
next unless item
|
810
|
-
|
811
814
|
unless Encoding.compatible?(target.encoding, item.encoding)
|
812
|
-
#
|
813
|
-
|
814
|
-
|
815
|
+
# Workaround for Readline test
|
816
|
+
if defined?(::Readline) && ::Readline == ::Reline
|
817
|
+
raise Encoding::CompatibilityError, "incompatible character encodings: #{target.encoding} and #{item.encoding}"
|
818
|
+
end
|
815
819
|
end
|
816
820
|
|
817
821
|
if @config.completion_ignore_case
|
@@ -908,28 +912,36 @@ class Reline::LineEditor
|
|
908
912
|
)
|
909
913
|
end
|
910
914
|
|
911
|
-
private def run_for_operators(key, method_symbol
|
915
|
+
private def run_for_operators(key, method_symbol)
|
916
|
+
# Reject multibyte input (converted to ed_insert) in vi_command mode
|
917
|
+
return if method_symbol == :ed_insert && @config.editing_mode_is?(:vi_command) && !@waiting_proc
|
918
|
+
|
919
|
+
if ARGUMENT_DIGIT_METHODS.include?(method_symbol) && !@waiting_proc
|
920
|
+
wrap_method_call(method_symbol, key, false)
|
921
|
+
return
|
922
|
+
end
|
923
|
+
|
912
924
|
if @vi_waiting_operator
|
913
|
-
if VI_MOTIONS.include?(method_symbol)
|
925
|
+
if @waiting_proc || VI_MOTIONS.include?(method_symbol)
|
914
926
|
old_byte_pointer = @byte_pointer
|
915
927
|
@vi_arg = (@vi_arg || 1) * @vi_waiting_operator_arg
|
916
|
-
|
928
|
+
wrap_method_call(method_symbol, key, true)
|
917
929
|
unless @waiting_proc
|
918
930
|
byte_pointer_diff = @byte_pointer - old_byte_pointer
|
919
931
|
@byte_pointer = old_byte_pointer
|
920
|
-
|
921
|
-
wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff)
|
932
|
+
__send__(@vi_waiting_operator, byte_pointer_diff)
|
922
933
|
cleanup_waiting
|
923
934
|
end
|
924
935
|
else
|
925
936
|
# Ignores operator when not motion is given.
|
926
|
-
|
937
|
+
wrap_method_call(method_symbol, key, false)
|
927
938
|
cleanup_waiting
|
928
939
|
end
|
929
|
-
@vi_arg = nil
|
930
940
|
else
|
931
|
-
|
941
|
+
wrap_method_call(method_symbol, key, false)
|
932
942
|
end
|
943
|
+
@vi_arg = nil
|
944
|
+
@kill_ring.process
|
933
945
|
end
|
934
946
|
|
935
947
|
private def argumentable?(method_obj)
|
@@ -942,20 +954,23 @@ class Reline::LineEditor
|
|
942
954
|
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
|
943
955
|
end
|
944
956
|
|
945
|
-
def wrap_method_call(method_symbol,
|
946
|
-
if @
|
947
|
-
|
948
|
-
|
957
|
+
def wrap_method_call(method_symbol, key, with_operator)
|
958
|
+
if @waiting_proc
|
959
|
+
@waiting_proc.call(key)
|
960
|
+
return
|
949
961
|
end
|
962
|
+
|
963
|
+
return unless respond_to?(method_symbol, true)
|
964
|
+
method_obj = method(method_symbol)
|
950
965
|
if @vi_arg and argumentable?(method_obj)
|
951
|
-
if
|
952
|
-
method_obj.(key, arg: @vi_arg, inclusive:
|
966
|
+
if inclusive?(method_obj)
|
967
|
+
method_obj.(key, arg: @vi_arg, inclusive: with_operator)
|
953
968
|
else
|
954
969
|
method_obj.(key, arg: @vi_arg)
|
955
970
|
end
|
956
971
|
else
|
957
|
-
if
|
958
|
-
method_obj.(key, inclusive:
|
972
|
+
if inclusive?(method_obj)
|
973
|
+
method_obj.(key, inclusive: with_operator)
|
959
974
|
else
|
960
975
|
method_obj.(key)
|
961
976
|
end
|
@@ -970,81 +985,20 @@ class Reline::LineEditor
|
|
970
985
|
@drop_terminate_spaces = false
|
971
986
|
end
|
972
987
|
|
973
|
-
|
974
|
-
|
975
|
-
cleanup_waiting
|
976
|
-
elsif @waiting_proc
|
977
|
-
old_byte_pointer = @byte_pointer
|
978
|
-
@waiting_proc.call(key)
|
979
|
-
if @vi_waiting_operator
|
980
|
-
byte_pointer_diff = @byte_pointer - old_byte_pointer
|
981
|
-
@byte_pointer = old_byte_pointer
|
982
|
-
method_obj = method(@vi_waiting_operator)
|
983
|
-
wrap_method_call(@vi_waiting_operator, method_obj, byte_pointer_diff)
|
984
|
-
cleanup_waiting
|
985
|
-
end
|
986
|
-
@kill_ring.process
|
987
|
-
return
|
988
|
-
end
|
988
|
+
ARGUMENT_DIGIT_METHODS = %i[ed_digit vi_zero ed_argument_digit]
|
989
|
+
VI_WAITING_ACCEPT_METHODS = %i[vi_change_meta vi_delete_meta vi_yank ed_insert ed_argument_digit]
|
989
990
|
|
990
|
-
|
991
|
-
|
991
|
+
private def process_key(key, method_symbol)
|
992
|
+
if @waiting_proc
|
993
|
+
cleanup_waiting unless key.size == 1
|
992
994
|
end
|
993
|
-
if
|
994
|
-
|
995
|
-
run_for_operators(key, method_symbol) do |with_operator|
|
996
|
-
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
997
|
-
end
|
998
|
-
else
|
999
|
-
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
1000
|
-
end
|
1001
|
-
@kill_ring.process
|
1002
|
-
if @vi_arg
|
1003
|
-
@vi_arg = nil
|
1004
|
-
end
|
1005
|
-
elsif @vi_arg
|
1006
|
-
if key.chr =~ /[0-9]/
|
1007
|
-
ed_argument_digit(key)
|
1008
|
-
else
|
1009
|
-
if argumentable?(method_obj)
|
1010
|
-
run_for_operators(key, method_symbol) do |with_operator|
|
1011
|
-
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
1012
|
-
end
|
1013
|
-
elsif method_obj
|
1014
|
-
wrap_method_call(method_symbol, method_obj, key)
|
1015
|
-
else
|
1016
|
-
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1017
|
-
end
|
1018
|
-
@kill_ring.process
|
1019
|
-
if @vi_arg
|
1020
|
-
@vi_arg = nil
|
1021
|
-
end
|
1022
|
-
end
|
1023
|
-
elsif method_obj
|
1024
|
-
if method_symbol == :ed_argument_digit
|
1025
|
-
wrap_method_call(method_symbol, method_obj, key)
|
1026
|
-
else
|
1027
|
-
run_for_operators(key, method_symbol) do |with_operator|
|
1028
|
-
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
1029
|
-
end
|
1030
|
-
end
|
1031
|
-
@kill_ring.process
|
1032
|
-
else
|
1033
|
-
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
995
|
+
if @vi_waiting_operator
|
996
|
+
cleanup_waiting unless VI_WAITING_ACCEPT_METHODS.include?(method_symbol) || VI_MOTIONS.include?(method_symbol)
|
1034
997
|
end
|
1035
|
-
end
|
1036
998
|
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
process_key(key.combined_char, method_symbol)
|
1041
|
-
else
|
1042
|
-
process_key(key.char.chr(encoding), nil)
|
1043
|
-
end
|
1044
|
-
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1045
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1046
|
-
@byte_pointer -= byte_size
|
1047
|
-
end
|
999
|
+
process_insert(force: method_symbol != :ed_insert)
|
1000
|
+
|
1001
|
+
run_for_operators(key, method_symbol)
|
1048
1002
|
end
|
1049
1003
|
|
1050
1004
|
def update(key)
|
@@ -1058,25 +1012,22 @@ class Reline::LineEditor
|
|
1058
1012
|
end
|
1059
1013
|
|
1060
1014
|
def input_key(key)
|
1061
|
-
|
1015
|
+
old_buffer_of_lines = @buffer_of_lines.dup
|
1062
1016
|
@config.reset_oneshot_key_bindings
|
1063
|
-
@dialogs.each do |dialog|
|
1064
|
-
if key.char.instance_of?(Symbol) and key.char == dialog.name
|
1065
|
-
return
|
1066
|
-
end
|
1067
|
-
end
|
1068
1017
|
if key.char.nil?
|
1069
1018
|
process_insert(force: true)
|
1070
1019
|
@eof = buffer_empty?
|
1071
1020
|
finish
|
1072
1021
|
return
|
1073
1022
|
end
|
1023
|
+
return if @dialogs.any? { |dialog| dialog.name == key.method_symbol }
|
1024
|
+
|
1074
1025
|
@completion_occurs = false
|
1075
1026
|
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1027
|
+
process_key(key.char, key.method_symbol)
|
1028
|
+
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1029
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1030
|
+
@byte_pointer -= byte_size
|
1080
1031
|
end
|
1081
1032
|
|
1082
1033
|
@prev_action_state, @next_action_state = @next_action_state, NullActionState
|
@@ -1086,15 +1037,16 @@ class Reline::LineEditor
|
|
1086
1037
|
@completion_journey_state = nil
|
1087
1038
|
end
|
1088
1039
|
|
1089
|
-
|
1090
|
-
|
1040
|
+
modified = old_buffer_of_lines != @buffer_of_lines
|
1041
|
+
|
1042
|
+
push_undo_redo(modified) unless @restoring
|
1043
|
+
@restoring = false
|
1091
1044
|
|
1092
1045
|
if @in_pasting
|
1093
1046
|
clear_dialogs
|
1094
1047
|
return
|
1095
1048
|
end
|
1096
1049
|
|
1097
|
-
modified = @old_buffer_of_lines != @buffer_of_lines
|
1098
1050
|
if !@completion_occurs && modified && !@config.disable_completion && @config.autocompletion
|
1099
1051
|
# Auto complete starts only when edited
|
1100
1052
|
process_insert(force: true)
|
@@ -1103,26 +1055,17 @@ class Reline::LineEditor
|
|
1103
1055
|
modified
|
1104
1056
|
end
|
1105
1057
|
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1058
|
+
MAX_UNDO_REDO_HISTORY_SIZE = 100
|
1059
|
+
def push_undo_redo(modified)
|
1060
|
+
if modified
|
1061
|
+
@undo_redo_history = @undo_redo_history[0..@undo_redo_index]
|
1062
|
+
@undo_redo_history.push([@buffer_of_lines.dup, @byte_pointer, @line_index])
|
1063
|
+
if @undo_redo_history.size > MAX_UNDO_REDO_HISTORY_SIZE
|
1064
|
+
@undo_redo_history.shift
|
1065
|
+
end
|
1066
|
+
@undo_redo_index = @undo_redo_history.size - 1
|
1113
1067
|
else
|
1114
|
-
@
|
1115
|
-
@input_lines_position += 1
|
1116
|
-
@input_lines.push([@buffer_of_lines.dup, @byte_pointer, @line_index])
|
1117
|
-
end
|
1118
|
-
trim_input_lines
|
1119
|
-
end
|
1120
|
-
|
1121
|
-
MAX_INPUT_LINES = 100
|
1122
|
-
def trim_input_lines
|
1123
|
-
if @input_lines.size > MAX_INPUT_LINES
|
1124
|
-
@input_lines.shift
|
1125
|
-
@input_lines_position -= 1
|
1068
|
+
@undo_redo_history[@undo_redo_index] = [@buffer_of_lines.dup, @byte_pointer, @line_index]
|
1126
1069
|
end
|
1127
1070
|
end
|
1128
1071
|
|
@@ -1201,23 +1144,11 @@ class Reline::LineEditor
|
|
1201
1144
|
process_auto_indent
|
1202
1145
|
end
|
1203
1146
|
|
1204
|
-
def set_current_lines(lines, byte_pointer = nil, line_index = 0)
|
1205
|
-
cursor = current_byte_pointer_cursor
|
1206
|
-
@buffer_of_lines = lines
|
1207
|
-
@line_index = line_index
|
1208
|
-
if byte_pointer
|
1209
|
-
@byte_pointer = byte_pointer
|
1210
|
-
else
|
1211
|
-
calculate_nearest_cursor(cursor)
|
1212
|
-
end
|
1213
|
-
process_auto_indent
|
1214
|
-
end
|
1215
|
-
|
1216
1147
|
def retrieve_completion_block
|
1217
1148
|
quote_characters = Reline.completer_quote_characters
|
1218
1149
|
before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
|
1219
1150
|
quote = nil
|
1220
|
-
#
|
1151
|
+
# Calculate closing quote when cursor is at the end of the line
|
1221
1152
|
if current_line.bytesize == @byte_pointer && !quote_characters.empty?
|
1222
1153
|
escaped = false
|
1223
1154
|
before.each do |c|
|
@@ -1255,7 +1186,6 @@ class Reline::LineEditor
|
|
1255
1186
|
end
|
1256
1187
|
|
1257
1188
|
def insert_multiline_text(text)
|
1258
|
-
save_old_buffer
|
1259
1189
|
pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer)
|
1260
1190
|
post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..)
|
1261
1191
|
lines = (pre + Reline::Unicode.safe_encode(text, encoding).gsub(/\r\n?/, "\n") + post).split("\n", -1)
|
@@ -1263,7 +1193,6 @@ class Reline::LineEditor
|
|
1263
1193
|
@buffer_of_lines[@line_index, 1] = lines
|
1264
1194
|
@line_index += lines.size - 1
|
1265
1195
|
@byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize
|
1266
|
-
push_input_lines
|
1267
1196
|
end
|
1268
1197
|
|
1269
1198
|
def insert_text(text)
|
@@ -1429,21 +1358,11 @@ class Reline::LineEditor
|
|
1429
1358
|
# digit or if the existing argument is already greater than a
|
1430
1359
|
# million.
|
1431
1360
|
# GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
|
1432
|
-
private def ed_insert(
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
return
|
1438
|
-
end
|
1439
|
-
str = key
|
1440
|
-
else
|
1441
|
-
begin
|
1442
|
-
key.chr.encode(Encoding::UTF_8)
|
1443
|
-
rescue Encoding::UndefinedConversionError
|
1444
|
-
return
|
1445
|
-
end
|
1446
|
-
str = key.chr
|
1361
|
+
private def ed_insert(str)
|
1362
|
+
begin
|
1363
|
+
str.encode(Encoding::UTF_8)
|
1364
|
+
rescue Encoding::UndefinedConversionError
|
1365
|
+
return
|
1447
1366
|
end
|
1448
1367
|
if @in_pasting
|
1449
1368
|
@continuous_insertion_buffer << str
|
@@ -1454,24 +1373,26 @@ class Reline::LineEditor
|
|
1454
1373
|
|
1455
1374
|
insert_text(str)
|
1456
1375
|
end
|
1457
|
-
alias_method :ed_digit, :ed_insert
|
1458
1376
|
alias_method :self_insert, :ed_insert
|
1459
1377
|
|
1460
|
-
private def
|
1461
|
-
@
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1378
|
+
private def ed_digit(key)
|
1379
|
+
if @vi_arg
|
1380
|
+
ed_argument_digit(key)
|
1381
|
+
else
|
1382
|
+
ed_insert(key)
|
1383
|
+
end
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
private def insert_raw_char(str, arg: 1)
|
1387
|
+
arg.times do
|
1388
|
+
if str == "\C-j" or str == "\C-m"
|
1389
|
+
key_newline(str)
|
1390
|
+
elsif str != "\0"
|
1391
|
+
# Ignore NUL.
|
1392
|
+
ed_insert(str)
|
1470
1393
|
end
|
1471
|
-
|
1472
|
-
}
|
1394
|
+
end
|
1473
1395
|
end
|
1474
|
-
alias_method :quoted_insert, :ed_quoted_insert
|
1475
1396
|
|
1476
1397
|
private def ed_next_char(key, arg: 1)
|
1477
1398
|
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
@@ -1507,7 +1428,14 @@ class Reline::LineEditor
|
|
1507
1428
|
@byte_pointer = 0
|
1508
1429
|
end
|
1509
1430
|
alias_method :beginning_of_line, :ed_move_to_beg
|
1510
|
-
|
1431
|
+
|
1432
|
+
private def vi_zero(key)
|
1433
|
+
if @vi_arg
|
1434
|
+
ed_argument_digit(key)
|
1435
|
+
else
|
1436
|
+
ed_move_to_beg(key)
|
1437
|
+
end
|
1438
|
+
end
|
1511
1439
|
|
1512
1440
|
private def ed_move_to_end(key)
|
1513
1441
|
@byte_pointer = current_line.bytesize
|
@@ -1520,13 +1448,13 @@ class Reline::LineEditor
|
|
1520
1448
|
lambda do |key|
|
1521
1449
|
search_again = false
|
1522
1450
|
case key
|
1523
|
-
when "\C-h"
|
1451
|
+
when "\C-h", "\C-?"
|
1524
1452
|
grapheme_clusters = search_word.grapheme_clusters
|
1525
1453
|
if grapheme_clusters.size > 0
|
1526
1454
|
grapheme_clusters.pop
|
1527
1455
|
search_word = grapheme_clusters.join
|
1528
1456
|
end
|
1529
|
-
when "\C-r"
|
1457
|
+
when "\C-r", "\C-s"
|
1530
1458
|
search_again = true if search_key == key
|
1531
1459
|
search_key = key
|
1532
1460
|
else
|
@@ -1543,10 +1471,10 @@ class Reline::LineEditor
|
|
1543
1471
|
end
|
1544
1472
|
if @history_pointer
|
1545
1473
|
case search_key
|
1546
|
-
when "\C-r"
|
1474
|
+
when "\C-r"
|
1547
1475
|
history_pointer_base = 0
|
1548
1476
|
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
1549
|
-
when "\C-s"
|
1477
|
+
when "\C-s"
|
1550
1478
|
history_pointer_base = @history_pointer + 1
|
1551
1479
|
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
1552
1480
|
end
|
@@ -1556,10 +1484,10 @@ class Reline::LineEditor
|
|
1556
1484
|
end
|
1557
1485
|
elsif @history_pointer
|
1558
1486
|
case search_key
|
1559
|
-
when "\C-r"
|
1487
|
+
when "\C-r"
|
1560
1488
|
history_pointer_base = 0
|
1561
1489
|
history = Reline::HISTORY[0..@history_pointer]
|
1562
|
-
when "\C-s"
|
1490
|
+
when "\C-s"
|
1563
1491
|
history_pointer_base = @history_pointer
|
1564
1492
|
history = Reline::HISTORY[@history_pointer..-1]
|
1565
1493
|
end
|
@@ -1568,11 +1496,11 @@ class Reline::LineEditor
|
|
1568
1496
|
history = Reline::HISTORY
|
1569
1497
|
end
|
1570
1498
|
case search_key
|
1571
|
-
when "\C-r"
|
1499
|
+
when "\C-r"
|
1572
1500
|
hit_index = history.rindex { |item|
|
1573
1501
|
item.include?(search_word)
|
1574
1502
|
}
|
1575
|
-
when "\C-s"
|
1503
|
+
when "\C-s"
|
1576
1504
|
hit_index = history.index { |item|
|
1577
1505
|
item.include?(search_word)
|
1578
1506
|
}
|
@@ -1583,9 +1511,9 @@ class Reline::LineEditor
|
|
1583
1511
|
end
|
1584
1512
|
end
|
1585
1513
|
case search_key
|
1586
|
-
when "\C-r"
|
1514
|
+
when "\C-r"
|
1587
1515
|
prompt_name = 'reverse-i-search'
|
1588
|
-
when "\C-s"
|
1516
|
+
when "\C-s"
|
1589
1517
|
prompt_name = 'i-search'
|
1590
1518
|
end
|
1591
1519
|
prompt_name = "failed #{prompt_name}" unless hit
|
@@ -1597,16 +1525,15 @@ class Reline::LineEditor
|
|
1597
1525
|
backup = @buffer_of_lines.dup, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history
|
1598
1526
|
searcher = generate_searcher(key)
|
1599
1527
|
@searching_prompt = "(reverse-i-search)`': "
|
1600
|
-
termination_keys = ["\C-j"
|
1601
|
-
termination_keys.concat(@config.isearch_terminators.chars
|
1528
|
+
termination_keys = ["\C-j"]
|
1529
|
+
termination_keys.concat(@config.isearch_terminators.chars) if @config.isearch_terminators
|
1602
1530
|
@waiting_proc = ->(k) {
|
1603
|
-
|
1604
|
-
if k == "\C-g".ord
|
1531
|
+
if k == "\C-g"
|
1605
1532
|
# cancel search and restore buffer
|
1606
1533
|
@buffer_of_lines, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history = backup
|
1607
1534
|
@searching_prompt = nil
|
1608
1535
|
@waiting_proc = nil
|
1609
|
-
elsif !termination_keys.include?(k) && (
|
1536
|
+
elsif !termination_keys.include?(k) && (k.match?(/[[:print:]]/) || k == "\C-h" || k == "\C-?" || k == "\C-r" || k == "\C-s")
|
1610
1537
|
search_word, prompt_name, hit_pointer = searcher.call(k)
|
1611
1538
|
Reline.last_incremental_search = search_word
|
1612
1539
|
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
@@ -1738,17 +1665,10 @@ class Reline::LineEditor
|
|
1738
1665
|
finish
|
1739
1666
|
end
|
1740
1667
|
else
|
1741
|
-
if @line_index ==
|
1742
|
-
if confirm_multiline_termination
|
1743
|
-
finish
|
1744
|
-
else
|
1745
|
-
key_newline(key)
|
1746
|
-
end
|
1747
|
-
else
|
1748
|
-
# should check confirm_multiline_termination to finish?
|
1749
|
-
@line_index = @buffer_of_lines.size - 1
|
1750
|
-
@byte_pointer = current_line.bytesize
|
1668
|
+
if @line_index == @buffer_of_lines.size - 1 && confirm_multiline_termination
|
1751
1669
|
finish
|
1670
|
+
else
|
1671
|
+
key_newline(key)
|
1752
1672
|
end
|
1753
1673
|
end
|
1754
1674
|
else
|
@@ -1756,6 +1676,11 @@ class Reline::LineEditor
|
|
1756
1676
|
end
|
1757
1677
|
end
|
1758
1678
|
|
1679
|
+
private def ed_force_submit(_key)
|
1680
|
+
process_insert(force: true)
|
1681
|
+
finish
|
1682
|
+
end
|
1683
|
+
|
1759
1684
|
private def em_delete_prev_char(key, arg: 1)
|
1760
1685
|
arg.times do
|
1761
1686
|
if @byte_pointer == 0 and @line_index > 0
|
@@ -1822,7 +1747,7 @@ class Reline::LineEditor
|
|
1822
1747
|
alias_method :kill_whole_line, :em_kill_line
|
1823
1748
|
|
1824
1749
|
private def em_delete(key)
|
1825
|
-
if buffer_empty? and key == "\C-d"
|
1750
|
+
if buffer_empty? and key == "\C-d"
|
1826
1751
|
@eof = true
|
1827
1752
|
finish
|
1828
1753
|
elsif @byte_pointer < current_line.bytesize
|
@@ -2240,20 +2165,9 @@ class Reline::LineEditor
|
|
2240
2165
|
end
|
2241
2166
|
|
2242
2167
|
private def ed_argument_digit(key)
|
2243
|
-
|
2244
|
-
|
2245
|
-
|
2246
|
-
unescaped_key = key ^ 0b10000000
|
2247
|
-
unless unescaped_key.chr.to_i.zero?
|
2248
|
-
@vi_arg = unescaped_key.chr.to_i
|
2249
|
-
end
|
2250
|
-
end
|
2251
|
-
else
|
2252
|
-
@vi_arg = key.chr.to_i
|
2253
|
-
end
|
2254
|
-
else
|
2255
|
-
@vi_arg = @vi_arg * 10 + key.chr.to_i
|
2256
|
-
end
|
2168
|
+
# key is expected to be `ESC digit` or `digit`
|
2169
|
+
num = key[/\d/].to_i
|
2170
|
+
@vi_arg = (@vi_arg || 0) * 10 + num
|
2257
2171
|
end
|
2258
2172
|
|
2259
2173
|
private def vi_to_column(key, arg: 0)
|
@@ -2272,7 +2186,7 @@ class Reline::LineEditor
|
|
2272
2186
|
before = current_line.byteslice(0, @byte_pointer)
|
2273
2187
|
remaining_point = @byte_pointer + byte_size
|
2274
2188
|
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2275
|
-
set_current_line(before + k
|
2189
|
+
set_current_line(before + k + after)
|
2276
2190
|
@waiting_proc = nil
|
2277
2191
|
elsif arg > 1
|
2278
2192
|
byte_size = 0
|
@@ -2282,7 +2196,7 @@ class Reline::LineEditor
|
|
2282
2196
|
before = current_line.byteslice(0, @byte_pointer)
|
2283
2197
|
remaining_point = @byte_pointer + byte_size
|
2284
2198
|
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2285
|
-
replaced = k
|
2199
|
+
replaced = k * arg
|
2286
2200
|
set_current_line(before + replaced + after, @byte_pointer + replaced.bytesize)
|
2287
2201
|
@waiting_proc = nil
|
2288
2202
|
end
|
@@ -2298,11 +2212,6 @@ class Reline::LineEditor
|
|
2298
2212
|
end
|
2299
2213
|
|
2300
2214
|
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2301
|
-
if key.instance_of?(String)
|
2302
|
-
inputted_char = key
|
2303
|
-
else
|
2304
|
-
inputted_char = key.chr
|
2305
|
-
end
|
2306
2215
|
prev_total = nil
|
2307
2216
|
total = nil
|
2308
2217
|
found = false
|
@@ -2313,7 +2222,7 @@ class Reline::LineEditor
|
|
2313
2222
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2314
2223
|
total = [mbchar.bytesize, width]
|
2315
2224
|
else
|
2316
|
-
if
|
2225
|
+
if key == mbchar
|
2317
2226
|
arg -= 1
|
2318
2227
|
if arg.zero?
|
2319
2228
|
found = true
|
@@ -2350,11 +2259,6 @@ class Reline::LineEditor
|
|
2350
2259
|
end
|
2351
2260
|
|
2352
2261
|
private def search_prev_char(key, arg, need_next_char = false)
|
2353
|
-
if key.instance_of?(String)
|
2354
|
-
inputted_char = key
|
2355
|
-
else
|
2356
|
-
inputted_char = key.chr
|
2357
|
-
end
|
2358
2262
|
prev_total = nil
|
2359
2263
|
total = nil
|
2360
2264
|
found = false
|
@@ -2365,7 +2269,7 @@ class Reline::LineEditor
|
|
2365
2269
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2366
2270
|
total = [mbchar.bytesize, width]
|
2367
2271
|
else
|
2368
|
-
if
|
2272
|
+
if key == mbchar
|
2369
2273
|
arg -= 1
|
2370
2274
|
if arg.zero?
|
2371
2275
|
found = true
|
@@ -2417,24 +2321,23 @@ class Reline::LineEditor
|
|
2417
2321
|
@config.editing_mode = :vi_insert
|
2418
2322
|
end
|
2419
2323
|
|
2420
|
-
private def
|
2421
|
-
@
|
2324
|
+
private def move_undo_redo(direction)
|
2325
|
+
@restoring = true
|
2326
|
+
return unless (0..@undo_redo_history.size - 1).cover?(@undo_redo_index + direction)
|
2422
2327
|
|
2423
|
-
|
2328
|
+
@undo_redo_index += direction
|
2329
|
+
buffer_of_lines, byte_pointer, line_index = @undo_redo_history[@undo_redo_index]
|
2330
|
+
@buffer_of_lines = buffer_of_lines.dup
|
2331
|
+
@line_index = line_index
|
2332
|
+
@byte_pointer = byte_pointer
|
2333
|
+
end
|
2424
2334
|
|
2425
|
-
|
2426
|
-
|
2427
|
-
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2335
|
+
private def undo(_key)
|
2336
|
+
move_undo_redo(-1)
|
2428
2337
|
end
|
2429
2338
|
|
2430
2339
|
private def redo(_key)
|
2431
|
-
|
2432
|
-
|
2433
|
-
return if @input_lines_position >= @input_lines.size - 1
|
2434
|
-
|
2435
|
-
@input_lines_position += 1
|
2436
|
-
target_lines, target_cursor_x, target_cursor_y = @input_lines[@input_lines_position]
|
2437
|
-
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2340
|
+
move_undo_redo(+1)
|
2438
2341
|
end
|
2439
2342
|
|
2440
2343
|
private def prev_action_state_value(type)
|