[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
|
||||
|
||||
def retrieve_completion_block(set_completion_quote_character = false)
|
||||
if Reline.completer_word_break_characters.empty?
|
||||
word_break_regexp = nil
|
||||
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_characters = Reline.completer_quote_characters
|
||||
before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
|
||||
quote = nil
|
||||
closing_quote = nil
|
||||
escaped_quote = nil
|
||||
i = 0
|
||||
while i < @byte_pointer do
|
||||
slice = current_line.byteslice(i, @byte_pointer - i)
|
||||
unless slice.valid_encoding?
|
||||
i += 1
|
||||
next
|
||||
end
|
||||
if quote and slice.start_with?(closing_quote)
|
||||
quote = nil
|
||||
i += 1
|
||||
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)
|
||||
unless quote_characters.empty?
|
||||
escaped = false
|
||||
before.each do |c|
|
||||
if escaped
|
||||
escaped = false
|
||||
next
|
||||
elsif c == '\\'
|
||||
escaped = true
|
||||
elsif quote
|
||||
quote = nil if c == quote
|
||||
elsif quote_characters.include?(c)
|
||||
quote = c
|
||||
end
|
||||
end
|
||||
else
|
||||
preposing = ''
|
||||
if break_pointer
|
||||
preposing = current_line.byteslice(0, break_pointer)
|
||||
else
|
||||
preposing = ''
|
||||
end
|
||||
word_break_characters = quote_characters + Reline.completer_word_break_characters
|
||||
break_index = before.rindex { |c| word_break_characters.include?(c) || quote_characters.include?(c) } || -1
|
||||
preposing = before.take(break_index + 1).join
|
||||
target = before.drop(break_index + 1).join
|
||||
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
|
||||
target = before
|
||||
end
|
||||
lines = whole_lines
|
||||
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)
|
||||
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
|
||||
@line_editor.completion_proc = proc { |word|
|
||||
%w{
|
||||
|
@ -3,6 +3,66 @@ require 'reline/line_editor'
|
||||
require 'stringio'
|
||||
|
||||
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 TestIO < Reline::IO
|
||||
def move_cursor_column(col)
|
||||
|
Loading…
x
Reference in New Issue
Block a user