summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomoya ishida <[email protected]>2024-05-09 01:00:26 +0900
committergit <[email protected]>2024-05-08 16:00:30 +0000
commit26446cccc9e8c4dfb7655ae686106a1ee200f2ec (patch)
tree6d0c63b4094564861d1d16c5ca5016e03554dfb6
parentad9c89fab8c532b4e5d926962c5d9d5ae5b01de4 (diff)
[ruby/reline] Implement bracketed paste insert
(https://github.com/ruby/reline/pull/655) https://github.com/ruby/reline/commit/e92dcbf514
-rw-r--r--lib/reline.rb19
-rw-r--r--lib/reline/ansi.rb53
-rw-r--r--lib/reline/config.rb1
-rw-r--r--lib/reline/line_editor.rb12
-rw-r--r--lib/reline/unicode.rb10
-rw-r--r--test/reline/yamatanooroti/test_rendering.rb9
6 files changed, 55 insertions, 49 deletions
diff --git a/lib/reline.rb b/lib/reline.rb
index d5bf2e363b..fb00b96531 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -312,6 +312,10 @@ module Reline
$stderr.sync = true
$stderr.puts "Reline is used by #{Process.pid}"
end
+ unless config.test_mode or config.loaded?
+ config.read
+ io_gate.set_default_key_bindings(config)
+ end
otio = io_gate.prep
may_req_ambiguous_char_width
@@ -338,11 +342,6 @@ module Reline
end
end
- unless config.test_mode or config.loaded?
- config.read
- io_gate.set_default_key_bindings(config)
- end
-
line_editor.print_nomultiline_prompt(prompt)
line_editor.update_dialogs
line_editor.rerender
@@ -352,7 +351,15 @@ module Reline
loop do
read_io(config.keyseq_timeout) { |inputs|
line_editor.set_pasting_state(io_gate.in_pasting?)
- inputs.each { |key| line_editor.update(key) }
+ inputs.each do |key|
+ if key.char == :bracketed_paste_start
+ text = io_gate.read_bracketed_paste
+ line_editor.insert_pasted_text(text)
+ line_editor.scroll_into_view
+ else
+ line_editor.update(key)
+ end
+ end
}
if line_editor.finished?
line_editor.render_finished
diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb
index 5e1f7249e3..45a475a787 100644
--- a/lib/reline/ansi.rb
+++ b/lib/reline/ansi.rb
@@ -45,6 +45,7 @@ class Reline::ANSI
end
def self.set_default_key_bindings(config, allow_terminfo: true)
+ set_bracketed_paste_key_bindings(config)
set_default_key_bindings_ansi_cursor(config)
if allow_terminfo && Reline::Terminfo.enabled?
set_default_key_bindings_terminfo(config)
@@ -66,6 +67,12 @@ class Reline::ANSI
end
end
+ def self.set_bracketed_paste_key_bindings(config)
+ [:emacs, :vi_insert, :vi_command].each do |keymap|
+ config.add_default_key_binding_by_keymap(keymap, START_BRACKETED_PASTE.bytes, :bracketed_paste_start)
+ end
+ end
+
def self.set_default_key_bindings_ansi_cursor(config)
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
bindings = [["\e[#{char}", default_func]] # CSI + char
@@ -178,46 +185,26 @@ 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(timeout_second)
+ START_BRACKETED_PASTE = String.new("\e[200~", encoding: Encoding::ASCII_8BIT)
+ END_BRACKETED_PASTE = String.new("\e[201~", encoding: Encoding::ASCII_8BIT)
+ def self.read_bracketed_paste
buffer = String.new(encoding: Encoding::ASCII_8BIT)
- buffer << inner_getc(timeout_second)
- 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(timeout_second)
- elsif END_BRACKETED_PASTE == buffer
- @@in_bracketed_paste_mode = false
- ungetc(-1)
- return inner_getc(timeout_second)
- end
- succ_c = inner_getc(Reline.core.config.keyseq_timeout)
-
- if succ_c
- buffer << succ_c
- else
- break
- end
+ until buffer.end_with?(END_BRACKETED_PASTE)
+ c = inner_getc(Float::INFINITY)
+ break unless c
+ buffer << c
end
- buffer.bytes.reverse_each do |ch|
- ungetc ch
- end
- inner_getc(timeout_second)
+ string = buffer.delete_suffix(END_BRACKETED_PASTE).force_encoding(encoding)
+ string.valid_encoding? ? string : ''
end
# if the usage expects to wait indefinitely, use Float::INFINITY for timeout_second
def self.getc(timeout_second)
- if Reline.core.config.enable_bracketed_paste
- getc_with_bracketed_paste(timeout_second)
- else
- inner_getc(timeout_second)
- end
+ inner_getc(timeout_second)
end
def self.in_pasting?
- @@in_bracketed_paste_mode or (not empty_buffer?)
+ not empty_buffer?
end
def self.empty_buffer?
@@ -361,11 +348,15 @@ class Reline::ANSI
end
def self.prep
+ # Enable bracketed paste
+ @@output.write "\e[?2004h" if Reline.core.config.enable_bracketed_paste
retrieve_keybuffer
nil
end
def self.deprep(otio)
+ # Disable bracketed paste
+ @@output.write "\e[?2004l" if Reline.core.config.enable_bracketed_paste
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
end
end
diff --git a/lib/reline/config.rb b/lib/reline/config.rb
index e06b4d16e5..d44c2675ab 100644
--- a/lib/reline/config.rb
+++ b/lib/reline/config.rb
@@ -51,6 +51,7 @@ class Reline::Config
@autocompletion = false
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
@loaded = false
+ @enable_bracketed_paste = true
end
def reset
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
index 57a3ab2f97..9e221f4c9c 100644
--- a/lib/reline/line_editor.rb
+++ b/lib/reline/line_editor.rb
@@ -283,7 +283,7 @@ class Reline::LineEditor
indent1 = @auto_indent_proc.(@buffer_of_lines.take(@line_index - 1).push(''), @line_index - 1, 0, true)
indent2 = @auto_indent_proc.(@buffer_of_lines.take(@line_index), @line_index - 1, @buffer_of_lines[@line_index - 1].bytesize, false)
indent = indent2 || indent1
- @buffer_of_lines[@line_index - 1] = ' ' * indent + @buffer_of_lines[@line_index - 1].gsub(/\A */, '')
+ @buffer_of_lines[@line_index - 1] = ' ' * indent + @buffer_of_lines[@line_index - 1].gsub(/\A\s*/, '')
)
process_auto_indent @line_index, add_newline: true
else
@@ -1305,6 +1305,16 @@ class Reline::LineEditor
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
end
+ def insert_pasted_text(text)
+ pre = @buffer_of_lines[@line_index].byteslice(0, @byte_pointer)
+ post = @buffer_of_lines[@line_index].byteslice(@byte_pointer..)
+ lines = (pre + text.gsub(/\r\n?/, "\n") + post).split("\n", -1)
+ lines << '' if lines.empty?
+ @buffer_of_lines[@line_index, 1] = lines
+ @line_index += lines.size - 1
+ @byte_pointer = @buffer_of_lines[@line_index].bytesize - post.bytesize
+ end
+
def insert_text(text)
if @buffer_of_lines[@line_index].bytesize == @byte_pointer
@buffer_of_lines[@line_index] += text
diff --git a/lib/reline/unicode.rb b/lib/reline/unicode.rb
index 82c9ec427c..d7460d6d4a 100644
--- a/lib/reline/unicode.rb
+++ b/lib/reline/unicode.rb
@@ -43,11 +43,13 @@ class Reline::Unicode
def self.escape_for_print(str)
str.chars.map! { |gr|
- escaped = EscapedPairs[gr.ord]
- if escaped && gr != -"\n" && gr != -"\t"
- escaped
- else
+ case gr
+ when -"\n"
gr
+ when -"\t"
+ -' '
+ else
+ EscapedPairs[gr.ord] || gr
end
}.join
end
diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb
index 74798c338f..9e8d7da78f 100644
--- a/test/reline/yamatanooroti/test_rendering.rb
+++ b/test/reline/yamatanooroti/test_rendering.rb
@@ -543,15 +543,10 @@ begin
EOC
end
- def test_enable_bracketed_paste
+ def test_bracketed_paste
omit if Reline.core.io_gate.win?
- write_inputrc <<~LINES
- set enable-bracketed-paste on
- LINES
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
- write("\e[200~,")
- write("def hoge\n 3\nend")
- write("\e[200~.")
+ write("\e[200~def hoge\r\t3\rend\e[201~")
close
assert_screen(<<~EOC)
Multiline REPL.