Skip to content

Commit cbf2132

Browse files
authored
Fix tab completion appending quote (#782)
1 parent caf1187 commit cbf2132

File tree

5 files changed

+66
-66
lines changed

5 files changed

+66
-66
lines changed

lib/reline/line_editor.rb

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,9 @@ def context
578578
@context
579579
end
580580

581-
def retrieve_completion_block(set_completion_quote_character = false)
582-
@line_editor.retrieve_completion_block(set_completion_quote_character)
581+
def retrieve_completion_block(_unused = false)
582+
preposing, target, postposing, _quote = @line_editor.retrieve_completion_block
583+
[preposing, target, postposing]
583584
end
584585

585586
def call_completion_proc_with_checking_args(pre, target, post)
@@ -826,8 +827,7 @@ def editing_mode
826827
end.uniq
827828
end
828829

829-
private def perform_completion(list)
830-
preposing, target, postposing = retrieve_completion_block
830+
private def perform_completion(preposing, target, postposing, quote, list)
831831
candidates = filter_normalize_candidates(target, list)
832832

833833
case @completion_state
@@ -851,7 +851,7 @@ def editing_mode
851851
append_character = ''
852852
if candidates.include?(completed)
853853
if candidates.one?
854-
append_character = completion_append_character.to_s
854+
append_character = quote || completion_append_character.to_s
855855
@completion_state = CompletionState::PERFECT_MATCH
856856
elsif @config.show_all_if_ambiguous
857857
menu(candidates)
@@ -895,8 +895,8 @@ def dialog_proc_scope_completion_journey_data
895895
end
896896

897897
private def retrieve_completion_journey_state
898-
preposing, target, postposing = retrieve_completion_block
899-
list = call_completion_proc
898+
preposing, target, postposing, quote = retrieve_completion_block
899+
list = call_completion_proc(preposing, target, postposing, quote)
900900
return unless list.is_a?(Array)
901901

902902
candidates = list.select{ |item| item.start_with?(target) }
@@ -1146,9 +1146,8 @@ def scroll_into_view
11461146
end
11471147
end
11481148

1149-
def call_completion_proc
1150-
result = retrieve_completion_block(true)
1151-
pre, target, post = result
1149+
def call_completion_proc(pre, target, post, quote)
1150+
Reline.core.instance_variable_set(:@completion_quote_character, quote)
11521151
result = call_completion_proc_with_checking_args(pre, target, post)
11531152
Reline.core.instance_variable_set(:@completion_quote_character, nil)
11541153
result
@@ -1224,11 +1223,12 @@ def set_current_lines(lines, byte_pointer = nil, line_index = 0)
12241223
process_auto_indent
12251224
end
12261225

1227-
def retrieve_completion_block(set_completion_quote_character = false)
1226+
def retrieve_completion_block
12281227
quote_characters = Reline.completer_quote_characters
12291228
before = current_line.byteslice(0, @byte_pointer).grapheme_clusters
12301229
quote = nil
1231-
unless quote_characters.empty?
1230+
# Calcualte closing quote when cursor is at the end of the line
1231+
if current_line.bytesize == @byte_pointer && !quote_characters.empty?
12321232
escaped = false
12331233
before.each do |c|
12341234
if escaped
@@ -1243,26 +1243,20 @@ def retrieve_completion_block(set_completion_quote_character = false)
12431243
end
12441244
end
12451245
end
1246+
12461247
word_break_characters = quote_characters + Reline.completer_word_break_characters
12471248
break_index = before.rindex { |c| word_break_characters.include?(c) || quote_characters.include?(c) } || -1
12481249
preposing = before.take(break_index + 1).join
12491250
target = before.drop(break_index + 1).join
12501251
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
1251-
if target
1252-
if set_completion_quote_character and quote
1253-
Reline.core.instance_variable_set(:@completion_quote_character, quote)
1254-
insert_text(quote) # FIXME: should not be here
1255-
target += quote
1256-
end
1257-
end
12581252
lines = whole_lines
12591253
if @line_index > 0
12601254
preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
12611255
end
12621256
if (lines.size - 1) > @line_index
12631257
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
12641258
end
1265-
[preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding)]
1259+
[preposing.encode(encoding), target.encode(encoding), postposing.encode(encoding), quote&.encode(encoding)]
12661260
end
12671261

12681262
def confirm_multiline_termination
@@ -1393,10 +1387,11 @@ def finish
13931387
@completion_occurs = move_completed_list(:down)
13941388
else
13951389
@completion_journey_state = nil
1396-
result = call_completion_proc
1390+
pre, target, post, quote = retrieve_completion_block
1391+
result = call_completion_proc(pre, target, post, quote)
13971392
if result.is_a?(Array)
13981393
@completion_occurs = true
1399-
perform_completion(result)
1394+
perform_completion(pre, target, post, quote, result)
14001395
end
14011396
end
14021397
end
@@ -1860,9 +1855,9 @@ def finish
18601855
if current_line.empty? or @byte_pointer < current_line.bytesize
18611856
em_delete(key)
18621857
elsif !@config.autocompletion # show completed list
1863-
result = call_completion_proc
1858+
pre, target, post, quote = retrieve_completion_block
1859+
result = call_completion_proc(pre, target, post, quote)
18641860
if result.is_a?(Array)
1865-
_preposing, target = retrieve_completion_block
18661861
candidates = filter_normalize_candidates(target, result)
18671862
menu(candidates)
18681863
end

test/reline/helper.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ def input_raw_keys(input, convert = true)
135135
end
136136
end
137137

138+
def set_line_around_cursor(before, after)
139+
input_keys("\C-a\C-k")
140+
input_keys(after)
141+
input_keys("\C-a")
142+
input_keys(before)
143+
end
144+
138145
def assert_line_around_cursor(before, after)
139146
before = convert_str(before)
140147
after = convert_str(after)

test/reline/test_key_actor_emacs.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,30 @@ def test_completion_append_character
933933
assert_line_around_cursor('foo_fooX foo_barX', '')
934934
end
935935

936+
def test_completion_with_quote_append
937+
@line_editor.completion_proc = proc { |word|
938+
%w[foo bar baz].select { |s| s.start_with? word }
939+
}
940+
set_line_around_cursor('x = "b', '')
941+
input_keys("\C-i", false)
942+
assert_line_around_cursor('x = "ba', '')
943+
set_line_around_cursor('x = "f', ' ')
944+
input_keys("\C-i", false)
945+
assert_line_around_cursor('x = "foo', ' ')
946+
set_line_around_cursor("x = 'f", '')
947+
input_keys("\C-i", false)
948+
assert_line_around_cursor("x = 'foo'", '')
949+
set_line_around_cursor('"a "f', '')
950+
input_keys("\C-i", false)
951+
assert_line_around_cursor('"a "foo', '')
952+
set_line_around_cursor('"a\\" "f', '')
953+
input_keys("\C-i", false)
954+
assert_line_around_cursor('"a\\" "foo', '')
955+
set_line_around_cursor('"a" "f', '')
956+
input_keys("\C-i", false)
957+
assert_line_around_cursor('"a" "foo"', '')
958+
end
959+
936960
def test_completion_with_completion_ignore_case
937961
@line_editor.completion_proc = proc { |word|
938962
%w{

test/reline/test_line_editor.rb

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,12 @@ def retrieve_completion_block(lines, line_index, byte_pointer)
1515
@line_editor.instance_variable_set(:@buffer_of_lines, lines)
1616
@line_editor.instance_variable_set(:@line_index, line_index)
1717
@line_editor.instance_variable_set(:@byte_pointer, byte_pointer)
18-
@line_editor.retrieve_completion_block(false)
18+
@line_editor.retrieve_completion_block
1919
end
2020

2121
def retrieve_completion_quote(line)
22-
retrieve_completion_block([line], 0, line.bytesize)
23-
_, target = @line_editor.retrieve_completion_block(false)
24-
_, target2 = @line_editor.retrieve_completion_block(true)
25-
# This is a hack to get the quoted character.
26-
# retrieve_completion_block should be refactored to return the quoted character.
27-
target2.chars.last if target2 != target
22+
_, _, _, quote = retrieve_completion_block([line], 0, line.bytesize)
23+
quote
2824
end
2925

3026
def teardown
@@ -35,20 +31,20 @@ def teardown
3531
def test_retrieve_completion_block
3632
Reline.completer_word_break_characters = ' ([{'
3733
Reline.completer_quote_characters = ''
38-
assert_equal(['', '', 'foo'], retrieve_completion_block(['foo'], 0, 0))
39-
assert_equal(['', 'f', 'oo'], retrieve_completion_block(['foo'], 0, 1))
40-
assert_equal(['foo ', 'ba', 'r baz'], retrieve_completion_block(['foo bar baz'], 0, 6))
41-
assert_equal(['foo([', 'b', 'ar])baz'], retrieve_completion_block(['foo([bar])baz'], 0, 6))
42-
assert_equal(['foo([{', '', '}])baz'], retrieve_completion_block(['foo([{}])baz'], 0, 6))
43-
assert_equal(["abc\nfoo ", 'ba', "r baz\ndef"], retrieve_completion_block(['abc', 'foo bar baz', 'def'], 1, 6))
34+
assert_equal(['', '', 'foo', nil], retrieve_completion_block(['foo'], 0, 0))
35+
assert_equal(['', 'f', 'oo', nil], retrieve_completion_block(['foo'], 0, 1))
36+
assert_equal(['foo ', 'ba', 'r baz', nil], retrieve_completion_block(['foo bar baz'], 0, 6))
37+
assert_equal(['foo([', 'b', 'ar])baz', nil], retrieve_completion_block(['foo([bar])baz'], 0, 6))
38+
assert_equal(['foo([{', '', '}])baz', nil], retrieve_completion_block(['foo([{}])baz'], 0, 6))
39+
assert_equal(["abc\nfoo ", 'ba', "r baz\ndef", nil], retrieve_completion_block(['abc', 'foo bar baz', 'def'], 1, 6))
4440
end
4541

4642
def test_retrieve_completion_block_with_quote_characters
4743
Reline.completer_word_break_characters = ' ([{'
4844
Reline.completer_quote_characters = ''
49-
assert_equal(['"" ', '"wo', 'rd'], retrieve_completion_block(['"" "word'], 0, 6))
45+
assert_equal(['"" ', '"wo', 'rd', nil], retrieve_completion_block(['"" "word'], 0, 6))
5046
Reline.completer_quote_characters = '"'
51-
assert_equal(['"" "', 'wo', 'rd'], retrieve_completion_block(['"" "word'], 0, 6))
47+
assert_equal(['"" "', 'wo', 'rd', nil], retrieve_completion_block(['"" "word'], 0, 6))
5248
end
5349

5450
def test_retrieve_completion_quote

test/reline/test_string_processing.rb

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,42 +27,20 @@ def test_calculate_width_with_escape_sequence
2727
def test_completion_proc_with_preposing_and_postposing
2828
buf = ['def hoge', ' puts :aaa', 'end']
2929

30-
@line_editor.instance_variable_set(:@is_multiline, true)
31-
@line_editor.instance_variable_set(:@buffer_of_lines, buf)
32-
@line_editor.instance_variable_set(:@byte_pointer, 3)
33-
@line_editor.instance_variable_set(:@line_index, 1)
34-
@line_editor.instance_variable_set(:@completion_proc, proc { |target|
35-
assert_equal('p', target)
36-
})
37-
@line_editor.__send__(:call_completion_proc)
38-
3930
@line_editor.instance_variable_set(:@is_multiline, true)
4031
@line_editor.instance_variable_set(:@buffer_of_lines, buf)
4132
@line_editor.instance_variable_set(:@byte_pointer, 6)
4233
@line_editor.instance_variable_set(:@line_index, 1)
34+
completion_proc_called = false
4335
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
4436
assert_equal('puts', target)
4537
assert_equal("def hoge\n ", pre)
4638
assert_equal(" :aaa\nend", post)
39+
completion_proc_called = true
4740
})
48-
@line_editor.__send__(:call_completion_proc)
4941

50-
@line_editor.instance_variable_set(:@byte_pointer, 6)
51-
@line_editor.instance_variable_set(:@line_index, 0)
52-
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
53-
assert_equal('ho', target)
54-
assert_equal('def ', pre)
55-
assert_equal("ge\n puts :aaa\nend", post)
56-
})
57-
@line_editor.__send__(:call_completion_proc)
58-
59-
@line_editor.instance_variable_set(:@byte_pointer, 1)
60-
@line_editor.instance_variable_set(:@line_index, 2)
61-
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
62-
assert_equal('e', target)
63-
assert_equal("def hoge\n puts :aaa\n", pre)
64-
assert_equal('nd', post)
65-
})
66-
@line_editor.__send__(:call_completion_proc)
42+
assert_equal(["def hoge\n ", 'puts', " :aaa\nend", nil], @line_editor.retrieve_completion_block)
43+
@line_editor.__send__(:call_completion_proc, "def hoge\n ", 'puts', " :aaa\nend", nil)
44+
assert(completion_proc_called)
6745
end
6846
end

0 commit comments

Comments
 (0)