diff --git a/lib/reline/io/ansi.rb b/lib/reline/io/ansi.rb index 918be5c51e..d117ad5773 100644 --- a/lib/reline/io/ansi.rb +++ b/lib/reline/io/ansi.rb @@ -124,6 +124,10 @@ def inner_getc(timeout_second) Reline.core.line_editor.handle_signal end c = @input.getbyte + + # When "Escape non-ASCII Input with Control-V" is enabled in macOS Terminal.app, + # all non-ascii bytes are automatically escaped with `C-v`. + # "\xE3\x81\x82" (U+3042) becomes "\x16\xE3\x16\x81\x16\x82". (c == 0x16 && @input.tty? && @input.raw(min: 0, time: 0, &:getbyte)) || c rescue Errno::EIO # Maybe the I/O has been closed. diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb index aff5c0462b..8767684371 100644 --- a/test/reline/yamatanooroti/test_rendering.rb +++ b/test/reline/yamatanooroti/test_rendering.rb @@ -52,6 +52,14 @@ def teardown ENV.delete('RELINE_TEST_PROMPT') if ENV['RELINE_TEST_PROMPT'] end + # Yamatanooroti::TestCase#write converts 0x80-0xff bytes to "\e"+(byte&0x7f).chr + # This method avoids the conversion and writes the string as is. + def write_without_meta_conversion(str) + sync + @pty_input.write(str) + try_sync + end + def test_history_back start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') write(":a\n") @@ -561,6 +569,27 @@ def test_bracketed_paste close end + def test_ctrl_v_escaped_input + omit if Reline.core.io_gate.win? + + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.') + + # When a configuration "Escape non-ASCII Input with Control-V" is enabled + # in macOS Termina.app, all non-ascii characters are escaped with ^V. + write_without_meta_conversion "\C-v\xE3\C-v\x81\C-v\x82" + assert_screen(/prompt> あ/) + + # Bracketed pasted content is also escaped + write_without_meta_conversion "\e[200~\C-a\C-v\xE3\C-v\x81\C-v\x84\C-b\e[201~" + assert_screen(/prompt> あ\^Aい\^B/) + + # Invalid bytes should be ignored with timeout + write_without_meta_conversion "\C-v\x82\C-v\xA4" + sleep 1 + write 'C' + assert_screen(/prompt> あ\^Aい\^BC/) + end + def test_bracketed_paste_with_undo_redo omit if Reline.core.io_gate.win? start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')