reline 0.5.11 → 0.6.0
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 +40 -63
- data/lib/reline/io/dumb.rb +15 -1
- 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 +26 -16
- data/lib/reline/line_editor.rb +198 -352
- data/lib/reline/unicode.rb +119 -391
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +14 -14
- metadata +6 -4
- data/lib/reline/terminfo.rb +0 -158
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
|
@@ -36,7 +35,6 @@ class Reline::LineEditor
|
|
36
35
|
|
37
36
|
module CompletionState
|
38
37
|
NORMAL = :normal
|
39
|
-
COMPLETION = :completion
|
40
38
|
MENU = :menu
|
41
39
|
MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
|
42
40
|
PERFECT_MATCH = :perfect_match
|
@@ -254,7 +252,7 @@ class Reline::LineEditor
|
|
254
252
|
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
|
255
253
|
@input_lines = [[[""], 0, 0]]
|
256
254
|
@input_lines_position = 0
|
257
|
-
@
|
255
|
+
@restoring = false
|
258
256
|
@prev_action_state = NullActionState
|
259
257
|
@next_action_state = NullActionState
|
260
258
|
reset_line
|
@@ -266,7 +264,6 @@ class Reline::LineEditor
|
|
266
264
|
@line_index = 0
|
267
265
|
@cache.clear
|
268
266
|
@line_backup_in_history = nil
|
269
|
-
@multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
|
270
267
|
end
|
271
268
|
|
272
269
|
def multiline_on
|
@@ -300,8 +297,8 @@ class Reline::LineEditor
|
|
300
297
|
end
|
301
298
|
end
|
302
299
|
|
303
|
-
private def
|
304
|
-
Reline::Unicode.
|
300
|
+
private def split_line_by_width(str, max_width, offset: 0)
|
301
|
+
Reline::Unicode.split_line_by_width(str, max_width, encoding, offset: offset)
|
305
302
|
end
|
306
303
|
|
307
304
|
def current_byte_pointer_cursor
|
@@ -391,8 +388,8 @@ class Reline::LineEditor
|
|
391
388
|
if (cached = cached_wraps[[prompt, line]])
|
392
389
|
next cached
|
393
390
|
end
|
394
|
-
*wrapped_prompts, code_line_prompt =
|
395
|
-
wrapped_lines =
|
391
|
+
*wrapped_prompts, code_line_prompt = split_line_by_width(prompt, width)
|
392
|
+
wrapped_lines = split_line_by_width(line, width, offset: calculate_width(code_line_prompt, true))
|
396
393
|
wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
|
397
394
|
end
|
398
395
|
end
|
@@ -416,7 +413,7 @@ class Reline::LineEditor
|
|
416
413
|
# do nothing
|
417
414
|
elsif level == :blank
|
418
415
|
Reline::IOGate.move_cursor_column base_x
|
419
|
-
|
416
|
+
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{' ' * width}"
|
420
417
|
else
|
421
418
|
x, w, content = new_items[level]
|
422
419
|
cover_begin = base_x != 0 && new_levels[base_x - 1] == level
|
@@ -426,7 +423,7 @@ class Reline::LineEditor
|
|
426
423
|
content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
|
427
424
|
end
|
428
425
|
Reline::IOGate.move_cursor_column x + pos
|
429
|
-
|
426
|
+
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{content}#{Reline::IOGate.reset_color_sequence}"
|
430
427
|
end
|
431
428
|
base_x += width
|
432
429
|
end
|
@@ -439,8 +436,8 @@ class Reline::LineEditor
|
|
439
436
|
# Calculate cursor position in word wrapped content.
|
440
437
|
def wrapped_cursor_position
|
441
438
|
prompt_width = calculate_width(prompt_list[@line_index], true)
|
442
|
-
line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
|
443
|
-
wrapped_line_before_cursor =
|
439
|
+
line_before_cursor = Reline::Unicode.escape_for_print(whole_lines[@line_index].byteslice(0, @byte_pointer))
|
440
|
+
wrapped_line_before_cursor = split_line_by_width(' ' * prompt_width + line_before_cursor, screen_width)
|
444
441
|
wrapped_cursor_y = wrapped_prompt_and_input_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
|
445
442
|
wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
|
446
443
|
[wrapped_cursor_x, wrapped_cursor_y]
|
@@ -462,19 +459,21 @@ class Reline::LineEditor
|
|
462
459
|
end
|
463
460
|
|
464
461
|
def render_finished
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
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
|
470
470
|
end
|
471
|
-
@output.puts lines.map { |l| "#{l}\r\n" }.join
|
472
471
|
end
|
473
472
|
|
474
473
|
def print_nomultiline_prompt
|
475
474
|
Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
|
476
475
|
# Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
|
477
|
-
|
476
|
+
Reline::IOGate.write Reline::Unicode.strip_non_printing_start_end(@prompt) if @prompt && !@is_multiline
|
478
477
|
ensure
|
479
478
|
Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
|
480
479
|
end
|
@@ -505,7 +504,9 @@ class Reline::LineEditor
|
|
505
504
|
end
|
506
505
|
end
|
507
506
|
|
508
|
-
|
507
|
+
Reline::IOGate.buffered_output do
|
508
|
+
render_differential new_lines, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top
|
509
|
+
end
|
509
510
|
end
|
510
511
|
|
511
512
|
# Reflects lines to be rendered and new cursor position to the screen
|
@@ -579,8 +580,9 @@ class Reline::LineEditor
|
|
579
580
|
@context
|
580
581
|
end
|
581
582
|
|
582
|
-
def retrieve_completion_block(
|
583
|
-
@line_editor.retrieve_completion_block
|
583
|
+
def retrieve_completion_block(_unused = false)
|
584
|
+
preposing, target, postposing, _quote = @line_editor.retrieve_completion_block
|
585
|
+
[preposing, target, postposing]
|
584
586
|
end
|
585
587
|
|
586
588
|
def call_completion_proc_with_checking_args(pre, target, post)
|
@@ -800,105 +802,73 @@ class Reline::LineEditor
|
|
800
802
|
@config.editing_mode
|
801
803
|
end
|
802
804
|
|
803
|
-
private def menu(
|
805
|
+
private def menu(list)
|
804
806
|
@menu_info = MenuInfo.new(list)
|
805
807
|
end
|
806
808
|
|
807
|
-
private def
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
809
|
+
private def filter_normalize_candidates(target, list)
|
810
|
+
target = target.downcase if @config.completion_ignore_case
|
811
|
+
list.select do |item|
|
812
|
+
next unless item
|
813
|
+
unless Encoding.compatible?(target.encoding, item.encoding)
|
814
|
+
# Workaround for Readline test
|
815
|
+
if defined?(::Readline) && ::Readline == ::Reline
|
816
|
+
raise Encoding::CompatibilityError, "incompatible character encodings: #{target.encoding} and #{item.encoding}"
|
817
|
+
end
|
812
818
|
end
|
819
|
+
|
813
820
|
if @config.completion_ignore_case
|
814
|
-
|
821
|
+
item.downcase.start_with?(target)
|
815
822
|
else
|
816
|
-
|
817
|
-
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
|
823
|
+
item.start_with?(target)
|
847
824
|
end
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
825
|
+
end.map do |item|
|
826
|
+
item.unicode_normalize
|
827
|
+
rescue Encoding::CompatibilityError
|
828
|
+
item
|
829
|
+
end.uniq
|
852
830
|
end
|
853
831
|
|
854
|
-
private def perform_completion(
|
832
|
+
private def perform_completion(preposing, target, postposing, quote, list)
|
833
|
+
candidates = filter_normalize_candidates(target, list)
|
834
|
+
|
855
835
|
case @completion_state
|
856
|
-
when CompletionState::NORMAL
|
857
|
-
@completion_state = CompletionState::COMPLETION
|
858
836
|
when CompletionState::PERFECT_MATCH
|
859
837
|
if @dig_perfect_match_proc
|
860
|
-
@dig_perfect_match_proc.(@perfect_matched)
|
861
|
-
|
862
|
-
@completion_state = CompletionState::COMPLETION
|
838
|
+
@dig_perfect_match_proc.call(@perfect_matched)
|
839
|
+
return
|
863
840
|
end
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
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
|
841
|
+
when CompletionState::MENU
|
842
|
+
menu(candidates)
|
843
|
+
return
|
844
|
+
when CompletionState::MENU_WITH_PERFECT_MATCH
|
845
|
+
menu(candidates)
|
876
846
|
@completion_state = CompletionState::PERFECT_MATCH
|
847
|
+
return
|
877
848
|
end
|
878
|
-
|
879
|
-
|
880
|
-
return if completed.
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
end
|
891
|
-
@perfect_matched = completed
|
849
|
+
|
850
|
+
completed = Reline::Unicode.common_prefix(candidates, ignore_case: @config.completion_ignore_case)
|
851
|
+
return if completed.empty?
|
852
|
+
|
853
|
+
append_character = ''
|
854
|
+
if candidates.include?(completed)
|
855
|
+
if candidates.one?
|
856
|
+
append_character = quote || completion_append_character.to_s
|
857
|
+
@completion_state = CompletionState::PERFECT_MATCH
|
858
|
+
elsif @config.show_all_if_ambiguous
|
859
|
+
menu(candidates)
|
860
|
+
@completion_state = CompletionState::PERFECT_MATCH
|
892
861
|
else
|
893
|
-
@completion_state = CompletionState::
|
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
|
862
|
+
@completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
|
900
863
|
end
|
864
|
+
@perfect_matched = completed
|
865
|
+
else
|
866
|
+
@completion_state = CompletionState::MENU
|
867
|
+
menu(candidates) if @config.show_all_if_ambiguous
|
901
868
|
end
|
869
|
+
@buffer_of_lines[@line_index] = (preposing + completed + append_character + postposing).split("\n")[@line_index] || String.new(encoding: encoding)
|
870
|
+
line_to_pointer = (preposing + completed + append_character).split("\n")[@line_index] || String.new(encoding: encoding)
|
871
|
+
@byte_pointer = line_to_pointer.bytesize
|
902
872
|
end
|
903
873
|
|
904
874
|
def dialog_proc_scope_completion_journey_data
|
@@ -927,8 +897,8 @@ class Reline::LineEditor
|
|
927
897
|
end
|
928
898
|
|
929
899
|
private def retrieve_completion_journey_state
|
930
|
-
preposing, target, postposing = retrieve_completion_block
|
931
|
-
list = call_completion_proc
|
900
|
+
preposing, target, postposing, quote = retrieve_completion_block
|
901
|
+
list = call_completion_proc(preposing, target, postposing, quote)
|
932
902
|
return unless list.is_a?(Array)
|
933
903
|
|
934
904
|
candidates = list.select{ |item| item.start_with?(target) }
|
@@ -1003,10 +973,18 @@ class Reline::LineEditor
|
|
1003
973
|
@drop_terminate_spaces = false
|
1004
974
|
end
|
1005
975
|
|
976
|
+
ARGUMENT_DIGIT_METHODS = %i[ed_digit vi_zero ed_argument_digit]
|
977
|
+
VI_WAITING_ACCEPT_METHODS = %i[vi_change_meta vi_delete_meta vi_yank ed_insert ed_argument_digit]
|
978
|
+
|
1006
979
|
private def process_key(key, method_symbol)
|
1007
|
-
if
|
1008
|
-
cleanup_waiting
|
1009
|
-
|
980
|
+
if @waiting_proc
|
981
|
+
cleanup_waiting unless key.size == 1
|
982
|
+
end
|
983
|
+
if @vi_waiting_operator
|
984
|
+
cleanup_waiting unless VI_WAITING_ACCEPT_METHODS.include?(method_symbol) || VI_MOTIONS.include?(method_symbol)
|
985
|
+
end
|
986
|
+
|
987
|
+
if @waiting_proc
|
1010
988
|
old_byte_pointer = @byte_pointer
|
1011
989
|
@waiting_proc.call(key)
|
1012
990
|
if @vi_waiting_operator
|
@@ -1020,23 +998,14 @@ class Reline::LineEditor
|
|
1020
998
|
return
|
1021
999
|
end
|
1022
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)
|
1003
|
+
|
1023
1004
|
if method_symbol and respond_to?(method_symbol, true)
|
1024
1005
|
method_obj = method(method_symbol)
|
1025
1006
|
end
|
1026
|
-
if
|
1027
|
-
if
|
1028
|
-
run_for_operators(key, method_symbol) do |with_operator|
|
1029
|
-
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
1030
|
-
end
|
1031
|
-
else
|
1032
|
-
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
1033
|
-
end
|
1034
|
-
@kill_ring.process
|
1035
|
-
if @vi_arg
|
1036
|
-
@vi_arg = nil
|
1037
|
-
end
|
1038
|
-
elsif @vi_arg
|
1039
|
-
if key.chr =~ /[0-9]/
|
1007
|
+
if @vi_arg
|
1008
|
+
if ARGUMENT_DIGIT_METHODS.include?(method_symbol)
|
1040
1009
|
ed_argument_digit(key)
|
1041
1010
|
else
|
1042
1011
|
if argumentable?(method_obj)
|
@@ -1045,13 +1014,9 @@ class Reline::LineEditor
|
|
1045
1014
|
end
|
1046
1015
|
elsif method_obj
|
1047
1016
|
wrap_method_call(method_symbol, method_obj, key)
|
1048
|
-
else
|
1049
|
-
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1050
1017
|
end
|
1051
1018
|
@kill_ring.process
|
1052
|
-
|
1053
|
-
@vi_arg = nil
|
1054
|
-
end
|
1019
|
+
@vi_arg = nil
|
1055
1020
|
end
|
1056
1021
|
elsif method_obj
|
1057
1022
|
if method_symbol == :ed_argument_digit
|
@@ -1062,30 +1027,6 @@ class Reline::LineEditor
|
|
1062
1027
|
end
|
1063
1028
|
end
|
1064
1029
|
@kill_ring.process
|
1065
|
-
else
|
1066
|
-
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1067
|
-
end
|
1068
|
-
end
|
1069
|
-
|
1070
|
-
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
|
1082
|
-
method_symbol = @config.editing_mode.get_method(key.combined_char)
|
1083
|
-
process_key(key.combined_char, method_symbol)
|
1084
|
-
@multibyte_buffer.clear
|
1085
|
-
end
|
1086
|
-
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1087
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1088
|
-
@byte_pointer -= byte_size
|
1089
1030
|
end
|
1090
1031
|
end
|
1091
1032
|
|
@@ -1102,23 +1043,23 @@ class Reline::LineEditor
|
|
1102
1043
|
def input_key(key)
|
1103
1044
|
save_old_buffer
|
1104
1045
|
@config.reset_oneshot_key_bindings
|
1105
|
-
@dialogs.each do |dialog|
|
1106
|
-
if key.char.instance_of?(Symbol) and key.char == dialog.name
|
1107
|
-
return
|
1108
|
-
end
|
1109
|
-
end
|
1110
1046
|
if key.char.nil?
|
1111
1047
|
process_insert(force: true)
|
1112
1048
|
@eof = buffer_empty?
|
1113
1049
|
finish
|
1114
1050
|
return
|
1115
1051
|
end
|
1052
|
+
@dialogs.each do |dialog|
|
1053
|
+
if key.method_symbol == dialog.name
|
1054
|
+
return
|
1055
|
+
end
|
1056
|
+
end
|
1116
1057
|
@completion_occurs = false
|
1117
1058
|
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1059
|
+
process_key(key.char, key.method_symbol)
|
1060
|
+
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1061
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1062
|
+
@byte_pointer -= byte_size
|
1122
1063
|
end
|
1123
1064
|
|
1124
1065
|
@prev_action_state, @next_action_state = @next_action_state, NullActionState
|
@@ -1128,8 +1069,8 @@ class Reline::LineEditor
|
|
1128
1069
|
@completion_journey_state = nil
|
1129
1070
|
end
|
1130
1071
|
|
1131
|
-
push_input_lines unless @
|
1132
|
-
@
|
1072
|
+
push_input_lines unless @restoring
|
1073
|
+
@restoring = false
|
1133
1074
|
|
1134
1075
|
if @in_pasting
|
1135
1076
|
clear_dialogs
|
@@ -1178,9 +1119,8 @@ class Reline::LineEditor
|
|
1178
1119
|
end
|
1179
1120
|
end
|
1180
1121
|
|
1181
|
-
def call_completion_proc
|
1182
|
-
|
1183
|
-
pre, target, post = result
|
1122
|
+
def call_completion_proc(pre, target, post, quote)
|
1123
|
+
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
1184
1124
|
result = call_completion_proc_with_checking_args(pre, target, post)
|
1185
1125
|
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
1186
1126
|
result
|
@@ -1244,84 +1184,32 @@ class Reline::LineEditor
|
|
1244
1184
|
process_auto_indent
|
1245
1185
|
end
|
1246
1186
|
|
1247
|
-
def
|
1248
|
-
|
1249
|
-
|
1250
|
-
@line_index = line_index
|
1251
|
-
if byte_pointer
|
1252
|
-
@byte_pointer = byte_pointer
|
1253
|
-
else
|
1254
|
-
calculate_nearest_cursor(cursor)
|
1255
|
-
end
|
1256
|
-
process_auto_indent
|
1257
|
-
end
|
1258
|
-
|
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
|
1187
|
+
def retrieve_completion_block
|
1188
|
+
quote_characters = Reline.completer_quote_characters
|
1189
|
+
before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
|
1273
1190
|
quote = nil
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
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)
|
1191
|
+
# Calcualte closing quote when cursor is at the end of the line
|
1192
|
+
if current_line.bytesize == @byte_pointer && !quote_characters.empty?
|
1193
|
+
escaped = false
|
1194
|
+
before.each do |c|
|
1195
|
+
if escaped
|
1196
|
+
escaped = false
|
1197
|
+
next
|
1198
|
+
elsif c == '\\'
|
1199
|
+
escaped = true
|
1200
|
+
elsif quote
|
1201
|
+
quote = nil if c == quote
|
1202
|
+
elsif quote_characters.include?(c)
|
1203
|
+
quote = c
|
1314
1204
|
end
|
1315
1205
|
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
1206
|
end
|
1207
|
+
|
1208
|
+
word_break_characters = quote_characters + Reline.completer_word_break_characters
|
1209
|
+
break_index = before.rindex { |c| word_break_characters.include?(c) || quote_characters.include?(c) } || -1
|
1210
|
+
preposing = before.take(break_index + 1).join
|
1211
|
+
target = before.drop(break_index + 1).join
|
1212
|
+
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
1325
1213
|
lines = whole_lines
|
1326
1214
|
if @line_index > 0
|
1327
1215
|
preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
|
@@ -1329,7 +1217,7 @@ class Reline::LineEditor
|
|
1329
1217
|
if (lines.size - 1) > @line_index
|
1330
1218
|
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
|
1331
1219
|
end
|
1332
|
-
[preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding)]
|
1220
|
+
[preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding), quote&.encode(encoding)]
|
1333
1221
|
end
|
1334
1222
|
|
1335
1223
|
def confirm_multiline_termination
|
@@ -1338,7 +1226,6 @@ class Reline::LineEditor
|
|
1338
1226
|
end
|
1339
1227
|
|
1340
1228
|
def insert_multiline_text(text)
|
1341
|
-
save_old_buffer
|
1342
1229
|
pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer)
|
1343
1230
|
post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..)
|
1344
1231
|
lines = (pre + Reline::Unicode.safe_encode(text, encoding).gsub(/\r\n?/, "\n") + post).split("\n", -1)
|
@@ -1346,7 +1233,6 @@ class Reline::LineEditor
|
|
1346
1233
|
@buffer_of_lines[@line_index, 1] = lines
|
1347
1234
|
@line_index += lines.size - 1
|
1348
1235
|
@byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize
|
1349
|
-
push_input_lines
|
1350
1236
|
end
|
1351
1237
|
|
1352
1238
|
def insert_text(text)
|
@@ -1460,10 +1346,11 @@ class Reline::LineEditor
|
|
1460
1346
|
@completion_occurs = move_completed_list(:down)
|
1461
1347
|
else
|
1462
1348
|
@completion_journey_state = nil
|
1463
|
-
|
1349
|
+
pre, target, post, quote = retrieve_completion_block
|
1350
|
+
result = call_completion_proc(pre, target, post, quote)
|
1464
1351
|
if result.is_a?(Array)
|
1465
1352
|
@completion_occurs = true
|
1466
|
-
perform_completion(
|
1353
|
+
perform_completion(pre, target, post, quote, result)
|
1467
1354
|
end
|
1468
1355
|
end
|
1469
1356
|
end
|
@@ -1511,21 +1398,11 @@ class Reline::LineEditor
|
|
1511
1398
|
# digit or if the existing argument is already greater than a
|
1512
1399
|
# million.
|
1513
1400
|
# GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
|
1514
|
-
private def ed_insert(
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
return
|
1520
|
-
end
|
1521
|
-
str = key
|
1522
|
-
else
|
1523
|
-
begin
|
1524
|
-
key.chr.encode(Encoding::UTF_8)
|
1525
|
-
rescue Encoding::UndefinedConversionError
|
1526
|
-
return
|
1527
|
-
end
|
1528
|
-
str = key.chr
|
1401
|
+
private def ed_insert(str)
|
1402
|
+
begin
|
1403
|
+
str.encode(Encoding::UTF_8)
|
1404
|
+
rescue Encoding::UndefinedConversionError
|
1405
|
+
return
|
1529
1406
|
end
|
1530
1407
|
if @in_pasting
|
1531
1408
|
@continuous_insertion_buffer << str
|
@@ -1539,21 +1416,16 @@ class Reline::LineEditor
|
|
1539
1416
|
alias_method :ed_digit, :ed_insert
|
1540
1417
|
alias_method :self_insert, :ed_insert
|
1541
1418
|
|
1542
|
-
private def
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
else
|
1550
|
-
ed_insert(key)
|
1551
|
-
end
|
1419
|
+
private def insert_raw_char(str, arg: 1)
|
1420
|
+
arg.times do
|
1421
|
+
if str == "\C-j" or str == "\C-m"
|
1422
|
+
key_newline(str)
|
1423
|
+
elsif str != "\0"
|
1424
|
+
# Ignore NUL.
|
1425
|
+
ed_insert(str)
|
1552
1426
|
end
|
1553
|
-
|
1554
|
-
}
|
1427
|
+
end
|
1555
1428
|
end
|
1556
|
-
alias_method :quoted_insert, :ed_quoted_insert
|
1557
1429
|
|
1558
1430
|
private def ed_next_char(key, arg: 1)
|
1559
1431
|
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
@@ -1582,7 +1454,7 @@ class Reline::LineEditor
|
|
1582
1454
|
alias_method :backward_char, :ed_prev_char
|
1583
1455
|
|
1584
1456
|
private def vi_first_print(key)
|
1585
|
-
@byte_pointer
|
1457
|
+
@byte_pointer = Reline::Unicode.vi_first_print(current_line)
|
1586
1458
|
end
|
1587
1459
|
|
1588
1460
|
private def ed_move_to_beg(key)
|
@@ -1598,26 +1470,21 @@ class Reline::LineEditor
|
|
1598
1470
|
|
1599
1471
|
private def generate_searcher(search_key)
|
1600
1472
|
search_word = String.new(encoding: encoding)
|
1601
|
-
multibyte_buf = String.new(encoding: 'ASCII-8BIT')
|
1602
1473
|
hit_pointer = nil
|
1603
1474
|
lambda do |key|
|
1604
1475
|
search_again = false
|
1605
1476
|
case key
|
1606
|
-
when "\C-h"
|
1477
|
+
when "\C-h", "\C-?"
|
1607
1478
|
grapheme_clusters = search_word.grapheme_clusters
|
1608
1479
|
if grapheme_clusters.size > 0
|
1609
1480
|
grapheme_clusters.pop
|
1610
1481
|
search_word = grapheme_clusters.join
|
1611
1482
|
end
|
1612
|
-
when "\C-r"
|
1483
|
+
when "\C-r", "\C-s"
|
1613
1484
|
search_again = true if search_key == key
|
1614
1485
|
search_key = key
|
1615
1486
|
else
|
1616
|
-
|
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
|
1487
|
+
search_word << key
|
1621
1488
|
end
|
1622
1489
|
hit = nil
|
1623
1490
|
if not search_word.empty? and @line_backup_in_history&.include?(search_word)
|
@@ -1630,10 +1497,10 @@ class Reline::LineEditor
|
|
1630
1497
|
end
|
1631
1498
|
if @history_pointer
|
1632
1499
|
case search_key
|
1633
|
-
when "\C-r"
|
1500
|
+
when "\C-r"
|
1634
1501
|
history_pointer_base = 0
|
1635
1502
|
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
1636
|
-
when "\C-s"
|
1503
|
+
when "\C-s"
|
1637
1504
|
history_pointer_base = @history_pointer + 1
|
1638
1505
|
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
1639
1506
|
end
|
@@ -1643,10 +1510,10 @@ class Reline::LineEditor
|
|
1643
1510
|
end
|
1644
1511
|
elsif @history_pointer
|
1645
1512
|
case search_key
|
1646
|
-
when "\C-r"
|
1513
|
+
when "\C-r"
|
1647
1514
|
history_pointer_base = 0
|
1648
1515
|
history = Reline::HISTORY[0..@history_pointer]
|
1649
|
-
when "\C-s"
|
1516
|
+
when "\C-s"
|
1650
1517
|
history_pointer_base = @history_pointer
|
1651
1518
|
history = Reline::HISTORY[@history_pointer..-1]
|
1652
1519
|
end
|
@@ -1655,11 +1522,11 @@ class Reline::LineEditor
|
|
1655
1522
|
history = Reline::HISTORY
|
1656
1523
|
end
|
1657
1524
|
case search_key
|
1658
|
-
when "\C-r"
|
1525
|
+
when "\C-r"
|
1659
1526
|
hit_index = history.rindex { |item|
|
1660
1527
|
item.include?(search_word)
|
1661
1528
|
}
|
1662
|
-
when "\C-s"
|
1529
|
+
when "\C-s"
|
1663
1530
|
hit_index = history.index { |item|
|
1664
1531
|
item.include?(search_word)
|
1665
1532
|
}
|
@@ -1670,9 +1537,9 @@ class Reline::LineEditor
|
|
1670
1537
|
end
|
1671
1538
|
end
|
1672
1539
|
case search_key
|
1673
|
-
when "\C-r"
|
1540
|
+
when "\C-r"
|
1674
1541
|
prompt_name = 'reverse-i-search'
|
1675
|
-
when "\C-s"
|
1542
|
+
when "\C-s"
|
1676
1543
|
prompt_name = 'i-search'
|
1677
1544
|
end
|
1678
1545
|
prompt_name = "failed #{prompt_name}" unless hit
|
@@ -1684,16 +1551,15 @@ class Reline::LineEditor
|
|
1684
1551
|
backup = @buffer_of_lines.dup, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history
|
1685
1552
|
searcher = generate_searcher(key)
|
1686
1553
|
@searching_prompt = "(reverse-i-search)`': "
|
1687
|
-
termination_keys = ["\C-j"
|
1688
|
-
termination_keys.concat(@config.isearch_terminators.chars
|
1554
|
+
termination_keys = ["\C-j"]
|
1555
|
+
termination_keys.concat(@config.isearch_terminators.chars) if @config.isearch_terminators
|
1689
1556
|
@waiting_proc = ->(k) {
|
1690
|
-
|
1691
|
-
if k == "\C-g".ord
|
1557
|
+
if k == "\C-g"
|
1692
1558
|
# cancel search and restore buffer
|
1693
1559
|
@buffer_of_lines, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history = backup
|
1694
1560
|
@searching_prompt = nil
|
1695
1561
|
@waiting_proc = nil
|
1696
|
-
elsif !termination_keys.include?(k) && (
|
1562
|
+
elsif !termination_keys.include?(k) && (k.match?(/[[:print:]]/) || k == "\C-h" || k == "\C-?" || k == "\C-r" || k == "\C-s")
|
1697
1563
|
search_word, prompt_name, hit_pointer = searcher.call(k)
|
1698
1564
|
Reline.last_incremental_search = search_word
|
1699
1565
|
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
@@ -1909,7 +1775,7 @@ class Reline::LineEditor
|
|
1909
1775
|
alias_method :kill_whole_line, :em_kill_line
|
1910
1776
|
|
1911
1777
|
private def em_delete(key)
|
1912
|
-
if buffer_empty? and key == "\C-d"
|
1778
|
+
if buffer_empty? and key == "\C-d"
|
1913
1779
|
@eof = true
|
1914
1780
|
finish
|
1915
1781
|
elsif @byte_pointer < current_line.bytesize
|
@@ -1927,9 +1793,11 @@ class Reline::LineEditor
|
|
1927
1793
|
if current_line.empty? or @byte_pointer < current_line.bytesize
|
1928
1794
|
em_delete(key)
|
1929
1795
|
elsif !@config.autocompletion # show completed list
|
1930
|
-
|
1796
|
+
pre, target, post, quote = retrieve_completion_block
|
1797
|
+
result = call_completion_proc(pre, target, post, quote)
|
1931
1798
|
if result.is_a?(Array)
|
1932
|
-
|
1799
|
+
candidates = filter_normalize_candidates(target, result)
|
1800
|
+
menu(candidates)
|
1933
1801
|
end
|
1934
1802
|
end
|
1935
1803
|
end
|
@@ -1961,7 +1829,7 @@ class Reline::LineEditor
|
|
1961
1829
|
|
1962
1830
|
private def em_next_word(key)
|
1963
1831
|
if current_line.bytesize > @byte_pointer
|
1964
|
-
byte_size
|
1832
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
1965
1833
|
@byte_pointer += byte_size
|
1966
1834
|
end
|
1967
1835
|
end
|
@@ -1969,7 +1837,7 @@ class Reline::LineEditor
|
|
1969
1837
|
|
1970
1838
|
private def ed_prev_word(key)
|
1971
1839
|
if @byte_pointer > 0
|
1972
|
-
byte_size
|
1840
|
+
byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
|
1973
1841
|
@byte_pointer -= byte_size
|
1974
1842
|
end
|
1975
1843
|
end
|
@@ -1977,7 +1845,7 @@ class Reline::LineEditor
|
|
1977
1845
|
|
1978
1846
|
private def em_delete_next_word(key)
|
1979
1847
|
if current_line.bytesize > @byte_pointer
|
1980
|
-
byte_size
|
1848
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
1981
1849
|
line, word = byteslice!(current_line, @byte_pointer, byte_size)
|
1982
1850
|
set_current_line(line)
|
1983
1851
|
@kill_ring.append(word)
|
@@ -1987,7 +1855,7 @@ class Reline::LineEditor
|
|
1987
1855
|
|
1988
1856
|
private def ed_delete_prev_word(key)
|
1989
1857
|
if @byte_pointer > 0
|
1990
|
-
byte_size
|
1858
|
+
byte_size = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
|
1991
1859
|
line, word = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
1992
1860
|
set_current_line(line, @byte_pointer - byte_size)
|
1993
1861
|
@kill_ring.append(word, true)
|
@@ -2027,7 +1895,7 @@ class Reline::LineEditor
|
|
2027
1895
|
|
2028
1896
|
private def em_capitol_case(key)
|
2029
1897
|
if current_line.bytesize > @byte_pointer
|
2030
|
-
byte_size,
|
1898
|
+
byte_size, new_str = Reline::Unicode.em_forward_word_with_capitalization(current_line, @byte_pointer)
|
2031
1899
|
before = current_line.byteslice(0, @byte_pointer)
|
2032
1900
|
after = current_line.byteslice((@byte_pointer + byte_size)..-1)
|
2033
1901
|
set_current_line(before + new_str + after, @byte_pointer + new_str.bytesize)
|
@@ -2037,7 +1905,7 @@ class Reline::LineEditor
|
|
2037
1905
|
|
2038
1906
|
private def em_lower_case(key)
|
2039
1907
|
if current_line.bytesize > @byte_pointer
|
2040
|
-
byte_size
|
1908
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2041
1909
|
part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
2042
1910
|
mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
|
2043
1911
|
}.join
|
@@ -2050,7 +1918,7 @@ class Reline::LineEditor
|
|
2050
1918
|
|
2051
1919
|
private def em_upper_case(key)
|
2052
1920
|
if current_line.bytesize > @byte_pointer
|
2053
|
-
byte_size
|
1921
|
+
byte_size = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2054
1922
|
part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
2055
1923
|
mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
|
2056
1924
|
}.join
|
@@ -2063,7 +1931,7 @@ class Reline::LineEditor
|
|
2063
1931
|
|
2064
1932
|
private def em_kill_region(key)
|
2065
1933
|
if @byte_pointer > 0
|
2066
|
-
byte_size
|
1934
|
+
byte_size = Reline::Unicode.em_big_backward_word(current_line, @byte_pointer)
|
2067
1935
|
line, deleted = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
2068
1936
|
set_current_line(line, @byte_pointer - byte_size)
|
2069
1937
|
@kill_ring.append(deleted, true)
|
@@ -2094,7 +1962,7 @@ class Reline::LineEditor
|
|
2094
1962
|
|
2095
1963
|
private def vi_next_word(key, arg: 1)
|
2096
1964
|
if current_line.bytesize > @byte_pointer
|
2097
|
-
byte_size
|
1965
|
+
byte_size = Reline::Unicode.vi_forward_word(current_line, @byte_pointer, @drop_terminate_spaces)
|
2098
1966
|
@byte_pointer += byte_size
|
2099
1967
|
end
|
2100
1968
|
arg -= 1
|
@@ -2103,7 +1971,7 @@ class Reline::LineEditor
|
|
2103
1971
|
|
2104
1972
|
private def vi_prev_word(key, arg: 1)
|
2105
1973
|
if @byte_pointer > 0
|
2106
|
-
byte_size
|
1974
|
+
byte_size = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
|
2107
1975
|
@byte_pointer -= byte_size
|
2108
1976
|
end
|
2109
1977
|
arg -= 1
|
@@ -2112,7 +1980,7 @@ class Reline::LineEditor
|
|
2112
1980
|
|
2113
1981
|
private def vi_end_word(key, arg: 1, inclusive: false)
|
2114
1982
|
if current_line.bytesize > @byte_pointer
|
2115
|
-
byte_size
|
1983
|
+
byte_size = Reline::Unicode.vi_forward_end_word(current_line, @byte_pointer)
|
2116
1984
|
@byte_pointer += byte_size
|
2117
1985
|
end
|
2118
1986
|
arg -= 1
|
@@ -2127,7 +1995,7 @@ class Reline::LineEditor
|
|
2127
1995
|
|
2128
1996
|
private def vi_next_big_word(key, arg: 1)
|
2129
1997
|
if current_line.bytesize > @byte_pointer
|
2130
|
-
byte_size
|
1998
|
+
byte_size = Reline::Unicode.vi_big_forward_word(current_line, @byte_pointer)
|
2131
1999
|
@byte_pointer += byte_size
|
2132
2000
|
end
|
2133
2001
|
arg -= 1
|
@@ -2136,7 +2004,7 @@ class Reline::LineEditor
|
|
2136
2004
|
|
2137
2005
|
private def vi_prev_big_word(key, arg: 1)
|
2138
2006
|
if @byte_pointer > 0
|
2139
|
-
byte_size
|
2007
|
+
byte_size = Reline::Unicode.vi_big_backward_word(current_line, @byte_pointer)
|
2140
2008
|
@byte_pointer -= byte_size
|
2141
2009
|
end
|
2142
2010
|
arg -= 1
|
@@ -2145,7 +2013,7 @@ class Reline::LineEditor
|
|
2145
2013
|
|
2146
2014
|
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
2147
2015
|
if current_line.bytesize > @byte_pointer
|
2148
|
-
byte_size
|
2016
|
+
byte_size = Reline::Unicode.vi_big_forward_end_word(current_line, @byte_pointer)
|
2149
2017
|
@byte_pointer += byte_size
|
2150
2018
|
end
|
2151
2019
|
arg -= 1
|
@@ -2325,20 +2193,9 @@ class Reline::LineEditor
|
|
2325
2193
|
end
|
2326
2194
|
|
2327
2195
|
private def ed_argument_digit(key)
|
2328
|
-
|
2329
|
-
|
2330
|
-
|
2331
|
-
unescaped_key = key ^ 0b10000000
|
2332
|
-
unless unescaped_key.chr.to_i.zero?
|
2333
|
-
@vi_arg = unescaped_key.chr.to_i
|
2334
|
-
end
|
2335
|
-
end
|
2336
|
-
else
|
2337
|
-
@vi_arg = key.chr.to_i
|
2338
|
-
end
|
2339
|
-
else
|
2340
|
-
@vi_arg = @vi_arg * 10 + key.chr.to_i
|
2341
|
-
end
|
2196
|
+
# key is expected to be `ESC digit` or `digit`
|
2197
|
+
num = key[/\d/].to_i
|
2198
|
+
@vi_arg = (@vi_arg || 0) * 10 + num
|
2342
2199
|
end
|
2343
2200
|
|
2344
2201
|
private def vi_to_column(key, arg: 0)
|
@@ -2357,7 +2214,7 @@ class Reline::LineEditor
|
|
2357
2214
|
before = current_line.byteslice(0, @byte_pointer)
|
2358
2215
|
remaining_point = @byte_pointer + byte_size
|
2359
2216
|
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2360
|
-
set_current_line(before + k
|
2217
|
+
set_current_line(before + k + after)
|
2361
2218
|
@waiting_proc = nil
|
2362
2219
|
elsif arg > 1
|
2363
2220
|
byte_size = 0
|
@@ -2367,7 +2224,7 @@ class Reline::LineEditor
|
|
2367
2224
|
before = current_line.byteslice(0, @byte_pointer)
|
2368
2225
|
remaining_point = @byte_pointer + byte_size
|
2369
2226
|
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2370
|
-
replaced = k
|
2227
|
+
replaced = k * arg
|
2371
2228
|
set_current_line(before + replaced + after, @byte_pointer + replaced.bytesize)
|
2372
2229
|
@waiting_proc = nil
|
2373
2230
|
end
|
@@ -2383,11 +2240,6 @@ class Reline::LineEditor
|
|
2383
2240
|
end
|
2384
2241
|
|
2385
2242
|
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2386
|
-
if key.instance_of?(String)
|
2387
|
-
inputted_char = key
|
2388
|
-
else
|
2389
|
-
inputted_char = key.chr
|
2390
|
-
end
|
2391
2243
|
prev_total = nil
|
2392
2244
|
total = nil
|
2393
2245
|
found = false
|
@@ -2398,7 +2250,7 @@ class Reline::LineEditor
|
|
2398
2250
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2399
2251
|
total = [mbchar.bytesize, width]
|
2400
2252
|
else
|
2401
|
-
if
|
2253
|
+
if key == mbchar
|
2402
2254
|
arg -= 1
|
2403
2255
|
if arg.zero?
|
2404
2256
|
found = true
|
@@ -2435,11 +2287,6 @@ class Reline::LineEditor
|
|
2435
2287
|
end
|
2436
2288
|
|
2437
2289
|
private def search_prev_char(key, arg, need_next_char = false)
|
2438
|
-
if key.instance_of?(String)
|
2439
|
-
inputted_char = key
|
2440
|
-
else
|
2441
|
-
inputted_char = key.chr
|
2442
|
-
end
|
2443
2290
|
prev_total = nil
|
2444
2291
|
total = nil
|
2445
2292
|
found = false
|
@@ -2450,7 +2297,7 @@ class Reline::LineEditor
|
|
2450
2297
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2451
2298
|
total = [mbchar.bytesize, width]
|
2452
2299
|
else
|
2453
|
-
if
|
2300
|
+
if key == mbchar
|
2454
2301
|
arg -= 1
|
2455
2302
|
if arg.zero?
|
2456
2303
|
found = true
|
@@ -2502,24 +2349,23 @@ class Reline::LineEditor
|
|
2502
2349
|
@config.editing_mode = :vi_insert
|
2503
2350
|
end
|
2504
2351
|
|
2505
|
-
private def
|
2506
|
-
@
|
2352
|
+
private def move_undo_redo(direction)
|
2353
|
+
@restoring = true
|
2354
|
+
return unless (0..@input_lines.size - 1).cover?(@input_lines_position + direction)
|
2507
2355
|
|
2508
|
-
|
2356
|
+
@input_lines_position += direction
|
2357
|
+
buffer_of_lines, byte_pointer, line_index = @input_lines[@input_lines_position]
|
2358
|
+
@buffer_of_lines = buffer_of_lines.dup
|
2359
|
+
@line_index = line_index
|
2360
|
+
@byte_pointer = byte_pointer
|
2361
|
+
end
|
2509
2362
|
|
2510
|
-
|
2511
|
-
|
2512
|
-
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2363
|
+
private def undo(_key)
|
2364
|
+
move_undo_redo(-1)
|
2513
2365
|
end
|
2514
2366
|
|
2515
2367
|
private def redo(_key)
|
2516
|
-
|
2517
|
-
|
2518
|
-
return if @input_lines_position >= @input_lines.size - 1
|
2519
|
-
|
2520
|
-
@input_lines_position += 1
|
2521
|
-
target_lines, target_cursor_x, target_cursor_y = @input_lines[@input_lines_position]
|
2522
|
-
set_current_lines(target_lines.dup, target_cursor_x, target_cursor_y)
|
2368
|
+
move_undo_redo(+1)
|
2523
2369
|
end
|
2524
2370
|
|
2525
2371
|
private def prev_action_state_value(type)
|