[ruby/reline] Fix completion quote, preposing and target calculation
bug (https://github.com/ruby/reline/pull/763) https://github.com/ruby/reline/commit/d3ba7216eb
This commit is contained in:
parent
c6ca339955
commit
bf47b1b523
@ -1225,70 +1225,35 @@ class Reline::LineEditor
|
|||||||
end
|
end
|
||||||
|
|
||||||
def retrieve_completion_block(set_completion_quote_character = false)
|
def retrieve_completion_block(set_completion_quote_character = false)
|
||||||
if Reline.completer_word_break_characters.empty?
|
quote_characters = Reline.completer_quote_characters
|
||||||
word_break_regexp = nil
|
before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
|
||||||
else
|
|
||||||
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
|
|
||||||
end
|
|
||||||
if Reline.completer_quote_characters.empty?
|
|
||||||
quote_characters_regexp = nil
|
|
||||||
else
|
|
||||||
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
|
||||||
end
|
|
||||||
before = current_line.byteslice(0, @byte_pointer)
|
|
||||||
rest = nil
|
|
||||||
break_pointer = nil
|
|
||||||
quote = nil
|
quote = nil
|
||||||
closing_quote = nil
|
unless quote_characters.empty?
|
||||||
escaped_quote = nil
|
escaped = false
|
||||||
i = 0
|
before.each do |c|
|
||||||
while i < @byte_pointer do
|
if escaped
|
||||||
slice = current_line.byteslice(i, @byte_pointer - i)
|
escaped = false
|
||||||
unless slice.valid_encoding?
|
next
|
||||||
i += 1
|
elsif c == '\\'
|
||||||
next
|
escaped = true
|
||||||
end
|
elsif quote
|
||||||
if quote and slice.start_with?(closing_quote)
|
quote = nil if c == quote
|
||||||
quote = nil
|
elsif quote_characters.include?(c)
|
||||||
i += 1
|
quote = c
|
||||||
rest = nil
|
|
||||||
elsif quote and slice.start_with?(escaped_quote)
|
|
||||||
# skip
|
|
||||||
i += 2
|
|
||||||
elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
|
|
||||||
rest = $'
|
|
||||||
quote = $&
|
|
||||||
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
|
||||||
escaped_quote = /\\#{Regexp.escape(quote)}/
|
|
||||||
i += 1
|
|
||||||
break_pointer = i - 1
|
|
||||||
elsif word_break_regexp and not quote and slice =~ word_break_regexp
|
|
||||||
rest = $'
|
|
||||||
i += 1
|
|
||||||
before = current_line.byteslice(i, @byte_pointer - i)
|
|
||||||
break_pointer = i
|
|
||||||
else
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
|
||||||
if rest
|
|
||||||
preposing = current_line.byteslice(0, break_pointer)
|
|
||||||
target = rest
|
|
||||||
if set_completion_quote_character and quote
|
|
||||||
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
|
||||||
if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
|
|
||||||
insert_text(quote)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
end
|
||||||
preposing = ''
|
word_break_characters = quote_characters + Reline.completer_word_break_characters
|
||||||
if break_pointer
|
break_index = before.rindex { |c| word_break_characters.include?(c) || quote_characters.include?(c) } || -1
|
||||||
preposing = current_line.byteslice(0, break_pointer)
|
preposing = before.take(break_index + 1).join
|
||||||
else
|
target = before.drop(break_index + 1).join
|
||||||
preposing = ''
|
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
||||||
|
if target
|
||||||
|
if set_completion_quote_character and quote
|
||||||
|
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
||||||
|
insert_text(quote) # FIXME: should not be here
|
||||||
|
target += quote
|
||||||
end
|
end
|
||||||
target = before
|
|
||||||
end
|
end
|
||||||
lines = whole_lines
|
lines = whole_lines
|
||||||
if @line_index > 0
|
if @line_index > 0
|
||||||
|
@ -853,28 +853,6 @@ class Reline::KeyActor::EmacsTest < Reline::TestCase
|
|||||||
assert_equal(%w{foo_foo foo_bar foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
|
assert_equal(%w{foo_foo foo_bar foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_completion_with_indent_and_completer_quote_characters
|
|
||||||
@line_editor.completion_proc = proc { |word|
|
|
||||||
%w{
|
|
||||||
"".foo_foo
|
|
||||||
"".foo_bar
|
|
||||||
"".foo_baz
|
|
||||||
"".qux
|
|
||||||
}.map { |i|
|
|
||||||
i.encode(@encoding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input_keys(' "".fo')
|
|
||||||
assert_line_around_cursor(' "".fo', '')
|
|
||||||
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
|
|
||||||
input_keys("\C-i", false)
|
|
||||||
assert_line_around_cursor(' "".foo_', '')
|
|
||||||
assert_equal(nil, @line_editor.instance_variable_get(:@menu_info))
|
|
||||||
input_keys("\C-i", false)
|
|
||||||
assert_line_around_cursor(' "".foo_', '')
|
|
||||||
assert_equal(%w{"".foo_foo "".foo_bar "".foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_completion_with_perfect_match
|
def test_completion_with_perfect_match
|
||||||
@line_editor.completion_proc = proc { |word|
|
@line_editor.completion_proc = proc { |word|
|
||||||
%w{
|
%w{
|
||||||
|
@ -3,6 +3,66 @@ require 'reline/line_editor'
|
|||||||
require 'stringio'
|
require 'stringio'
|
||||||
|
|
||||||
class Reline::LineEditor
|
class Reline::LineEditor
|
||||||
|
|
||||||
|
class CompletionBlockTest < Reline::TestCase
|
||||||
|
def setup
|
||||||
|
@original_quote_characters = Reline.completer_quote_characters
|
||||||
|
@original_word_break_characters = Reline.completer_word_break_characters
|
||||||
|
@line_editor = Reline::LineEditor.new(nil, Encoding::UTF_8)
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieve_completion_block(lines, line_index, byte_pointer)
|
||||||
|
@line_editor.instance_variable_set(:@buffer_of_lines, lines)
|
||||||
|
@line_editor.instance_variable_set(:@line_index, line_index)
|
||||||
|
@line_editor.instance_variable_set(:@byte_pointer, byte_pointer)
|
||||||
|
@line_editor.retrieve_completion_block(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieve_completion_quote(line)
|
||||||
|
retrieve_completion_block([line], 0, line.bytesize)
|
||||||
|
_, target = @line_editor.retrieve_completion_block(false)
|
||||||
|
_, target2 = @line_editor.retrieve_completion_block(true)
|
||||||
|
# This is a hack to get the quoted character.
|
||||||
|
# retrieve_completion_block should be refactored to return the quoted character.
|
||||||
|
target2.chars.last if target2 != target
|
||||||
|
end
|
||||||
|
|
||||||
|
def teardown
|
||||||
|
Reline.completer_quote_characters = @original_quote_characters
|
||||||
|
Reline.completer_word_break_characters = @original_word_break_characters
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_retrieve_completion_block
|
||||||
|
Reline.completer_word_break_characters = ' ([{'
|
||||||
|
Reline.completer_quote_characters = ''
|
||||||
|
assert_equal(['', '', 'foo'], retrieve_completion_block(['foo'], 0, 0))
|
||||||
|
assert_equal(['', 'f', 'oo'], retrieve_completion_block(['foo'], 0, 1))
|
||||||
|
assert_equal(['foo ', 'ba', 'r baz'], retrieve_completion_block(['foo bar baz'], 0, 6))
|
||||||
|
assert_equal(['foo([', 'b', 'ar])baz'], retrieve_completion_block(['foo([bar])baz'], 0, 6))
|
||||||
|
assert_equal(['foo([{', '', '}])baz'], retrieve_completion_block(['foo([{}])baz'], 0, 6))
|
||||||
|
assert_equal(["abc\nfoo ", 'ba', "r baz\ndef"], retrieve_completion_block(['abc', 'foo bar baz', 'def'], 1, 6))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_retrieve_completion_block_with_quote_characters
|
||||||
|
Reline.completer_word_break_characters = ' ([{'
|
||||||
|
Reline.completer_quote_characters = ''
|
||||||
|
assert_equal(['"" ', '"wo', 'rd'], retrieve_completion_block(['"" "word'], 0, 6))
|
||||||
|
Reline.completer_quote_characters = '"'
|
||||||
|
assert_equal(['"" "', 'wo', 'rd'], retrieve_completion_block(['"" "word'], 0, 6))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_retrieve_completion_quote
|
||||||
|
Reline.completer_quote_characters = '"\''
|
||||||
|
assert_equal('"', retrieve_completion_quote('"\''))
|
||||||
|
assert_equal(nil, retrieve_completion_quote('""'))
|
||||||
|
assert_equal("'", retrieve_completion_quote('""\'"'))
|
||||||
|
assert_equal(nil, retrieve_completion_quote('""\'\''))
|
||||||
|
assert_equal('"', retrieve_completion_quote('"\\"'))
|
||||||
|
assert_equal(nil, retrieve_completion_quote('"\\""'))
|
||||||
|
assert_equal(nil, retrieve_completion_quote('"\\\\"'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class RenderLineDifferentialTest < Reline::TestCase
|
class RenderLineDifferentialTest < Reline::TestCase
|
||||||
class TestIO < Reline::IO
|
class TestIO < Reline::IO
|
||||||
def move_cursor_column(col)
|
def move_cursor_column(col)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user