mirror of
https://github.com/ruby/ruby.git
synced 2025-08-26 22:45:03 +02:00
[ruby/reline] Implement bracketed paste insert
(https://github.com/ruby/reline/pull/655)
e92dcbf514
This commit is contained in:
parent
ad9c89fab8
commit
26446cccc9
6 changed files with 55 additions and 49 deletions
|
@ -312,6 +312,10 @@ module Reline
|
||||||
$stderr.sync = true
|
$stderr.sync = true
|
||||||
$stderr.puts "Reline is used by #{Process.pid}"
|
$stderr.puts "Reline is used by #{Process.pid}"
|
||||||
end
|
end
|
||||||
|
unless config.test_mode or config.loaded?
|
||||||
|
config.read
|
||||||
|
io_gate.set_default_key_bindings(config)
|
||||||
|
end
|
||||||
otio = io_gate.prep
|
otio = io_gate.prep
|
||||||
|
|
||||||
may_req_ambiguous_char_width
|
may_req_ambiguous_char_width
|
||||||
|
@ -338,11 +342,6 @@ module Reline
|
||||||
end
|
end
|
||||||
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.print_nomultiline_prompt(prompt)
|
||||||
line_editor.update_dialogs
|
line_editor.update_dialogs
|
||||||
line_editor.rerender
|
line_editor.rerender
|
||||||
|
@ -352,7 +351,15 @@ module Reline
|
||||||
loop do
|
loop do
|
||||||
read_io(config.keyseq_timeout) { |inputs|
|
read_io(config.keyseq_timeout) { |inputs|
|
||||||
line_editor.set_pasting_state(io_gate.in_pasting?)
|
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?
|
if line_editor.finished?
|
||||||
line_editor.render_finished
|
line_editor.render_finished
|
||||||
|
|
|
@ -45,6 +45,7 @@ class Reline::ANSI
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.set_default_key_bindings(config, allow_terminfo: true)
|
def self.set_default_key_bindings(config, allow_terminfo: true)
|
||||||
|
set_bracketed_paste_key_bindings(config)
|
||||||
set_default_key_bindings_ansi_cursor(config)
|
set_default_key_bindings_ansi_cursor(config)
|
||||||
if allow_terminfo && Reline::Terminfo.enabled?
|
if allow_terminfo && Reline::Terminfo.enabled?
|
||||||
set_default_key_bindings_terminfo(config)
|
set_default_key_bindings_terminfo(config)
|
||||||
|
@ -66,6 +67,12 @@ class Reline::ANSI
|
||||||
end
|
end
|
||||||
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)
|
def self.set_default_key_bindings_ansi_cursor(config)
|
||||||
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
|
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
|
||||||
bindings = [["\e[#{char}", default_func]] # CSI + char
|
bindings = [["\e[#{char}", default_func]] # CSI + char
|
||||||
|
@ -178,46 +185,26 @@ class Reline::ANSI
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@in_bracketed_paste_mode = false
|
START_BRACKETED_PASTE = String.new("\e[200~", encoding: Encoding::ASCII_8BIT)
|
||||||
START_BRACKETED_PASTE = String.new("\e[200~,", encoding: Encoding::ASCII_8BIT)
|
END_BRACKETED_PASTE = String.new("\e[201~", encoding: Encoding::ASCII_8BIT)
|
||||||
END_BRACKETED_PASTE = String.new("\e[200~.", encoding: Encoding::ASCII_8BIT)
|
def self.read_bracketed_paste
|
||||||
def self.getc_with_bracketed_paste(timeout_second)
|
|
||||||
buffer = String.new(encoding: Encoding::ASCII_8BIT)
|
buffer = String.new(encoding: Encoding::ASCII_8BIT)
|
||||||
buffer << inner_getc(timeout_second)
|
until buffer.end_with?(END_BRACKETED_PASTE)
|
||||||
while START_BRACKETED_PASTE.start_with?(buffer) or END_BRACKETED_PASTE.start_with?(buffer) do
|
c = inner_getc(Float::INFINITY)
|
||||||
if START_BRACKETED_PASTE == buffer
|
break unless c
|
||||||
@@in_bracketed_paste_mode = true
|
buffer << c
|
||||||
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
|
|
||||||
end
|
end
|
||||||
buffer.bytes.reverse_each do |ch|
|
string = buffer.delete_suffix(END_BRACKETED_PASTE).force_encoding(encoding)
|
||||||
ungetc ch
|
string.valid_encoding? ? string : ''
|
||||||
end
|
|
||||||
inner_getc(timeout_second)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# if the usage expects to wait indefinitely, use Float::INFINITY for timeout_second
|
# if the usage expects to wait indefinitely, use Float::INFINITY for timeout_second
|
||||||
def self.getc(timeout_second)
|
def self.getc(timeout_second)
|
||||||
if Reline.core.config.enable_bracketed_paste
|
inner_getc(timeout_second)
|
||||||
getc_with_bracketed_paste(timeout_second)
|
|
||||||
else
|
|
||||||
inner_getc(timeout_second)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.in_pasting?
|
def self.in_pasting?
|
||||||
@@in_bracketed_paste_mode or (not empty_buffer?)
|
not empty_buffer?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.empty_buffer?
|
def self.empty_buffer?
|
||||||
|
@ -361,11 +348,15 @@ class Reline::ANSI
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.prep
|
def self.prep
|
||||||
|
# Enable bracketed paste
|
||||||
|
@@output.write "\e[?2004h" if Reline.core.config.enable_bracketed_paste
|
||||||
retrieve_keybuffer
|
retrieve_keybuffer
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.deprep(otio)
|
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
|
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,6 +51,7 @@ class Reline::Config
|
||||||
@autocompletion = false
|
@autocompletion = false
|
||||||
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
|
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
|
||||||
@loaded = false
|
@loaded = false
|
||||||
|
@enable_bracketed_paste = true
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset
|
def reset
|
||||||
|
|
|
@ -283,7 +283,7 @@ class Reline::LineEditor
|
||||||
indent1 = @auto_indent_proc.(@buffer_of_lines.take(@line_index - 1).push(''), @line_index - 1, 0, true)
|
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)
|
indent2 = @auto_indent_proc.(@buffer_of_lines.take(@line_index), @line_index - 1, @buffer_of_lines[@line_index - 1].bytesize, false)
|
||||||
indent = indent2 || indent1
|
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
|
process_auto_indent @line_index, add_newline: true
|
||||||
else
|
else
|
||||||
|
@ -1305,6 +1305,16 @@ class Reline::LineEditor
|
||||||
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
||||||
end
|
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)
|
def insert_text(text)
|
||||||
if @buffer_of_lines[@line_index].bytesize == @byte_pointer
|
if @buffer_of_lines[@line_index].bytesize == @byte_pointer
|
||||||
@buffer_of_lines[@line_index] += text
|
@buffer_of_lines[@line_index] += text
|
||||||
|
|
|
@ -43,11 +43,13 @@ class Reline::Unicode
|
||||||
|
|
||||||
def self.escape_for_print(str)
|
def self.escape_for_print(str)
|
||||||
str.chars.map! { |gr|
|
str.chars.map! { |gr|
|
||||||
escaped = EscapedPairs[gr.ord]
|
case gr
|
||||||
if escaped && gr != -"\n" && gr != -"\t"
|
when -"\n"
|
||||||
escaped
|
|
||||||
else
|
|
||||||
gr
|
gr
|
||||||
|
when -"\t"
|
||||||
|
-' '
|
||||||
|
else
|
||||||
|
EscapedPairs[gr.ord] || gr
|
||||||
end
|
end
|
||||||
}.join
|
}.join
|
||||||
end
|
end
|
||||||
|
|
|
@ -543,15 +543,10 @@ begin
|
||||||
EOC
|
EOC
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_enable_bracketed_paste
|
def test_bracketed_paste
|
||||||
omit if Reline.core.io_gate.win?
|
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.')
|
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
|
||||||
write("\e[200~,")
|
write("\e[200~def hoge\r\t3\rend\e[201~")
|
||||||
write("def hoge\n 3\nend")
|
|
||||||
write("\e[200~.")
|
|
||||||
close
|
close
|
||||||
assert_screen(<<~EOC)
|
assert_screen(<<~EOC)
|
||||||
Multiline REPL.
|
Multiline REPL.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue