diff --git a/lib/reline.rb b/lib/reline.rb index b43584fc9b..f730f8af12 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -254,32 +254,35 @@ module Reline Reline::DEFAULT_DIALOG_CONTEXT = Array.new def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination) + unless confirm_multiline_termination + raise ArgumentError.new('#readmultiline needs block to confirm multiline termination') + end + Reline.update_iogate io_gate.with_raw_input do - unless confirm_multiline_termination - raise ArgumentError.new('#readmultiline needs block to confirm multiline termination') - end inner_readline(prompt, add_hist, true, &confirm_multiline_termination) + end - whole_buffer = line_editor.whole_buffer.dup - whole_buffer.taint if RUBY_VERSION < '2.7' - if add_hist and whole_buffer and whole_buffer.chomp("\n").size > 0 - Reline::HISTORY << whole_buffer - end + whole_buffer = line_editor.whole_buffer.dup + whole_buffer.taint if RUBY_VERSION < '2.7' + if add_hist and whole_buffer and whole_buffer.chomp("\n").size > 0 + Reline::HISTORY << whole_buffer + end - if line_editor.eof? - line_editor.reset_line - # Return nil if the input is aborted by C-d. - nil - else - whole_buffer - end + if line_editor.eof? + line_editor.reset_line + # Return nil if the input is aborted by C-d. + nil + else + whole_buffer end end def readline(prompt = '', add_hist = false) Reline.update_iogate - inner_readline(prompt, add_hist, false) + io_gate.with_raw_input do + inner_readline(prompt, add_hist, false) + end line = line_editor.line.dup line.taint if RUBY_VERSION < '2.7' diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb index 2d7c759ea2..0d7226b36f 100644 --- a/lib/reline/ansi.rb +++ b/lib/reline/ansi.rb @@ -151,7 +151,11 @@ class Reline::ANSI end def self.with_raw_input - @@input.raw { yield } + if @@input.tty? + @@input.raw(intr: true) { yield } + else + yield + end end @@buf = [] @@ -159,11 +163,13 @@ class Reline::ANSI unless @@buf.empty? return @@buf.shift end - until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte } - timeout_second -= 0.1 + until @@input.wait_readable(0.01) + timeout_second -= 0.01 return nil if timeout_second <= 0 - Reline.core.line_editor.resize + + Reline.core.line_editor.handle_signal end + c = @@input.getbyte (c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c rescue Errno::EIO # Maybe the I/O has been closed. diff --git a/lib/reline/general_io.rb b/lib/reline/general_io.rb index 0ac1c6c56d..d52151ad3c 100644 --- a/lib/reline/general_io.rb +++ b/lib/reline/general_io.rb @@ -46,6 +46,7 @@ class Reline::GeneralIO end c = nil loop do + Reline.core.line_editor.handle_signal result = @@input.wait_readable(0.1) next if result.nil? c = @@input.read(1) diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 1fcf5e0358..c236044e41 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -138,9 +138,6 @@ class Reline::LineEditor @screen_size = Reline::IOGate.get_screen_size reset_variables(prompt, encoding: encoding) @rendered_screen.base_y = Reline::IOGate.cursor_pos.y - Reline::IOGate.set_winch_handler do - @resized = true - end if ENV.key?('RELINE_ALT_SCROLLBAR') @full_block = '::' @upper_half_block = "''" @@ -164,7 +161,12 @@ class Reline::LineEditor end end - def resize + def handle_signal + handle_interrupted + handle_resized + end + + private def handle_resized return unless @resized @screen_size = Reline::IOGate.get_screen_size @@ -177,25 +179,35 @@ class Reline::LineEditor render_differential end + private def handle_interrupted + return unless @interrupted + + @interrupted = false + clear_dialogs + scrolldown = render_differential + Reline::IOGate.scroll_down scrolldown + Reline::IOGate.move_cursor_column 0 + @rendered_screen.lines = [] + @rendered_screen.cursor_y = 0 + case @old_trap + when 'DEFAULT', 'SYSTEM_DEFAULT' + raise Interrupt + when 'IGNORE' + # Do nothing + when 'EXIT' + exit + else + @old_trap.call if @old_trap.respond_to?(:call) + end + end + def set_signal_handlers - @old_trap = Signal.trap('INT') { - clear_dialogs - scrolldown = render_differential - Reline::IOGate.scroll_down scrolldown - Reline::IOGate.move_cursor_column 0 - @rendered_screen.lines = [] - @rendered_screen.cursor_y = 0 - case @old_trap - when 'DEFAULT', 'SYSTEM_DEFAULT' - raise Interrupt - when 'IGNORE' - # Do nothing - when 'EXIT' - exit - else - @old_trap.call if @old_trap.respond_to?(:call) - end - } + Reline::IOGate.set_winch_handler do + @resized = true + end + @old_trap = Signal.trap('INT') do + @interrupted = true + end end def finalize @@ -212,7 +224,6 @@ class Reline::LineEditor @encoding = encoding @is_multiline = false @finished = false - @cleared = false @history_pointer = nil @kill_ring ||= Reline::KillRing.new @vi_clipboard = '' @@ -234,6 +245,7 @@ class Reline::LineEditor @in_pasting = false @auto_indent_proc = nil @dialogs = [] + @interrupted = false @resized = false @cache = {} @rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0) @@ -541,19 +553,7 @@ class Reline::LineEditor screen_height - wrapped_cursor_y + screen_scroll_top - @rendered_screen.base_y - 1 end - def handle_cleared - return unless @cleared - - @cleared = false - Reline::IOGate.clear_screen - @screen_size = Reline::IOGate.get_screen_size - @rendered_screen.lines = [] - @rendered_screen.base_y = 0 - @rendered_screen.cursor_y = 0 - end - def rerender - handle_cleared render_differential unless @in_pasting end @@ -2074,7 +2074,11 @@ class Reline::LineEditor alias_method :yank_pop, :em_yank_pop private def ed_clear_screen(key) - @cleared = true + Reline::IOGate.clear_screen + @screen_size = Reline::IOGate.get_screen_size + @rendered_screen.lines = [] + @rendered_screen.base_y = 0 + @rendered_screen.cursor_y = 0 end alias_method :clear_screen, :ed_clear_screen diff --git a/lib/reline/windows.rb b/lib/reline/windows.rb index 28f28e15cc..ee3f73e383 100644 --- a/lib/reline/windows.rb +++ b/lib/reline/windows.rb @@ -259,7 +259,7 @@ class Reline::Windows def self.check_input_event num_of_events = 0.chr * 8 while @@output_buf.empty? - Reline.core.line_editor.resize + Reline.core.line_editor.handle_signal if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec # prevent for background consolemode change @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0) diff --git a/test/reline/test_key_actor_emacs.rb b/test/reline/test_key_actor_emacs.rb index 2311af0f5a..0dff275f6e 100644 --- a/test/reline/test_key_actor_emacs.rb +++ b/test/reline/test_key_actor_emacs.rb @@ -255,18 +255,18 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase end def test_ed_clear_screen - refute(@line_editor.instance_variable_get(:@cleared)) + @line_editor.instance_variable_get(:@rendered_screen).lines = [[]] input_keys("\C-l", false) - assert(@line_editor.instance_variable_get(:@cleared)) + assert_empty(@line_editor.instance_variable_get(:@rendered_screen).lines) end def test_ed_clear_screen_with_inputed input_keys('abc') input_keys("\C-b", false) - refute(@line_editor.instance_variable_get(:@cleared)) + @line_editor.instance_variable_get(:@rendered_screen).lines = [[]] assert_line_around_cursor('ab', 'c') input_keys("\C-l", false) - assert(@line_editor.instance_variable_get(:@cleared)) + assert_empty(@line_editor.instance_variable_get(:@rendered_screen).lines) assert_line_around_cursor('ab', 'c') end