mirror of
https://github.com/neovim/neovim.git
synced 2026-05-24 05:40:08 +00:00
fix(lsp): fallback to filterText for non-matching PlainText items #39695
Problem: PlainText completion items used `textEdit.newText` or `insertText` as the completion word even when they did not match the typed prefix. This could break popup completion behavior like 'completeopt+=longest'. Solution: Fall back to `filterText` when `newText` or `insertText` does not match the typed prefix.
This commit is contained in:
@@ -211,8 +211,15 @@ local function get_completion_word(item, prefix, match)
|
||||
elseif item.textEdit then
|
||||
local word = item.textEdit.newText
|
||||
word = string.gsub(word, '\r\n?', '\n')
|
||||
return word:match('([^\n]*)') or word
|
||||
word = word:match('([^\n]*)') or word
|
||||
if item.filterText and not match(word, prefix) then
|
||||
return item.filterText
|
||||
end
|
||||
return word
|
||||
elseif item.insertText and item.insertText ~= '' then
|
||||
if item.filterText and not match(item.insertText, prefix) then
|
||||
return item.filterText
|
||||
end
|
||||
return item.insertText
|
||||
end
|
||||
return item.label
|
||||
|
||||
@@ -222,6 +222,59 @@ describe('vim.lsp.completion: item conversion', function()
|
||||
eq(expected, got)
|
||||
end
|
||||
|
||||
it('uses filterText as word if label/newText would not match', function()
|
||||
local items = {
|
||||
{
|
||||
filterText = '<module',
|
||||
insertTextFormat = 2,
|
||||
kind = 10,
|
||||
label = 'module',
|
||||
sortText = 'module',
|
||||
textEdit = {
|
||||
newText = '<module>$1</module>$0',
|
||||
range = {
|
||||
start = { character = 0, line = 0 },
|
||||
['end'] = { character = 0, line = 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
filterText = 'atto',
|
||||
insertTextFormat = 1,
|
||||
kind = 7,
|
||||
label = '•std::atto',
|
||||
sortText = 'atto',
|
||||
textEdit = {
|
||||
newText = 'std::atto',
|
||||
range = {
|
||||
start = { character = 0, line = 0 },
|
||||
['end'] = { character = 0, line = 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
filterText = 'adopt_lock_t',
|
||||
insertTextFormat = 1,
|
||||
kind = 7,
|
||||
label = '•std::adopt_lock_t',
|
||||
sortText = 'adopt_lock_t',
|
||||
insertText = 'std::adopt_lock_t',
|
||||
},
|
||||
}
|
||||
assert_completion_matches('<mo', items, {
|
||||
{ abbr = 'module', word = '<module' },
|
||||
})
|
||||
assert_completion_matches('a', items, {
|
||||
{ abbr = '•std::atto', word = 'atto' },
|
||||
{ abbr = '•std::adopt_lock_t', word = 'adopt_lock_t' },
|
||||
})
|
||||
assert_completion_matches('', items, {
|
||||
{ abbr = 'module', word = 'module' },
|
||||
{ abbr = '•std::atto', word = 'std::atto' },
|
||||
{ abbr = '•std::adopt_lock_t', word = 'std::adopt_lock_t' },
|
||||
})
|
||||
end)
|
||||
|
||||
describe('when completeopt has fuzzy matching enabled', function()
|
||||
before_each(function()
|
||||
exec_lua(function()
|
||||
@@ -245,31 +298,6 @@ describe('vim.lsp.completion: item conversion', function()
|
||||
})
|
||||
end)
|
||||
|
||||
it('uses filterText as word if label/newText would not match', function()
|
||||
local items = {
|
||||
{
|
||||
filterText = '<module',
|
||||
insertTextFormat = 2,
|
||||
kind = 10,
|
||||
label = 'module',
|
||||
sortText = 'module',
|
||||
textEdit = {
|
||||
newText = '<module>$1</module>$0',
|
||||
range = {
|
||||
start = { character = 0, line = 0 },
|
||||
['end'] = { character = 0, line = 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert_completion_matches('<mo', items, {
|
||||
{ abbr = 'module', word = '<module' },
|
||||
})
|
||||
assert_completion_matches('', items, {
|
||||
{ abbr = 'module', word = 'module' },
|
||||
})
|
||||
end)
|
||||
|
||||
it('fuzzy matches on label when filterText is missing', function()
|
||||
assert_completion_matches('fo', {
|
||||
{ label = 'foo' },
|
||||
|
||||
Reference in New Issue
Block a user