diff --git a/lib/reline.rb b/lib/reline.rb index 671c222ef7..52e1e399c3 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -343,13 +343,14 @@ def readline(prompt = '', add_hist = false) read_io(config.keyseq_timeout) { |inputs| line_editor.set_pasting_state(io_gate.in_pasting?) inputs.each do |key| - if key.method_symbol == :bracketed_paste_start - text = io_gate.read_bracketed_paste - line_editor.insert_multiline_text(text) - line_editor.scroll_into_view - else - line_editor.update(key) + case key.method_symbol + when :bracketed_paste_start + # io_gate is Reline::ANSI because the key :bracketed_paste_start is only assigned in Reline::ANSI + key = Reline::Key.new(io_gate.read_bracketed_paste, :insert_multiline_text) + when :quoted_insert, :ed_quoted_insert + key = Reline::Key.new(io_gate.read_single_char(config.keyseq_timeout), :insert_raw_char) end + line_editor.update(key) end } if line_editor.finished? diff --git a/lib/reline/io.rb b/lib/reline/io.rb index c1dd1a56c8..0f48f53b82 100644 --- a/lib/reline/io.rb +++ b/lib/reline/io.rb @@ -35,6 +35,20 @@ def win? def reset_color_sequence self.class::RESET_COLOR end + + # Read a single encoding valid character from the input. + def read_single_char(keyseq_timeout) + buffer = String.new(encoding: Encoding::ASCII_8BIT) + loop do + timeout = buffer.empty? ? Float::INFINITY : keyseq_timeout + c = getc(timeout) + return unless c + + buffer << c + encoded = buffer.dup.force_encoding(encoding) + return encoded if encoded.valid_encoding? + end + end end end diff --git a/lib/reline/key_actor/vi_insert.rb b/lib/reline/key_actor/vi_insert.rb index 9a0ff57253..235b6fdf38 100644 --- a/lib/reline/key_actor/vi_insert.rb +++ b/lib/reline/key_actor/vi_insert.rb @@ -97,25 +97,25 @@ module Reline::KeyActor # 47 / :ed_insert, # 48 0 - :ed_insert, + :ed_digit, # 49 1 - :ed_insert, + :ed_digit, # 50 2 - :ed_insert, + :ed_digit, # 51 3 - :ed_insert, + :ed_digit, # 52 4 - :ed_insert, + :ed_digit, # 53 5 - :ed_insert, + :ed_digit, # 54 6 - :ed_insert, + :ed_digit, # 55 7 - :ed_insert, + :ed_digit, # 56 8 - :ed_insert, + :ed_digit, # 57 9 - :ed_insert, + :ed_digit, # 58 : :ed_insert, # 59 ; diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 1b61d9abe7..69ce7f574c 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -973,6 +973,7 @@ def wrap_method_call(method_symbol, method_obj, key, with_operator = false) @drop_terminate_spaces = false end + ARGUMENT_DIGIT_METHODS = %i[ed_digit vi_zero ed_argument_digit] VI_WAITING_ACCEPT_METHODS = %i[vi_change_meta vi_delete_meta vi_yank ed_insert ed_argument_digit] private def process_key(key, method_symbol) @@ -1004,7 +1005,7 @@ def wrap_method_call(method_symbol, method_obj, key, with_operator = false) method_obj = method(method_symbol) end if @vi_arg - if key.match?(/\A\d\z/) + if ARGUMENT_DIGIT_METHODS.include?(method_symbol) ed_argument_digit(key) else if argumentable?(method_obj) @@ -1015,9 +1016,7 @@ def wrap_method_call(method_symbol, method_obj, key, with_operator = false) wrap_method_call(method_symbol, method_obj, key) end @kill_ring.process - if @vi_arg - @vi_arg = nil - end + @vi_arg = nil end elsif method_obj if method_symbol == :ed_argument_digit @@ -1227,7 +1226,6 @@ def confirm_multiline_termination end def insert_multiline_text(text) - save_old_buffer pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer) post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..) lines = (pre + Reline::Unicode.safe_encode(text, encoding).gsub(/\r\n?/, "\n") + post).split("\n", -1) @@ -1235,7 +1233,6 @@ def insert_multiline_text(text) @buffer_of_lines[@line_index, 1] = lines @line_index += lines.size - 1 @byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize - push_input_lines end def insert_text(text) @@ -1419,20 +1416,16 @@ def finish alias_method :ed_digit, :ed_insert alias_method :self_insert, :ed_insert - private def ed_quoted_insert(str, arg: 1) - @waiting_proc = proc { |key| - arg.times do - if key == "\C-j" or key == "\C-m" - key_newline(key) - elsif key != "\0" - # Ignore NUL. - ed_insert(key) - end + private def insert_raw_char(str, arg: 1) + arg.times do + if str == "\C-j" or str == "\C-m" + key_newline(str) + elsif str != "\0" + # Ignore NUL. + ed_insert(str) end - @waiting_proc = nil - } + end end - alias_method :quoted_insert, :ed_quoted_insert private def ed_next_char(key, arg: 1) byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer) diff --git a/test/reline/helper.rb b/test/reline/helper.rb index a0c1b097e6..37a99bb18a 100644 --- a/test/reline/helper.rb +++ b/test/reline/helper.rb @@ -108,9 +108,9 @@ class Reline::TestCase < Test::Unit::TestCase input end - def input_key_by_symbol(method_symbol, csi: false) - dummy_char = csi ? "\e[A" : "\C-a" - @line_editor.input_key(Reline::Key.new(dummy_char, method_symbol, false)) + def input_key_by_symbol(method_symbol, char: nil, csi: false) + char ||= csi ? "\e[A" : "\C-a" + @line_editor.input_key(Reline::Key.new(char, method_symbol, false)) end def input_keys(input, convert = true) diff --git a/test/reline/test_key_actor_emacs.rb b/test/reline/test_key_actor_emacs.rb index 4aea3a6547..e52c6c2d9a 100644 --- a/test/reline/test_key_actor_emacs.rb +++ b/test/reline/test_key_actor_emacs.rb @@ -138,11 +138,25 @@ def test_em_delete_prev_char_for_mbchar_by_plural_code_points assert_line_around_cursor("か\u3099", '') end + def test_bracketed_paste_insert + set_line_around_cursor('A', 'Z') + input_key_by_symbol(:insert_multiline_text, char: "abc\n\C-abc") + assert_whole_lines(['Aabc', "\C-abcZ"]) + assert_line_around_cursor("\C-abc", 'Z') + end + def test_ed_quoted_insert - input_keys("ab\C-v\C-acd") - assert_line_around_cursor("ab\C-acd", '') - input_keys("\C-q\C-b") - assert_line_around_cursor("ab\C-acd\C-b", '') + set_line_around_cursor('A', 'Z') + input_key_by_symbol(:insert_raw_char, char: "\C-a") + assert_line_around_cursor("A\C-a", 'Z') + end + + def test_ed_quoted_insert_with_vi_arg + input_keys("a\C-[3") + input_key_by_symbol(:insert_raw_char, char: "\C-a") + input_keys("b\C-[4") + input_key_by_symbol(:insert_raw_char, char: '1') + assert_line_around_cursor("a\C-a\C-a\C-ab1111", '') end def test_ed_kill_line @@ -1474,7 +1488,9 @@ def test_ed_search_prev_next_history_in_multibyte end def test_ignore_NUL_by_ed_quoted_insert - input_keys(%Q{"\C-v\C-@"}, false) + input_keys('"') + input_key_by_symbol(:insert_raw_char, char: 0.chr) + input_keys('"') assert_line_around_cursor('""', '') end diff --git a/test/reline/test_key_actor_vi.rb b/test/reline/test_key_actor_vi.rb index 6d5b7e20c4..928adace2b 100644 --- a/test/reline/test_key_actor_vi.rb +++ b/test/reline/test_key_actor_vi.rb @@ -344,13 +344,17 @@ def test_vi_end_big_word end def test_ed_quoted_insert - input_keys("ab\C-v\C-acd") - assert_line_around_cursor("ab\C-acd", '') + input_keys('ab') + input_key_by_symbol(:insert_raw_char, char: "\C-a") + assert_line_around_cursor("ab\C-a", '') end def test_ed_quoted_insert_with_vi_arg - input_keys("ab\C-[3\C-v\C-aacd") - assert_line_around_cursor("a\C-a\C-a\C-abcd", '') + input_keys("ab\C-[3") + input_key_by_symbol(:insert_raw_char, char: "\C-a") + input_keys('4') + input_key_by_symbol(:insert_raw_char, char: '1') + assert_line_around_cursor("a\C-a\C-a\C-a1111", 'b') end def test_vi_replace_char