aboutsummaryrefslogtreecommitdiff
path: root/test/functional/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/plugin')
-rw-r--r--test/functional/plugin/editorconfig_spec.lua28
-rw-r--r--test/functional/plugin/health_spec.lua95
-rw-r--r--test/functional/plugin/lsp/codelens_spec.lua84
-rw-r--r--test/functional/plugin/lsp/completion_spec.lua547
-rw-r--r--test/functional/plugin/lsp/diagnostic_spec.lua536
-rw-r--r--test/functional/plugin/lsp/handler_spec.lua23
-rw-r--r--test/functional/plugin/lsp/incremental_sync_spec.lua91
-rw-r--r--test/functional/plugin/lsp/inlay_hint_spec.lua335
-rw-r--r--test/functional/plugin/lsp/semantic_tokens_spec.lua378
-rw-r--r--test/functional/plugin/lsp/testutil.lua125
-rw-r--r--test/functional/plugin/lsp/utils_spec.lua104
-rw-r--r--test/functional/plugin/lsp_spec.lua3807
-rw-r--r--test/functional/plugin/man_spec.lua42
-rw-r--r--test/functional/plugin/msgpack_spec.lua65
-rw-r--r--test/functional/plugin/shada_spec.lua77
-rw-r--r--test/functional/plugin/tohtml_spec.lua109
16 files changed, 3823 insertions, 2623 deletions
diff --git a/test/functional/plugin/editorconfig_spec.lua b/test/functional/plugin/editorconfig_spec.lua
index 839a723405..5f69b8938a 100644
--- a/test/functional/plugin/editorconfig_spec.lua
+++ b/test/functional/plugin/editorconfig_spec.lua
@@ -7,7 +7,6 @@ local eq = t.eq
local pathsep = n.get_pathsep()
local fn = n.fn
local api = n.api
-local exec_lua = n.exec_lua
local testdir = 'Xtest-editorconfig'
@@ -16,8 +15,16 @@ local testdir = 'Xtest-editorconfig'
local function test_case(name, expected)
local filename = testdir .. pathsep .. name
command('edit ' .. filename)
+
for opt, val in pairs(expected) do
- eq(val, api.nvim_get_option_value(opt, { buf = 0 }), name)
+ local opt_info = api.nvim_get_option_info2(opt, {})
+ if opt_info.scope == 'win' then
+ eq(val, api.nvim_get_option_value(opt, { win = 0 }), name)
+ elseif opt_info.scope == 'buf' then
+ eq(val, api.nvim_get_option_value(opt, { buf = 0 }), name)
+ else
+ eq(val, api.nvim_get_option_value(opt, {}), name)
+ end
end
end
@@ -93,6 +100,12 @@ setup(function()
[max_line_length.txt]
max_line_length = 42
+
+ [short_spelling_language.txt]
+ spelling_language = de
+
+ [long_spelling_language.txt]
+ spelling_language = en-NZ
]]
)
end)
@@ -213,13 +226,18 @@ But not this one
end)
it('does not operate on invalid buffers', function()
- local ok, err = unpack(exec_lua([[
+ local ok, err = unpack(n.exec_lua(function()
vim.cmd.edit('test.txt')
local bufnr = vim.api.nvim_get_current_buf()
vim.cmd.bwipeout(bufnr)
- return {pcall(require('editorconfig').config, bufnr)}
- ]]))
+ return { pcall(require('editorconfig').config, bufnr) }
+ end))
eq(true, ok, err)
end)
+
+ it('sets spelllang', function()
+ test_case('short_spelling_language.txt', { spelllang = 'de' })
+ test_case('long_spelling_language.txt', { spelllang = 'en_nz' })
+ end)
end)
diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua
index 9c7c953fb0..7089313303 100644
--- a/test/functional/plugin/health_spec.lua
+++ b/test/functional/plugin/health_spec.lua
@@ -40,11 +40,22 @@ describe(':checkhealth', function()
matches('ERROR $VIM .* zub', curbuf_contents())
end)
- it('completions can be listed via getcompletion()', function()
- clear()
+ it('getcompletion()', function()
+ clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } }
+
eq('vim.deprecated', getcompletion('vim', 'checkhealth')[1])
eq('vim.provider', getcompletion('vim.prov', 'checkhealth')[1])
eq('vim.lsp', getcompletion('vim.ls', 'checkhealth')[1])
+
+ -- "test_plug/health/init.lua" should complete as "test_plug", not "test_plug.health". #30342
+ eq({
+ 'test_plug',
+ 'test_plug.full_render',
+ 'test_plug.submodule',
+ 'test_plug.submodule_empty',
+ 'test_plug.success1',
+ 'test_plug.success2',
+ }, getcompletion('test_plug', 'checkhealth'))
end)
it('completion checks for vim.health._complete() return type #28456', function()
@@ -57,11 +68,9 @@ describe(':checkhealth', function()
end)
end)
-describe('health.vim', function()
+describe('vim.health', function()
before_each(function()
- clear { args = { '-u', 'NORC' } }
- -- Provides healthcheck functions
- command('set runtimepath+=test/functional/fixtures')
+ clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } }
end)
describe(':checkhealth', function()
@@ -70,7 +79,7 @@ describe('health.vim', function()
n.expect([[
==============================================================================
- test_plug.full_render: require("test_plug.full_render.health").check()
+ test_plug.full_render: require("test_plug.full_render.health").check()
report 1 ~
- OK life is fine
@@ -93,7 +102,7 @@ describe('health.vim', function()
n.expect([[
==============================================================================
- test_plug: require("test_plug.health").check()
+ test_plug: require("test_plug.health").check()
report 1 ~
- OK everything is fine
@@ -102,7 +111,7 @@ describe('health.vim', function()
- OK nothing to see here
==============================================================================
- test_plug.success1: require("test_plug.success1.health").check()
+ test_plug.success1: require("test_plug.success1.health").check()
report 1 ~
- OK everything is fine
@@ -111,7 +120,7 @@ describe('health.vim', function()
- OK nothing to see here
==============================================================================
- test_plug.success2: require("test_plug.success2.health").check()
+ test_plug.success2: require("test_plug.success2.health").check()
another 1 ~
- OK ok
@@ -123,7 +132,7 @@ describe('health.vim', function()
n.expect([[
==============================================================================
- test_plug.submodule: require("test_plug.submodule.health").check()
+ test_plug.submodule: require("test_plug.submodule.health").check()
report 1 ~
- OK everything is fine
@@ -148,9 +157,10 @@ describe('health.vim', function()
local screen = Screen.new(50, 12)
screen:attach()
screen:set_default_attr_ids({
+ h1 = { reverse = true },
+ h2 = { foreground = tonumber('0x6a0dad') },
Ok = { foreground = Screen.colors.LightGreen },
Error = { foreground = Screen.colors.Red },
- Heading = { foreground = tonumber('0x6a0dad') },
Bar = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGrey },
})
command('checkhealth foo success1')
@@ -158,15 +168,15 @@ describe('health.vim', function()
screen:expect {
grid = [[
^ |
- {Bar:──────────────────────────────────────────────────}|
- {Heading:foo: } |
+ {Bar: }|
+ {h1:foo: }|
|
- {Error:ERROR} No healthcheck found for "foo" plugin. |
|
- {Bar:──────────────────────────────────────────────────}|
- {Heading:test_plug.success1: require("test_plug.success1.he}|
+ {Bar: }|
+ {h1:test_plug.success1: require("test_pl}|
|
- {Heading:report 1} |
+ {h2:report 1} |
- {Ok:OK} everything is fine |
|
]],
@@ -179,7 +189,7 @@ describe('health.vim', function()
n.expect([[
==============================================================================
- non_existent_healthcheck:
+ non_existent_healthcheck:
- ERROR No healthcheck found for "non_existent_healthcheck" plugin.
]])
@@ -207,18 +217,17 @@ end)
describe(':checkhealth window', function()
before_each(function()
- clear { args = { '-u', 'NORC' } }
- -- Provides healthcheck functions
- command('set runtimepath+=test/functional/fixtures')
+ clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } }
command('set nofoldenable nowrap laststatus=0')
end)
it('opens directly if no buffer created', function()
local screen = Screen.new(50, 12)
screen:set_default_attr_ids {
+ h1 = { reverse = true },
+ h2 = { foreground = tonumber('0x6a0dad') },
[1] = { foreground = Screen.colors.Blue, bold = true },
[14] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray },
- [31] = { foreground = tonumber('0x6a0dad') },
[32] = { foreground = Screen.colors.PaleGreen2 },
}
screen:attach({ ext_multigrid = true })
@@ -230,15 +239,15 @@ describe(':checkhealth window', function()
[3:--------------------------------------------------]|
## grid 2
^ |
- {14:──────────────────────────────────────────────────}|
- {14:────────────────────────────} |
- {31:test_plug.success1: require("test_plug.success1. }|
- {31:health").check()} |
+ {14: }|
+ {14: } |
+ {h1:test_plug.success1: }|
+ {h1:require("test_plug.success1.health").check()} |
|
- {31:report 1} |
+ {h2:report 1} |
- {32:OK} everything is fine |
|
- {31:report 2} |
+ {h2:report 2} |
- {32:OK} nothing to see here |
## grid 3
|
@@ -249,9 +258,10 @@ describe(':checkhealth window', function()
local function test_health_vsplit(left, emptybuf, mods)
local screen = Screen.new(50, 20)
screen:set_default_attr_ids {
+ h1 = { reverse = true },
+ h2 = { foreground = tonumber('0x6a0dad') },
[1] = { foreground = Screen.colors.Blue, bold = true },
[14] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray },
- [31] = { foreground = tonumber('0x6a0dad') },
[32] = { foreground = Screen.colors.PaleGreen2 },
}
screen:attach({ ext_multigrid = true })
@@ -271,19 +281,20 @@ describe(':checkhealth window', function()
|
## grid 4
^ |
- {14:─────────────────────────}|*3
- {14:───} |
- {31:test_plug.success1: }|
- {31:require("test_plug. }|
- {31:success1.health").check()}|
+ {14: }|*3
+ {14: } |
+ {h1:test_plug. }|
+ {h1:success1: }|
+ {h1:require("test_plug. }|
+ {h1:success1.health").check()}|
|
- {31:report 1} |
+ {h2:report 1} |
- {32:OK} everything is fine |
|
- {31:report 2} |
+ {h2:report 2} |
- {32:OK} nothing to see here |
|
- {1:~ }|*4
+ {1:~ }|*3
]]):format(
left and '[4:-------------------------]│[2:------------------------]|*19'
or '[2:------------------------]│[4:-------------------------]|*19',
@@ -330,10 +341,10 @@ describe(':checkhealth window', function()
|
## grid 4
^ |
- ──────────────────────────────────────────────────|
- ──────────────────────────── |
- test_plug.success1: require("test_plug.success1. |
- health").check() |
+ |
+ |
+ test_plug.success1: |
+ require("test_plug.success1.health").check() |
|
report 1 |
- OK everything is fine |
@@ -382,7 +393,7 @@ describe(':checkhealth window', function()
command('file my_buff')
command('checkhealth success1')
-- define a function that collects all buffers in each tab
- -- returns a dictionary like {tab1 = ["buf1", "buf2"], tab2 = ["buf3"]}
+ -- returns a dict like {tab1 = ["buf1", "buf2"], tab2 = ["buf3"]}
source([[
function CollectBuffersPerTab()
let buffs = {}
diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua
index cd20e95dd1..20ef1cb49e 100644
--- a/test/functional/plugin/lsp/codelens_spec.lua
+++ b/test/functional/plugin/lsp/codelens_spec.lua
@@ -13,36 +13,34 @@ describe('vim.lsp.codelens', function()
it('on_codelens_stores_and_displays_lenses', function()
local fake_uri = 'file:///fake/uri'
- local bufnr = exec_lua(
- [[
- fake_uri = ...
+ local bufnr = exec_lua(function()
local bufnr = vim.uri_to_bufnr(fake_uri)
- local lines = {'So', 'many', 'lines'}
+ local lines = { 'So', 'many', 'lines' }
vim.fn.bufload(bufnr)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
return bufnr
- ]],
- fake_uri
- )
+ end)
- exec_lua(
- [[
- local bufnr = ...
+ exec_lua(function()
local lenses = {
{
range = {
- start = { line = 0, character = 0, },
- ['end'] = { line = 0, character = 0 }
+ start = { line = 0, character = 0 },
+ ['end'] = { line = 0, character = 0 },
},
- command = { title = 'Lens1', command = 'Dummy' }
+ command = { title = 'Lens1', command = 'Dummy' },
},
}
- vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr})
- ]],
- bufnr
- )
+ vim.lsp.codelens.on_codelens(
+ nil,
+ lenses,
+ { method = 'textDocument/codeLens', client_id = 1, bufnr = bufnr }
+ )
+ end)
- local stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr)
+ local stored_lenses = exec_lua(function()
+ return vim.lsp.codelens.get(bufnr)
+ end)
local expected = {
{
range = {
@@ -57,58 +55,54 @@ describe('vim.lsp.codelens', function()
}
eq(expected, stored_lenses)
- local virtual_text_chunks = exec_lua(
- [[
- local bufnr = ...
+ local virtual_text_chunks = exec_lua(function()
local ns = vim.lsp.codelens.__namespaces[1]
local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, {})
return vim.api.nvim_buf_get_extmark_by_id(bufnr, ns, extmarks[1][1], { details = true })[3].virt_text
- ]],
- bufnr
- )
+ end)
eq({ [1] = { 'Lens1', 'LspCodeLens' } }, virtual_text_chunks)
end)
it('can clear all lens', function()
local fake_uri = 'file:///fake/uri'
- local bufnr = exec_lua(
- [[
- fake_uri = ...
+ local bufnr = exec_lua(function()
local bufnr = vim.uri_to_bufnr(fake_uri)
- local lines = {'So', 'many', 'lines'}
+ local lines = { 'So', 'many', 'lines' }
vim.fn.bufload(bufnr)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
return bufnr
- ]],
- fake_uri
- )
+ end)
- exec_lua(
- [[
- local bufnr = ...
+ exec_lua(function()
local lenses = {
{
range = {
- start = { line = 0, character = 0, },
- ['end'] = { line = 0, character = 0 }
+ start = { line = 0, character = 0 },
+ ['end'] = { line = 0, character = 0 },
},
- command = { title = 'Lens1', command = 'Dummy' }
+ command = { title = 'Lens1', command = 'Dummy' },
},
}
- vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr})
- ]],
- bufnr
- )
+ vim.lsp.codelens.on_codelens(
+ nil,
+ lenses,
+ { method = 'textDocument/codeLens', client_id = 1, bufnr = bufnr }
+ )
+ end)
- local stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr)
+ local stored_lenses = exec_lua(function()
+ return vim.lsp.codelens.get(bufnr)
+ end)
eq(1, #stored_lenses)
- exec_lua([[
+ exec_lua(function()
vim.lsp.codelens.clear()
- ]])
+ end)
- stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr)
+ stored_lenses = exec_lua(function()
+ return vim.lsp.codelens.get(bufnr)
+ end)
eq(0, #stored_lenses)
end)
end)
diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua
index 2798d57381..4df8d77d44 100644
--- a/test/functional/plugin/lsp/completion_spec.lua
+++ b/test/functional/plugin/lsp/completion_spec.lua
@@ -1,9 +1,16 @@
---@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.
---
@@ -11,38 +18,32 @@ local exec_lua = n.exec_lua
---@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)
+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(
- [[
- local line, cursor_col, lnum, result = ...
+ 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, server_start_boundary = require("vim.lsp._completion")._convert_results(
+ local items, new_server_boundary = require('vim.lsp.completion')._convert_results(
line,
lnum,
cursor_col,
+ 1,
client_start_boundary,
- nil,
+ server_boundary,
result,
- "utf-16"
+ 'utf-16'
)
return {
items = items,
- server_start_boundary = server_start_boundary
+ server_start_boundary = new_server_boundary,
}
- ]],
- line,
- cursor_col,
- lnum,
- candidates
- )
+ end, candidates)
end
-describe('vim.lsp._completion', function()
+describe('vim.lsp.completion: item conversion', function()
before_each(n.clear)
-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
@@ -70,39 +71,24 @@ describe('vim.lsp._completion', function()
textEdit = { newText = 'foobar', range = range0 },
},
{ label = 'foocar', sortText = 'f', textEdit = { newText = 'foobar', range = range0 } },
- -- real-world snippet text
+ -- plain text
{
label = 'foocar',
sortText = 'g',
- insertText = 'foodar',
+ insertText = 'foodar(${1:var1})',
+ insertTextFormat = 1,
+ },
+ {
+ label = '•INT16_C(c)',
+ insertText = 'INT16_C(${1:c})',
insertTextFormat = 2,
+ filterText = 'INT16_C',
+ sortText = 'h',
textEdit = {
- newText = 'foobar(${1:place holder}, ${2:more ...holder{\\}})',
+ newText = 'INT16_C(${1:c})',
range = range0,
},
},
- {
- label = 'foocar',
- sortText = 'h',
- insertText = 'foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}',
- insertTextFormat = 2,
- },
- -- nested snippet tokens
- {
- label = 'foocar',
- sortText = 'i',
- insertText = 'foodar(${1:${2|typ1,typ2|}}) {$0\\}',
- insertTextFormat = 2,
- },
- -- braced tabstop
- { label = 'foocar', sortText = 'j', insertText = 'foodar()${0}', insertTextFormat = 2 },
- -- plain text
- {
- label = 'foocar',
- sortText = 'k',
- insertText = 'foodar(${1:var1})',
- insertTextFormat = 1,
- },
}
local expected = {
{
@@ -131,23 +117,167 @@ describe('vim.lsp._completion', function()
},
{
abbr = 'foocar',
- word = 'foobar(place holder, more ...holder{})',
+ word = 'foodar(${1:var1})', -- marked as PlainText, text is used as is
},
{
- abbr = 'foocar',
- word = 'foodar(var1 typ1, var2 *typ2) {}',
+ 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('filters on label if filterText is missing', function()
+ local completion_list = {
+ { label = 'foo' },
+ { label = 'bar' },
+ }
+ local result = complete('fo|', completion_list)
+ local expected = {
{
- abbr = 'foocar',
- word = 'foodar(typ1) {}',
+ abbr = 'foo',
+ word = 'foo',
},
+ }
+ result = vim.tbl_map(function(x)
+ return {
+ abbr = x.abbr,
+ word = x.word,
+ }
+ end, result.items)
+ eq(expected, result)
+ end)
+
+ it('works on non word prefix', function()
+ local completion_list = {
+ { label = ' foo', insertText = '->foo' },
+ }
+ local result = complete('wp.|', completion_list, 0, 2)
+ local expected = {
{
- abbr = 'foocar',
- word = 'foodar()',
+ abbr = ' foo',
+ word = '->foo',
},
+ }
+ result = vim.tbl_map(function(x)
+ return {
+ abbr = x.abbr,
+ word = x.word,
+ }
+ end, result.items)
+ eq(expected, result)
+ end)
+
+ it('trims trailing newline or tab from textEdit', function()
+ local range0 = {
+ start = { line = 0, character = 0 },
+ ['end'] = { line = 0, character = 0 },
+ }
+ local items = {
{
- abbr = 'foocar',
- word = 'foodar(${1:var1})',
+ detail = 'ansible.builtin',
+ filterText = 'lineinfile ansible.builtin.lineinfile builtin ansible',
+ kind = 7,
+ label = 'ansible.builtin.lineinfile',
+ sortText = '2_ansible.builtin.lineinfile',
+ textEdit = {
+ newText = 'ansible.builtin.lineinfile:\n ',
+ range = range0,
+ },
+ },
+ }
+ local result = complete('|', items)
+ result = vim.tbl_map(function(x)
+ return {
+ abbr = x.abbr,
+ word = x.word,
+ }
+ end, result.items)
+
+ local expected = {
+ {
+ abbr = 'ansible.builtin.lineinfile',
+ word = 'ansible.builtin.lineinfile:',
+ },
+ }
+ eq(expected, result)
+ end)
+
+ it('prefers wordlike components for snippets', function()
+ -- There are two goals here:
+ --
+ -- 1. The `word` should match what the user started typing, so that vim.fn.complete() doesn't
+ -- filter it away, preventing snippet expansion
+ --
+ -- For example, if they type `items@ins`, luals returns `table.insert(items, $0)` as
+ -- textEdit.newText and `insert` as label.
+ -- There would be no prefix match if textEdit.newText is used as `word`
+ --
+ -- 2. If users do not expand a snippet, but continue typing, they should see a somewhat reasonable
+ -- `word` getting inserted.
+ --
+ -- For example in:
+ --
+ -- insertText: "testSuites ${1:Env}"
+ -- label: "testSuites"
+ --
+ -- "testSuites" should have priority as `word`, as long as the full snippet gets expanded on accept (<c-y>)
+ local range0 = {
+ start = { line = 0, character = 0 },
+ ['end'] = { line = 0, character = 0 },
+ }
+ local completion_list = {
+ -- luals postfix snippet (typed text: items@ins|)
+ {
+ label = 'insert',
+ insertTextFormat = 2,
+ textEdit = {
+ newText = 'table.insert(items, $0)',
+ range = range0,
+ },
+ },
+
+ -- eclipse.jdt.ls `new` snippet
+ {
+ label = 'new',
+ insertTextFormat = 2,
+ textEdit = {
+ newText = '${1:Object} ${2:foo} = new ${1}(${3});\n${0}',
+ range = range0,
+ },
+ textEditText = '${1:Object} ${2:foo} = new ${1}(${3});\n${0}',
+ },
+
+ -- eclipse.jdt.ls `List.copyO` function call completion
+ {
+ label = 'copyOf(Collection<? extends E> coll) : List<E>',
+ insertTextFormat = 2,
+ insertText = 'copyOf',
+ textEdit = {
+ newText = 'copyOf(${1:coll})',
+ range = range0,
+ },
+ },
+ }
+ local expected = {
+ {
+ abbr = 'copyOf(Collection<? extends E> coll) : List<E>',
+ word = 'copyOf',
+ },
+ {
+ abbr = 'insert',
+ word = 'insert',
+ },
+ {
+ abbr = 'new',
+ word = 'new',
},
}
local result = complete('|', completion_list)
@@ -159,6 +289,7 @@ describe('vim.lsp._completion', function()
end, result.items)
eq(expected, result)
end)
+
it('uses correct start boundary', function()
local completion_list = {
isIncomplete = false,
@@ -186,8 +317,10 @@ describe('vim.lsp._completion', function()
dup = 1,
empty = 1,
icase = 1,
+ info = '',
kind = 'Module',
menu = '',
+ hl_group = '',
word = 'this_thread',
}
local result = complete(' std::this|', completion_list)
@@ -218,7 +351,7 @@ describe('vim.lsp._completion', function()
},
},
{
- filterText = 'notthis_thread',
+ filterText = 'no_match',
insertText = 'notthis_thread',
insertTextFormat = 1,
kind = 9,
@@ -240,8 +373,10 @@ describe('vim.lsp._completion', function()
dup = 1,
empty = 1,
icase = 1,
+ info = '',
kind = 'Module',
menu = '',
+ hl_group = '',
word = 'this_thread',
}
local result = complete(' std::this|is', completion_list)
@@ -278,4 +413,316 @@ describe('vim.lsp._completion', function()
eq('item-property-has-priority', item.data)
eq({ line = 1, character = 1 }, item.textEdit.range.start)
end)
+
+ it(
+ 'uses insertText as textEdit.newText if there are editRange defaults but no textEditText',
+ function()
+ --- @type lsp.CompletionList
+ local completion_list = {
+ isIncomplete = false,
+ itemDefaults = {
+ editRange = {
+ start = { line = 1, character = 1 },
+ ['end'] = { line = 1, character = 4 },
+ },
+ insertTextFormat = 2,
+ data = 'foobar',
+ },
+ items = {
+ {
+ insertText = 'the-insertText',
+ label = 'hello',
+ data = 'item-property-has-priority',
+ },
+ },
+ }
+ local result = complete('|', completion_list)
+ eq(1, #result.items)
+ local text = result.items[1].user_data.nvim.lsp.completion_item.textEdit.newText
+ eq('the-insertText', text)
+ end
+ )
+
+ it(
+ 'defaults to label as textEdit.newText if insertText or textEditText are not present',
+ function()
+ local completion_list = {
+ isIncomplete = false,
+ itemDefaults = {
+ editRange = {
+ start = { line = 1, character = 1 },
+ ['end'] = { line = 1, character = 4 },
+ },
+ insertTextFormat = 2,
+ data = 'foobar',
+ },
+ items = {
+ {
+ label = 'hello',
+ data = 'item-property-has-priority',
+ },
+ },
+ }
+ local result = complete('|', completion_list)
+ eq(1, #result.items)
+ local text = result.items[1].user_data.nvim.lsp.completion_item.textEdit.newText
+ eq('hello', text)
+ end
+ )
+end)
+
+describe('vim.lsp.completion: protocol', function()
+ before_each(function()
+ clear()
+ exec_lua(create_server_definition)
+ exec_lua(function()
+ _G.capture = {}
+ --- @diagnostic disable-next-line:duplicate-set-field
+ vim.fn.complete = function(col, matches)
+ _G.capture.col = col
+ _G.capture.matches = matches
+ end
+ end)
+ end)
+
+ after_each(clear)
+
+ --- @param completion_result lsp.CompletionList
+ --- @return integer
+ local function create_server(completion_result)
+ return exec_lua(function()
+ local server = _G._create_server({
+ capabilities = {
+ completionProvider = {
+ triggerCharacters = { '.' },
+ },
+ },
+ handlers = {
+ ['textDocument/completion'] = function(_, _, callback)
+ callback(nil, completion_result)
+ end,
+ },
+ })
+
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+ return vim.lsp.start({
+ name = 'dummy',
+ cmd = server.cmd,
+ on_attach = function(client, bufnr0)
+ vim.lsp.completion.enable(true, client.id, bufnr0, {
+ convert = function(item)
+ return { abbr = item.label:gsub('%b()', '') }
+ end,
+ })
+ end,
+ })
+ end)
+ end
+
+ local function assert_matches(fn)
+ retry(nil, nil, function()
+ fn(exec_lua('return _G.capture.matches'))
+ end)
+ end
+
+ --- @param pos [integer, integer]
+ local function trigger_at_pos(pos)
+ exec_lua(function()
+ local win = vim.api.nvim_get_current_win()
+ vim.api.nvim_win_set_cursor(win, pos)
+ vim.lsp.completion.trigger()
+ end)
+
+ retry(nil, nil, function()
+ neq(nil, exec_lua('return _G.capture.col'))
+ end)
+ end
+
+ it('fetches completions and shows them using complete on trigger', function()
+ create_server({
+ isIncomplete = false,
+ items = {
+ {
+ label = 'hello',
+ },
+ {
+ label = 'hercules',
+ tags = { 1 }, -- 1 represents Deprecated tag
+ },
+ {
+ label = 'hero',
+ deprecated = true,
+ },