diff --git a/lib/reline.rb b/lib/reline.rb index f3fd28b627..c020ac2709 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -264,32 +264,35 @@ def get_screen_size 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 @@ def self.output=(val) 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 @@ def self.inner_getc(timeout_second) 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 @@ def self.getc(_timeout_second) 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 d202ba02d2..1a425838a8 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -117,9 +117,6 @@ def reset(prompt = '', encoding:) @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 = "''" @@ -143,7 +140,12 @@ def reset(prompt = '', encoding:) 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 @@ -156,25 +158,35 @@ def resize 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 @@ -191,7 +203,6 @@ def reset_variables(prompt = '', encoding:) @encoding = encoding @is_multiline = false @finished = false - @cleared = false @history_pointer = nil @kill_ring ||= Reline::KillRing.new @vi_clipboard = '' @@ -213,6 +224,7 @@ def reset_variables(prompt = '', encoding:) @in_pasting = false @auto_indent_proc = nil @dialogs = [] + @interrupted = false @resized = false @cache = {} @rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0) @@ -520,19 +532,7 @@ def rest_height(wrapped_cursor_y) 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 @@ -2046,7 +2046,11 @@ def finish 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 @@ def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, ch 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 @@ def test_em_delete_ends_editing 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