[ruby/reline] Implement bracketed paste insert
(https://github.com/ruby/reline/pull/655) https://github.com/ruby/reline/commit/e92dcbf514
This commit is contained in:
parent
ad9c89fab8
commit
26446cccc9
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user