From c2bd5b84d06f5e74afce72e4e4af06d7176453b2 Mon Sep 17 00:00:00 2001 From: aycabta Date: Thu, 26 Nov 2020 19:13:34 +0900 Subject: [PATCH] [ruby/reline] Support bracketed paste mode https://github.com/ruby/reline/commit/d1a6869322 --- lib/reline.rb | 14 ++++++- lib/reline/ansi.rb | 45 ++++++++++++++++++++- lib/reline/config.rb | 1 + test/reline/yamatanooroti/test_rendering.rb | 18 +++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/lib/reline.rb b/lib/reline.rb index 2862f5bc64..6a5f9b2390 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -44,6 +44,7 @@ module Reline self.output = STDOUT yield self @completion_quote_character = nil + @bracketed_paste_finished = false end def encoding @@ -243,6 +244,10 @@ module Reline line_editor.input_key(c) line_editor.rerender } + if @bracketed_paste_finished + line_editor.rerender_all + @bracketed_paste_finished = false + end } if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished? prev_pasting_state = false @@ -275,8 +280,13 @@ module Reline buffer = [] loop do c = Reline::IOGate.getc - buffer << c - result = key_stroke.match_status(buffer) + if c == -1 + result = :unmatched + @bracketed_paste_finished = true + else + buffer << c + result = key_stroke.match_status(buffer) + end case result when :matched expanded = key_stroke.expand(buffer).map{ |expanded_c| diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb index f11dbb80f4..0b1a7e1745 100644 --- a/lib/reline/ansi.rb +++ b/lib/reline/ansi.rb @@ -1,4 +1,5 @@ require 'io/console' +require 'timeout' class Reline::ANSI def self.encoding @@ -67,7 +68,7 @@ class Reline::ANSI end @@buf = [] - def self.getc + def self.inner_getc unless @@buf.empty? return @@buf.shift end @@ -80,8 +81,48 @@ class Reline::ANSI nil end + @@in_bracketed_paste_mode = false + START_BRACKETED_PASTE = String.new("\e[200~,", encoding: Encoding::ASCII_8BIT) + END_BRACKETED_PASTE = String.new("\e[200~.", encoding: Encoding::ASCII_8BIT) + def self.getc_with_bracketed_paste + buffer = String.new(encoding: Encoding::ASCII_8BIT) + buffer << inner_getc + while START_BRACKETED_PASTE.start_with?(buffer) or END_BRACKETED_PASTE.start_with?(buffer) do + if START_BRACKETED_PASTE == buffer + @@in_bracketed_paste_mode = true + return inner_getc + elsif END_BRACKETED_PASTE == buffer + @@in_bracketed_paste_mode = false + ungetc(-1) + return inner_getc + end + begin + succ_c = nil + Timeout.timeout(Reline.core.config.keyseq_timeout * 100) { + succ_c = inner_getc + } + rescue Timeout::Error + break + else + buffer << succ_c + end + end + buffer.bytes.reverse_each do |ch| + ungetc ch + end + inner_getc + end + + def self.getc + if Reline.core.config.enable_bracketed_paste + getc_with_bracketed_paste + else + inner_getc + end + end + def self.in_pasting? - not Reline::IOGate.empty_buffer? + @@in_bracketed_paste_mode or (not Reline::IOGate.empty_buffer?) end def self.empty_buffer? diff --git a/lib/reline/config.rb b/lib/reline/config.rb index 370d100414..c66810d30e 100644 --- a/lib/reline/config.rb +++ b/lib/reline/config.rb @@ -37,6 +37,7 @@ class Reline::Config vi-cmd-mode-icon vi-ins-mode-icon emacs-mode-string + enable-bracketed-paste } VARIABLE_NAME_SYMBOLS = VARIABLE_NAMES.map { |v| :"#{v.tr(?-, ?_)}" } VARIABLE_NAME_SYMBOLS.each do |v| diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb index b82e30a7a7..7c87d9588a 100644 --- a/test/reline/yamatanooroti/test_rendering.rb +++ b/test/reline/yamatanooroti/test_rendering.rb @@ -423,6 +423,24 @@ begin EOC end + def test_enable_bracketed_paste + write_inputrc <<~LINES + set enable-bracketed-paste on + LINES + start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/bin/multiline_repl}, startup_message: 'Multiline REPL.') + write("\e[200~,") + write("def hoge\n 3\nend\n") + write("\e[200~.") + close + assert_screen(<<~EOC) + prompt> def hoge + prompt> 3 + prompt> end + => :hoge + prompt> + EOC + end + private def write_inputrc(content) File.open(@inputrc_file, 'w') do |f| f.write content