---@diagnostic disable: no-unknown
local t = require('test.testutil')
local t_lsp = require('test.functional.plugin.lsp.testutil')
local n = require('test.functional.testnvim')()
local clear = n.clear
local eq = t.eq
local neq = t.neq
local exec_lua = n.exec_lua
local feed = n.feed
local retry = t.retry
local create_server_definition = t_lsp.create_server_definition
--- Convert completion results.
---
---@param line string line contents. Mark cursor position with `|`
---@param candidates lsp.CompletionList|lsp.CompletionItem[]
---@param lnum? integer 0-based, defaults to 0
---@return {items: table[], server_start_boundary: integer?}
local function complete(line, candidates, lnum, server_boundary)
lnum = lnum or 0
-- nvim_win_get_cursor returns 0 based column, line:find returns 1 based
local cursor_col = line:find('|') - 1
line = line:gsub('|', '')
return exec_lua(function(result)
local line_to_cursor = line:sub(1, cursor_col)
local client_start_boundary = vim.fn.match(line_to_cursor, '\\k*$')
local items, new_server_boundary = require('vim.lsp.completion')._convert_results(
line,
lnum,
cursor_col,
1,
client_start_boundary,
server_boundary,
result,
'utf-16'
)
return {
items = items,
server_start_boundary = new_server_boundary,
}
end, candidates)
end
describe('vim.lsp.completion: item conversion', function()
before_each(n.clear)
-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
it('prefers textEdit over label as word', function()
local range0 = {
start = { line = 0, character = 0 },
['end'] = { line = 0, character = 0 },
}
local completion_list = {
-- resolves into label
{ label = 'foobar', sortText = 'a', documentation = 'documentation' },
{
label = 'foobar',
sortText = 'b',
documentation = { value = 'documentation' },
},
-- resolves into insertText
{ label = 'foocar', sortText = 'c', insertText = 'foobar' },
{ label = 'foocar', sortText = 'd', insertText = 'foobar' },
-- resolves into textEdit.newText
{
label = 'foocar',
sortText = 'e',
insertText = 'foodar',
textEdit = { newText = 'foobar', range = range0 },
},
{ label = 'foocar', sortText = 'f', textEdit = { newText = 'foobar', range = range0 } },
-- plain text
{
label = 'foocar',
sortText = 'g',
insertText = 'foodar(${1:var1})',
insertTextFormat = 1,
},
{
label = '•INT16_C(c)',
insertText = 'INT16_C(${1:c})',
insertTextFormat = 2,
filterText = 'INT16_C',
sortText = 'h',
textEdit = {
newText = 'INT16_C(${1:c})',
range = range0,
},
},
}
local expected = {
{
abbr = 'foobar',
word = 'foobar',
},
{
abbr = 'foobar',
word = 'foobar',
},
{
abbr = 'foocar',
word = 'foobar',
},
{
abbr = 'foocar',
word = 'foobar',
},
{
abbr = 'foocar',
word = 'foobar',
},
{
abbr = 'foocar',
word = 'foobar',
},
{
abbr = 'foocar',
word = 'foodar(${1:var1})', -- marked as PlainText, text is used as is
},
{
abbr = '•INT16_C(c)',
word = 'INT16_C',
},
}
local result = complete('|', completion_list)
result = vim.tbl_map(function(x)
return {
abbr = x.abbr,
word = x.word,
}
end, result.items)
eq(expected, result)
end)
it('does not filter if there is a textEdit', function()
local range0 = {
start = { line = 0, character = 0 },
['end'] = { line = 0, character = 0 },
}
local completion_list = {
{ label = 'foo', textEdit = { newText = 'foo', range = range0 } },
{ label = 'bar', textEdit = { newText = 'bar', range = range0 } },
}
local result = complete('fo|', completion_list)
local expected = {
{
abbr = 'foo',
word = 'foo',
},
{
abbr = 'bar',
word = 'bar',
},
}
result = vim.tbl_map(function(x)
return {
abbr = x.abbr,
word = x.word,
}
end, result.items)
local sorter = function(a, b)
return a.word > b.word
end
table.sort(expected, sorter)
table.sort(result, sorter)
eq(expected, result)
end)
---@param prefix string
---@param items lsp.CompletionItem[]
---@param expected table[]
local assert_completion_matches = function(prefix, items, expected)
local result = complete(prefix .. '|', items)
result = vim.tbl_map(function(x)
return {
abbr = x.abbr,
word = x.word,
}
end, result.items)
local sorter = function(a, b)
return a.word > b.word
end
table.sort(expected, sorter)
table.sort(result, sorter)
eq(expected, result)
end
describe('when completeopt has fuzzy matching enabled', function()
before_each(function()
exec_lua(function()
vim.opt.completeopt:append('fuzzy')
end)
end)
after_each(function()
exec_lua(function()
vim.opt.completeopt:remove('fuzzy')
end)
end)
it('fuzzy matches on filterText', function()
assert_completion_matches('fo', {
{ label = '?.foo', filterText = 'foo' },
{ label = 'faz other', filterText = 'faz other' },
{ label = 'bar', filterText = 'bar' },
}, {
{
abbr = 'faz other',
word = 'faz other',
|