aboutsummaryrefslogtreecommitdiff
path: root/test/functional/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/plugin')
-rw-r--r--test/functional/plugin/health_spec.lua12
-rw-r--r--test/functional/plugin/lsp/completion_spec.lua54
-rw-r--r--test/functional/plugin/lsp/diagnostic_spec.lua61
-rw-r--r--test/functional/plugin/lsp/folding_range_spec.lua647
-rw-r--r--test/functional/plugin/lsp/handler_spec.lua42
-rw-r--r--test/functional/plugin/lsp/incremental_sync_spec.lua10
-rw-r--r--test/functional/plugin/lsp/semantic_tokens_spec.lua2
-rw-r--r--test/functional/plugin/lsp/testutil.lua13
-rw-r--r--test/functional/plugin/lsp/utils_spec.lua62
-rw-r--r--test/functional/plugin/lsp_spec.lua419
-rw-r--r--test/functional/plugin/man_spec.lua26
11 files changed, 1169 insertions, 179 deletions
diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua
index 753da64522..406b5c3c16 100644
--- a/test/functional/plugin/health_spec.lua
+++ b/test/functional/plugin/health_spec.lua
@@ -66,6 +66,18 @@ describe(':checkhealth', function()
eq({}, getcompletion('', 'checkhealth'))
assert_alive()
end)
+
+ it('vim.g.health', function()
+ clear()
+ command("let g:health = {'style':'float'}")
+ command('checkhealth lsp')
+ eq(
+ 'editor',
+ exec_lua([[
+ return vim.api.nvim_win_get_config(0).relative
+ ]])
+ )
+ end)
end)
describe('vim.health', function()
diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua
index 39b6ddc105..4e90c2fd1b 100644
--- a/test/functional/plugin/lsp/completion_spec.lua
+++ b/test/functional/plugin/lsp/completion_spec.lua
@@ -216,6 +216,43 @@ 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' },
@@ -731,9 +768,10 @@ describe('vim.lsp.completion: item conversion', function()
)
end)
+--- @param name string
--- @param completion_result lsp.CompletionList
--- @return integer
-local function create_server(completion_result)
+local function create_server(name, completion_result)
return exec_lua(function()
local server = _G._create_server({
capabilities = {
@@ -751,7 +789,7 @@ local function create_server(completion_result)
local bufnr = vim.api.nvim_get_current_buf()
vim.api.nvim_win_set_buf(0, bufnr)
return vim.lsp.start({
- name = 'dummy',
+ name = name,
cmd = server.cmd,
on_attach = function(client, bufnr0)
vim.lsp.completion.enable(true, client.id, bufnr0, {
@@ -800,7 +838,7 @@ describe('vim.lsp.completion: protocol', function()
end
it('fetches completions and shows them using complete on trigger', function()
- create_server({
+ create_server('dummy', {
isIncomplete = false,
items = {
{
@@ -892,7 +930,7 @@ describe('vim.lsp.completion: protocol', function()
end)
it('merges results from multiple clients', function()
- create_server({
+ create_server('dummy1', {
isIncomplete = false,
items = {
{
@@ -900,7 +938,7 @@ describe('vim.lsp.completion: protocol', function()
},
},
})
- create_server({
+ create_server('dummy2', {
isIncomplete = false,
items = {
{
@@ -933,7 +971,7 @@ describe('vim.lsp.completion: protocol', function()
},
},
}
- local client_id = create_server(completion_list)
+ local client_id = create_server('dummy', completion_list)
exec_lua(function()
_G.called = false
@@ -970,7 +1008,7 @@ describe('vim.lsp.completion: protocol', function()
end)
it('enable(…,{convert=fn}) custom word/abbr format', function()
- create_server({
+ create_server('dummy', {
isIncomplete = false,
items = {
{
@@ -1012,7 +1050,7 @@ describe('vim.lsp.completion: integration', function()
exec_lua(function()
vim.o.completeopt = 'menuone,noselect'
end)
- create_server(completion_list)
+ create_server('dummy', completion_list)
feed('i world<esc>0ih<c-x><c-o>')
retry(nil, nil, function()
eq(
diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua
index 5afbe22793..4ecb056d01 100644
--- a/test/functional/plugin/lsp/diagnostic_spec.lua
+++ b/test/functional/plugin/lsp/diagnostic_spec.lua
@@ -89,7 +89,7 @@ describe('vim.lsp.diagnostic', function()
return extmarks
end
- client_id = assert(vim.lsp.start_client {
+ client_id = assert(vim.lsp.start({
cmd_env = {
NVIM_LUA_NOTRACK = '1',
},
@@ -101,7 +101,7 @@ describe('vim.lsp.diagnostic', function()
'--headless',
},
offset_encoding = 'utf-16',
- })
+ }, { attach = false }))
end)
fake_uri = 'file:///fake/uri'
@@ -209,10 +209,16 @@ describe('vim.lsp.diagnostic', function()
before_each(function()
exec_lua(create_server_definition)
exec_lua(function()
+ _G.requests = 0
_G.server = _G._create_server({
capabilities = {
diagnosticProvider = {},
},
+ handlers = {
+ [vim.lsp.protocol.Methods.textDocument_diagnostic] = function()
+ _G.requests = _G.requests + 1
+ end,
+ },
})
function _G.get_extmarks(bufnr, client_id0)
@@ -373,5 +379,56 @@ describe('vim.lsp.diagnostic', function()
end)
)
end)
+
+ it('handles server cancellation', function()
+ eq(
+ 1,
+ exec_lua(function()
+ vim.lsp.diagnostic.on_diagnostic({
+ code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
+ -- Empty data defaults to retriggering request
+ data = {},
+ message = '',
+ }, {}, {
+ method = vim.lsp.protocol.Methods.textDocument_diagnostic,
+ client_id = client_id,
+ })
+
+ return _G.requests
+ end)
+ )
+
+ eq(
+ 2,
+ exec_lua(function()
+ vim.lsp.diagnostic.on_diagnostic({
+ code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
+ data = { retriggerRequest = true },
+ message = '',
+ }, {}, {
+ method = vim.lsp.protocol.Methods.textDocument_diagnostic,
+ client_id = client_id,
+ })
+
+ return _G.requests
+ end)
+ )
+
+ eq(
+ 2,
+ exec_lua(function()
+ vim.lsp.diagnostic.on_diagnostic({
+ code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
+ data = { retriggerRequest = false },
+ message = '',
+ }, {}, {
+ method = vim.lsp.protocol.Methods.textDocument_diagnostic,
+ client_id = client_id,
+ })
+
+ return _G.requests
+ end)
+ )
+ end)
end)
end)
diff --git a/test/functional/plugin/lsp/folding_range_spec.lua b/test/functional/plugin/lsp/folding_range_spec.lua
new file mode 100644
index 0000000000..7e68a598d2
--- /dev/null
+++ b/test/functional/plugin/lsp/folding_range_spec.lua
@@ -0,0 +1,647 @@
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+local Screen = require('test.functional.ui.screen')
+local t_lsp = require('test.functional.plugin.lsp.testutil')
+
+local eq = t.eq
+local tempname = t.tmpname
+
+local clear_notrace = t_lsp.clear_notrace
+local create_server_definition = t_lsp.create_server_definition
+
+local api = n.api
+local exec_lua = n.exec_lua
+local insert = n.insert
+local command = n.command
+local feed = n.feed
+
+describe('vim.lsp.folding_range', function()
+ local text = [[// foldLevel() {{{2
+/// @return fold level at line number "lnum" in the current window.
+static int foldLevel(linenr_T lnum)
+{
+ // While updating the folds lines between invalid_top and invalid_bot have
+ // an undefined fold level. Otherwise update the folds first.
+ if (invalid_top == 0) {
+ checkupdate(curwin);
+ } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) {
+ return prev_lnum_lvl;
+ } else if (lnum >= invalid_top && lnum <= invalid_bot) {
+ return -1;
+ }
+
+ // Return quickly when there is no folding at all in this window.
+ if (!hasAnyFolding(curwin)) {
+ return 0;
+ }
+
+ return foldLevelWin(curwin, lnum);
+}]]
+
+ local result = {
+ {
+ endLine = 19,
+ kind = 'region',
+ startCharacter = 1,
+ startLine = 3,
+ },
+ {
+ endCharacter = 2,
+ endLine = 7,
+ kind = 'region',
+ startCharacter = 25,
+ startLine = 6,
+ },
+ {
+ endCharacter = 2,
+ endLine = 9,
+ kind = 'region',
+ startCharacter = 55,
+ startLine = 8,
+ },
+ {
+ endCharacter = 2,
+ endLine = 11,
+ kind = 'region',
+ startCharacter = 58,
+ startLine = 10,
+ },
+ {
+ endCharacter = 2,
+ endLine = 16,
+ kind = 'region',
+ startCharacter = 31,
+ startLine = 15,
+ },
+ {
+ endCharacter = 68,
+ endLine = 1,
+ kind = 'comment',
+ startCharacter = 2,
+ startLine = 0,
+ },
+ {
+ endCharacter = 64,
+ endLine = 5,
+ kind = 'comment',
+ startCharacter = 4,
+ startLine = 4,
+ },
+ }
+
+ local bufnr ---@type integer
+ local client_id ---@type integer
+
+ clear_notrace()
+ before_each(function()
+ clear_notrace()
+
+ exec_lua(create_server_definition)
+ bufnr = n.api.nvim_get_current_buf()
+ client_id = exec_lua(function()
+ _G.server = _G._create_server({
+ capabilities = {
+ foldingRangeProvider = true,
+ },
+ handlers = {
+ ['textDocument/foldingRange'] = function(_, _, callback)
+ callback(nil, result)
+ end,
+ },
+ })
+
+ vim.api.nvim_win_set_buf(0, bufnr)
+
+ return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
+ end)
+ command('set foldmethod=expr foldcolumn=1 foldlevel=999')
+ insert(text)
+ end)
+ after_each(function()
+ api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
+ end)
+
+ describe('setup()', function()
+ ---@type integer
+ local bufnr_set_expr
+ ---@type integer
+ local bufnr_never_set_expr
+
+ local function buf_autocmd_num(bufnr_to_check)
+ return exec_lua(function()
+ return #vim.api.nvim_get_autocmds({ buffer = bufnr_to_check, event = 'LspNotify' })
+ end)
+ end
+
+ before_each(function()
+ command([[setlocal foldexpr=v:lua.vim.lsp.foldexpr()]])
+ exec_lua(function()
+ bufnr_set_expr = vim.api.nvim_create_buf(true, false)
+ vim.api.nvim_set_current_buf(bufnr_set_expr)
+ end)
+ insert(text)
+ command('write ' .. tempname(false))
+ command([[setlocal foldexpr=v:lua.vim.lsp.foldexpr()]])
+ exec_lua(function()
+ bufnr_never_set_expr = vim.api.nvim_create_buf(true, false)
+ vim.api.nvim_set_current_buf(bufnr_never_set_expr)
+ end)
+ insert(text)
+ api.nvim_win_set_buf(0, bufnr_set_expr)
+ end)
+
+ it('only create event hooks where foldexpr has been set', function()
+ eq(1, buf_autocmd_num(bufnr))
+ eq(1, buf_autocmd_num(bufnr_set_expr))
+ eq(0, buf_autocmd_num(bufnr_never_set_expr))
+ end)
+
+ it('does not create duplicate event hooks after reloaded', function()
+ command('edit')
+ eq(1, buf_autocmd_num(bufnr_set_expr))
+ end)
+
+ it('cleans up event hooks when buffer is unloaded', function()
+ command('bdelete')
+ eq(0, buf_autocmd_num(bufnr_set_expr))
+ end)
+ end)
+
+ describe('expr()', function()
+ --- @type test.functional.ui.screen
+ local screen
+ before_each(function()
+ screen = Screen.new(80, 45)
+ screen:set_default_attr_ids({
+ [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue },
+ [2] = { bold = true, foreground = Screen.colors.Blue1 },
+ [3] = { bold = true, reverse = true },
+ [4] = { reverse = true },
+ })
+ command([[set foldexpr=v:lua.vim.lsp.foldexpr()]])
+ command([[split]])
+ end)
+
+ it('can compute fold levels', function()
+ ---@type table<integer, string>
+ local foldlevels = {}
+ for i = 1, 21 do
+ foldlevels[i] = exec_lua('return vim.lsp.foldexpr(' .. i .. ')')
+ end
+ eq({
+ [1] = '>1',
+ [2] = '<1',
+ [3] = '0',
+ [4] = '>1',
+ [5] = '>2',
+ [6] = '<2',
+ [7] = '>2',
+ [8] = '<2',
+ [9] = '>2',
+ [10] = '<2',
+ [11] = '>2',
+ [12] = '<2',
+ [13] = '1',
+ [14] = '1',
+ [15] = '1',
+ [16] = '>2',
+ [17] = '<2',
+ [18] = '1',
+ [19] = '1',
+ [20] = '<1',
+ [21] = '0',
+ }, foldlevels)
+ end)
+
+ it('updates folds in all windows', function()
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:[No Name] [+] }|
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }} |
+{4:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
+
+ it('persists wherever foldexpr is set', function()
+ command([[setlocal foldexpr=]])
+ feed('<C-w><C-w>zx')
+ screen:expect({
+ grid = [[
+{1: }// foldLevel() {{{2 |
+{1: }/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1: }{ |
+{1: } // While updating the folds lines between invalid_top and invalid_bot have |
+{1: } // an undefined fold level. Otherwise update the folds first. |
+{1: } if (invalid_top == 0) { |
+{1: } checkupdate(curwin); |
+{1: } } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1: } return prev_lnum_lvl; |
+{1: } } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1: } return -1; |
+{1: } } |
+{1: } |
+{1: } // Return quickly when there is no folding at all in this window. |
+{1: } if (!hasAnyFolding(curwin)) { |
+{1: } return 0; |
+{1: } } |
+{1: } |
+{1: } return foldLevelWin(curwin, lnum); |
+{1: }} |
+{4:[No Name] [+] }|
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
+
+ it('synchronizes changed rows with their previous foldlevels', function()
+ command('1,2d')
+ screen:expect({
+ grid = [[
+{1: }^static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }} |
+{2:~ }|*2
+{3:[No Name] [+] }|
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }} |
+{2:~ }|*2
+{4:[No Name] [+] }|
+ |
+]],
+ })
+ end)
+
+ it('clears folds when sole client detaches', function()
+ exec_lua(function()
+ vim.lsp.buf_detach_client(bufnr, client_id)
+ end)
+ screen:expect({
+ grid = [[
+{1: }// foldLevel() {{{2 |
+{1: }/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1: }{ |
+{1: } // While updating the folds lines between invalid_top and invalid_bot have |
+{1: } // an undefined fold level. Otherwise update the folds first. |
+{1: } if (invalid_top == 0) { |
+{1: } checkupdate(curwin); |
+{1: } } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1: } return prev_lnum_lvl; |
+{1: } } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1: } return -1; |
+{1: } } |
+{1: } |
+{1: } // Return quickly when there is no folding at all in this window. |
+{1: } if (!hasAnyFolding(curwin)) { |
+{1: } return 0; |
+{1: } } |
+{1: } |
+{1: } return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:[No Name] [+] }|
+{1: }// foldLevel() {{{2 |
+{1: }/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1: }{ |
+{1: } // While updating the folds lines between invalid_top and invalid_bot have |
+{1: } // an undefined fold level. Otherwise update the folds first. |
+{1: } if (invalid_top == 0) { |
+{1: } checkupdate(curwin); |
+{1: } } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1: } return prev_lnum_lvl; |
+{1: } } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1: } return -1; |
+{1: } } |
+{1: } |
+{1: } // Return quickly when there is no folding at all in this window. |
+{1: } if (!hasAnyFolding(curwin)) { |
+{1: } return 0; |
+{1: } } |
+{1: } |
+{1: } return foldLevelWin(curwin, lnum); |
+{1: }} |
+{4:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
+
+ it('remains valid after the client re-attaches.', function()
+ exec_lua(function()
+ vim.lsp.buf_detach_client(bufnr, client_id)
+ vim.lsp.buf_attach_client(bufnr, client_id)
+ end)
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:[No Name] [+] }|
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }} |
+{4:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
+ end)
+
+ describe('foldtext()', function()
+ --- @type test.functional.ui.screen
+ local screen
+ before_each(function()
+ screen = Screen.new(80, 23)
+ screen:set_default_attr_ids({
+ [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue },
+ [2] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey },
+ [3] = { bold = true, foreground = Screen.colors.Blue1 },
+ [4] = { bold = true, reverse = true },
+ [5] = { reverse = true },
+ })
+ command(
+ [[set foldexpr=v:lua.vim.lsp.foldexpr() foldtext=v:lua.vim.lsp.foldtext() foldlevel=1]]
+ )
+ end)
+
+ it('shows the first folded line if `collapsedText` does not exist', function()
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:+}{2: // While updating the folds lines between invalid_top and invalid_bot have···}|
+{1:+}{2: if (invalid_top == 0) {······················································}|
+{1:+}{2: } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) {························}|
+{1:+}{2: } else if (lnum >= invalid_top && lnum <= invalid_bot) {·····················}|
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:+}{2: if (!hasAnyFolding(curwin)) {················································}|
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:~ }|*6
+ |
+ ]],
+ })
+ end)
+ end)
+
+ describe('foldclose()', function()
+ --- @type test.functional.ui.screen
+ local screen
+ before_each(function()
+ screen = Screen.new(80, 23)
+ screen:set_default_attr_ids({
+ [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue },
+ [2] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey },
+ [3] = { bold = true, foreground = Screen.colors.Blue1 },
+ [4] = { bold = true, reverse = true },
+ [5] = { reverse = true },
+ })
+ command([[set foldexpr=v:lua.vim.lsp.foldexpr()]])
+ end)
+
+ it('closes all folds of one kind immediately', function()
+ exec_lua(function()
+ vim.lsp.foldclose('comment')
+ end)
+ screen:expect({
+ grid = [[
+{1:+}{2:+-- 2 lines: foldLevel()······················································}|
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:+}{2:+--- 2 lines: While updating the folds lines between invalid_top and invalid_b}|
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:~ }|*3
+ |
+ ]],
+ })
+ end)
+
+ it('closes the smallest fold first', function()
+ exec_lua(function()
+ vim.lsp.foldclose('region')
+ end)
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:+}{2:+-- 17 lines: {································································}|
+{1: }^} |
+{3:~ }|*17
+ |
+ ]],
+ })
+ command('4foldopen')
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:+}{2:+--- 2 lines: if (invalid_top == 0) {·········································}|
+{1:+}{2:+--- 2 lines: } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) {···········}|
+{1:+}{2:+--- 2 lines: } else if (lnum >= invalid_top && lnum <= invalid_bot) {········}|
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:+}{2:+--- 2 lines: if (!hasAnyFolding(curwin)) {···································}|
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:~ }|*5
+ |
+ ]],
+ })
+ end)
+
+ it('is defered when the buffer is not up-to-date', function()
+ exec_lua(function()
+ vim.lsp.foldclose('comment')
+ vim.lsp.util.buf_versions[bufnr] = 0
+ end)
+ screen:expect({
+ grid = [[
+{1:+}{2:+-- 2 lines: foldLevel()······················································}|
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:+}{2:+--- 2 lines: While updating the folds lines between invalid_top and invalid_b}|
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:~ }|*3
+ |
+ ]],
+ })
+ end)
+ end)
+end)
diff --git a/test/functional/plugin/lsp/handler_spec.lua b/test/functional/plugin/lsp/handler_spec.lua
deleted file mode 100644
index 4b05b676a8..0000000000
--- a/test/functional/plugin/lsp/handler_spec.lua
+++ /dev/null
@@ -1,42 +0,0 @@
-local t = require('test.testutil')
-local n = require('test.functional.testnvim')()
-
-local eq = t.eq
-local exec_lua = n.exec_lua
-local pcall_err = t.pcall_err
-local matches = t.matches
-
-describe('lsp-handlers', function()
- describe('vim.lsp._with_extend', function()
- it('should return a table with the default keys', function()
- eq(
- { hello = 'world' },
- exec_lua(function()
- return vim.lsp._with_extend('test', { hello = 'world' })
- end)
- )
- end)
-
- it('should override with config keys', function()
- eq(
- { hello = 'universe', other = true },
- exec_lua(function()
- return vim.lsp._with_extend(
- 'test',
- { other = true, hello = 'world' },
- { hello = 'universe' }
- )
- end)
- )
- end)
-
- it('should not allow invalid keys', function()
- matches(
- '.*Invalid option for `test`.*',
- pcall_err(exec_lua, function()
- return vim.lsp._with_extend('test', { hello = 'world' }, { invalid = true })
- end)
- )
- end)
- end)
-end)
diff --git a/test/functional/plugin/lsp/incremental_sync_spec.lua b/test/functional/plugin/lsp/incremental_sync_spec.lua
index f60e159d64..0bca1fa4ca 100644
--- a/test/functional/plugin/lsp/incremental_sync_spec.lua
+++ b/test/functional/plugin/lsp/incremental_sync_spec.lua
@@ -23,7 +23,7 @@ before_each(function()
-- local line_ending = format_line_ending[vim.api.nvim_get_option_value('fileformat', {})]
--- @diagnostic disable-next-line:duplicate-set-field
- function _G.test_register(bufnr, id, offset_encoding, line_ending)
+ function _G.test_register(bufnr, id, position_encoding, line_ending)
local prev_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
local function callback(_, bufnr0, _changedtick, firstline, lastline, new_lastline)
@@ -38,7 +38,7 @@ before_each(function()
firstline,
lastline,
new_lastline,
- offset_encoding,
+ position_encoding,
line_ending
)
@@ -63,15 +63,15 @@ local function test_edit(
prev_buffer,
edit_operations,
expected_text_changes,
- offset_encoding,
+ position_encoding,
line_ending
)
- offset_encoding = offset_encoding or 'utf-16'
+ position_encoding = position_encoding or 'utf-16'
line_ending = line_ending or '\n'
api.nvim_buf_set_lines(0, 0, -1, true, prev_buffer)
exec_lua(function()
- return _G.test_register(0, 'test1', offset_encoding, line_ending)
+ return _G.test_register(0, 'test1', position_encoding, line_ending)
end)
for _, edit in ipairs(edit_operations) do
diff --git a/test/functional/plugin/lsp/semantic_tokens_spec.lua b/test/functional/plugin/lsp/semantic_tokens_spec.lua
index 280bd27207..9912bf2063 100644
--- a/test/functional/plugin/lsp/semantic_tokens_spec.lua
+++ b/test/functional/plugin/lsp/semantic_tokens_spec.lua
@@ -456,7 +456,7 @@ describe('semantic token highlighting', function()
vim.notify = function(...)
table.insert(_G.notifications, 1, { ... })
end
- return vim.lsp.start_client({ name = 'dummy', cmd = _G.server.cmd })
+ return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }, { attach = false })
end)
eq(false, exec_lua('return vim.lsp.buf_is_attached(0, ...)', client_id))
diff --git a/test/functional/plugin/lsp/testutil.lua b/test/functional/plugin/lsp/testutil.lua
index a36cbac568..95fc22b96b 100644
--- a/test/functional/plugin/lsp/testutil.lua
+++ b/test/functional/plugin/lsp/testutil.lua
@@ -182,16 +182,17 @@ function M.test_rpc_server(config)
)
end
local client = setmetatable({}, {
- __index = function(_, name)
+ __index = function(t, name)
-- Workaround for not being able to yield() inside __index for Lua 5.1 :(
-- Otherwise I would just return the value here.
- return function(...)
+ return function(arg1, ...)
+ local ismethod = arg1 == t
return exec_lua(function(...)
- if type(_G.TEST_RPC_CLIENT[name]) == 'function' then
- return _G.TEST_RPC_CLIENT[name](...)
- else
- return _G.TEST_RPC_CLIENT[name]
+ local client = _G.TEST_RPC_CLIENT
+ if type(client[name]) == 'function' then
+ return client[name](ismethod and client or arg1, ...)
end
+ return client[name]
end, ...)
end
end,
diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua
index 813b8de812..1e3e759e0b 100644
--- a/test/functional/plugin/lsp/utils_spec.lua
+++ b/test/functional/plugin/lsp/utils_spec.lua
@@ -5,6 +5,8 @@ local Screen = require('test.functional.ui.screen')
local feed = n.feed
local eq = t.eq
local exec_lua = n.exec_lua
+local command, api = n.command, n.api
+local pcall_err = t.pcall_err
describe('vim.lsp.util', function()
before_each(n.clear)
@@ -265,6 +267,66 @@ describe('vim.lsp.util', function()
eq(56, opts.height)
end)
+
+ describe('vim.lsp.util.open_floating_preview', function()
+ local var_name = 'lsp_floating_preview'
+ local curbuf = api.nvim_get_current_buf()
+
+ it('clean bufvar after fclose', function()
+ exec_lua(function()
+ vim.lsp.util.open_floating_preview({ 'test' }, '', { height = 5, width = 2 })
+ end)
+ eq(true, api.nvim_win_is_valid(api.nvim_buf_get_var(curbuf, var_name)))
+ command('fclose')
+ eq(
+ 'Key not found: lsp_floating_preview',
+ pcall_err(api.nvim_buf_get_var, curbuf, var_name)
+ )
+ end)
+
+ it('clean bufvar after CursorMoved', function()
+ local result = exec_lua(function()
+ vim.lsp.util.open_floating_preview({ 'test' }, '', { height = 5, width = 2 })
+ local winnr = vim.b[vim.api.nvim_get_current_buf()].lsp_floating_preview
+ local result = vim.api.nvim_win_is_valid(winnr)
+ vim.api.nvim_feedkeys(vim.keycode('G'), 'txn', false)
+ return result
+ end)
+ eq(true, result)
+ eq(
+ 'Key not found: lsp_floating_preview',
+ pcall_err(api.nvim_buf_get_var, curbuf, var_name)
+ )
+ end)
+ end)
+ end)
+ end)
+
+ it('open_floating_preview zindex greater than current window', function()
+ local screen = Screen.new()
+ exec_lua(function()
+ vim.api.nvim_open_win(0, true, {
+ relative = 'editor',
+ border = 'single',
+ height = 11,
+ width = 51,
+ row = 2,
+ col = 2,
+ })
+ vim.keymap.set('n', 'K', function()
+ vim.lsp.util.open_floating_preview({ 'foo' }, '', { border = 'single' })
+ end, {})
end)
+ feed('K')
+ screen:expect([[
+ ┌───────────────────────────────────────────────────┐|
+ │{4:^ }│|
+ │┌───┐{11: }│|
+ ││{4:foo}│{11: }│|
+ │└───┘{11: }│|
+ │{11:~ }│|*7
+ └───────────────────────────────────────────────────┘|
+ |
+ ]])
end)
end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 5222216faf..db3ab8ed98 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -95,7 +95,7 @@ describe('LSP', function()
exec_lua(function()
_G.lsp = require('vim.lsp')
function _G.test__start_client()
- return vim.lsp.start_client {
+ return vim.lsp.start({
cmd_env = {
NVIM_LOG_FILE = fake_lsp_logfile,
NVIM_APPNAME = 'nvim_lsp_test',
@@ -112,7 +112,7 @@ describe('LSP', function()
name = 'test_folder',
},
},
- }
+ }, { attach = false })
end
_G.TEST_CLIENT1 = _G.test__start_client()
end)
@@ -232,7 +232,7 @@ describe('LSP', function()
-- client is a dummy object which will queue up commands to be run
-- once the server initializes. It can't accept lua callbacks or
-- other types that may be unserializable for now.
- client.stop()
+ client:stop()
end,
-- If the program timed out, then code will be nil.
on_exit = function(code, signal)
@@ -254,8 +254,8 @@ describe('LSP', function()
test_rpc_server {
test_name = 'basic_init',
on_init = function(client)
- client.notify('test')
- client.stop()
+ client:notify('test')
+ client:stop()
end,
on_exit = function(code, signal)
eq(101, code, 'exit code') -- See fake-lsp-server.lua
@@ -275,7 +275,7 @@ describe('LSP', function()
test_rpc_server({
test_name = 'basic_init_did_change_configuration',
on_init = function(client, _)
- client.stop()
+ client:stop()
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -333,9 +333,9 @@ describe('LSP', function()
test_name = 'basic_init',
on_init = function(client)
eq(0, client.server_capabilities().textDocumentSync.change)
- client.request('shutdown')
- client.notify('exit')
- client.stop()
+ client:request('shutdown')
+ client:notify('exit')
+ client:stop()
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -377,7 +377,7 @@ describe('LSP', function()
end,
on_init = function(_client)
client = _client
- client.notify('finish')
+ client:notify('finish')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -395,7 +395,7 @@ describe('LSP', function()
return vim.lsp.buf_is_attached(_G.BUFFER, _G.TEST_RPC_CLIENT_ID)
end)
)
- client.stop()
+ client:stop()
end
end,
}
@@ -430,7 +430,7 @@ describe('LSP', function()
return vim.lsp.buf_attach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID)
end)
)
- client.notify('finish')
+ client:notify('finish')
end,
on_handler = function(_, _, ctx)
if ctx.method == 'finish' then
@@ -439,7 +439,7 @@ describe('LSP', function()
return vim.lsp.buf_detach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID)
end)
eq('basic_init', api.nvim_get_var('lsp_detached'))
- client.stop()
+ client:stop()
end
end,
}
@@ -466,13 +466,20 @@ describe('LSP', function()
true,
exec_lua(function()
local keymap --- @type table<string,any>
+ local called = false
+ local origin = vim.lsp.buf.hover
+ vim.lsp.buf.hover = function()
+ called = true
+ end
vim._with({ buf = _G.BUFFER }, function()
keymap = vim.fn.maparg('K', 'n', false, true)
end)
- return keymap.callback == vim.lsp.buf.hover
+ keymap.callback()
+ vim.lsp.buf.hover = origin
+ return called
end)
)
- client.stop()
+ client:stop()
end
end,
on_exit = function(_, _)
@@ -480,13 +487,13 @@ describe('LSP', function()
eq('', get_buf_option('omnifunc'))
eq('', get_buf_option('formatexpr'))
eq(
- '',
+ true,
exec_lua(function()
local keymap --- @type string
vim._with({ buf = _G.BUFFER }, function()
keymap = vim.fn.maparg('K', 'n', false, false)
end)
- return keymap
+ return keymap:match('<Lua %d+: .+/runtime/lua/vim/lsp%.lua:%d+>') ~= nil
end)
)
end,
@@ -524,7 +531,7 @@ describe('LSP', function()
eq('v:lua.vim.lsp.tagfunc', get_buf_option('tagfunc', BUFFER_1))
eq('v:lua.vim.lsp.omnifunc', get_buf_option('omnifunc', BUFFER_2))
eq('v:lua.vim.lsp.formatexpr()', get_buf_option('formatexpr', BUFFER_2))
- client.stop()
+ client:stop()
end
end,
on_exit = function(_, _)
@@ -554,7 +561,7 @@ describe('LSP', function()
eq('tfu', get_buf_option('tagfunc'))
eq('ofu', get_buf_option('omnifunc'))
eq('fex', get_buf_option('formatexpr'))
- client.stop()
+ client:stop()
end
end,
on_exit = function(_, _)
@@ -711,10 +718,10 @@ describe('LSP', function()
ctx.method,
result
)
- client.notify('workspace/configuration', server_result)
+ client:notify('workspace/configuration', server_result)
end
if ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -756,7 +763,7 @@ describe('LSP', function()
test_rpc_server {
test_name = 'basic_check_capabilities',
on_init = function(client)
- client.stop()
+ client:stop()
local full_kind = exec_lua(function()
return require 'vim.lsp.protocol'.TextDocumentSyncKind.Full
end)
@@ -798,7 +805,7 @@ describe('LSP', function()
vim.api.nvim_exec_autocmds('BufWritePost', { buffer = _G.BUFFER, modeline = false })
end)
else
- client.stop()
+ client:stop()
end
end,
}
@@ -898,7 +905,7 @@ describe('LSP', function()
end)
end)
else
- client.stop()
+ client:stop()
end
end,
})
@@ -929,20 +936,20 @@ describe('LSP', function()
vim.api.nvim_exec_autocmds('BufWritePost', { buffer = _G.BUFFER, modeline = false })
end)
else
- client.stop()
+ client:stop()
end
end,
}
end)
- it('client.supports_methods() should validate capabilities', function()
+ it('client:supports_methods() should validate capabilities', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
}
test_rpc_server {
test_name = 'capabilities_for_client_supports_method',
on_init = function(client)
- client.stop()
+ client:stop()
local expected_sync_capabilities = {
change = 1,
openClose = true,
@@ -958,11 +965,11 @@ describe('LSP', function()
eq(true, client.server_capabilities().codeLensProvider.resolveProvider)
-- known methods for resolved capabilities
- eq(true, client.supports_method('textDocument/hover'))
- eq(false, client.supports_method('textDocument/definition'))
+ eq(true, client:supports_method('textDocument/hover'))
+ eq(false, client:supports_method('textDocument/definition'))
-- unknown methods are assumed to be supported.
- eq(true, client.supports_method('unknown-method'))
+ eq(true, client:supports_method('unknown-method'))
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -989,7 +996,7 @@ describe('LSP', function()
end)
end,
on_init = function(client)
- client.stop()
+ client:stop()
exec_lua(function()
vim.lsp.buf.type_definition()
end)
@@ -1018,7 +1025,7 @@ describe('LSP', function()
end)
end,
on_init = function(client)
- client.stop()
+ client:stop()
exec_lua(function()
vim.lsp.buf.type_definition()
end)
@@ -1042,7 +1049,7 @@ describe('LSP', function()
test_rpc_server {
test_name = 'check_forward_request_cancelled',
on_init = function(_client)
- _client.request('error_code_test')
+ _client:request('error_code_test')
client = _client
end,
on_exit = function(code, signal)
@@ -1053,7 +1060,40 @@ describe('LSP', function()
on_handler = function(err, _, ctx)
eq(table.remove(expected_handlers), { err, {}, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
+ end
+ end,
+ }
+ end)
+
+ it('should forward ServerCancelled to callback', function()
+ local expected_handlers = {
+ { NIL, {}, { method = 'finish', client_id = 1 } },
+ {
+ { code = -32802 },
+ NIL,
+ { method = 'error_code_test', bufnr = 1, client_id = 1, version = 0 },
+ },
+ }
+ local client --- @type vim.lsp.Client
+ test_rpc_server {
+ test_name = 'check_forward_server_cancelled',
+ on_init = function(_client)
+ _client:request('error_code_test')
+ client = _client
+ end,
+ on_exit = function(code, signal)
+ eq(0, code, 'exit code')
+ eq(0, signal, 'exit signal')
+ eq(0, #expected_handlers, 'did not call expected handler')
+ end,
+ on_handler = function(err, _, ctx)
+ eq(table.remove(expected_handlers), { err, _, ctx }, 'expected handler')
+ if ctx.method ~= 'finish' then
+ client:notify('finish')
+ end
+ if ctx.method == 'finish' then
+ client:stop()
end
end,
}
@@ -1072,7 +1112,7 @@ describe('LSP', function()
test_rpc_server {
test_name = 'check_forward_content_modified',
on_init = function(_client)
- _client.request('error_code_test')
+ _client:request('error_code_test')
client = _client
end,
on_exit = function(code, signal)
@@ -1082,12 +1122,11 @@ describe('LSP', function()
end,
on_handler = function(err, _, ctx)
eq(table.remove(expected_handlers), { err, _, ctx }, 'expected handler')
- -- if ctx.method == 'error_code_test' then client.notify("finish") end
if ctx.method ~= 'finish' then
- client.notify('finish')
+ client:notify('finish')
end
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1103,13 +1142,13 @@ describe('LSP', function()
test_name = 'check_pending_request_tracked',
on_init = function(_client)
client = _client
- client.request('slow_request')
+ client:request('slow_request')
local request = exec_lua(function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq('slow_request', request.method)
eq('pending', request.type)
- client.notify('release')
+ client:notify('release')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1123,10 +1162,10 @@ describe('LSP', function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq(nil, request)
- client.notify('finish')
+ client:notify('finish')
end
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1141,14 +1180,14 @@ describe('LSP', function()
test_name = 'check_cancel_request_tracked',
on_init = function(_client)
client = _client
- client.request('slow_request')
- client.cancel_request(2)
+ client:request('slow_request')
+ client:cancel_request(2)
local request = exec_lua(function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq('slow_request', request.method)
eq('cancel', request.type)
- client.notify('release')
+ client:notify('release')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1162,7 +1201,7 @@ describe('LSP', function()
end)
eq(nil, request)
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1178,19 +1217,19 @@ describe('LSP', function()
test_name = 'check_tracked_requests_cleared',
on_init = function(_client)
client = _client
- client.request('slow_request')
+ client:request('slow_request')
local request = exec_lua(function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq('slow_request', request.method)
eq('pending', request.type)
- client.cancel_request(2)
+ client:cancel_request(2)
request = exec_lua(function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq('slow_request', request.method)
eq('cancel', request.type)
- client.notify('release')
+ client:notify('release')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1204,10 +1243,10 @@ describe('LSP', function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq(nil, request)
- client.notify('finish')
+ client:notify('finish')
end
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1225,11 +1264,11 @@ describe('LSP', function()
command('let g:requests = 0')
command('autocmd LspRequest * let g:requests+=1')
client = _client
- client.request('slow_request')
+ client:request('slow_request')
eq(1, eval('g:requests'))
- client.cancel_request(2)
+ client:cancel_request(2)
eq(2, eval('g:requests'))
- client.notify('release')
+ client:notify('release')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1240,10 +1279,10 @@ describe('LSP', function()
on_handler = function(err, _, ctx)
eq(table.remove(expected_handlers), { err, {}, ctx }, 'expected handler')
if ctx.method == 'slow_request' then
- client.notify('finish')
+ client:notify('finish')
end
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1277,7 +1316,7 @@ describe('LSP', function()
end)
eq(full_kind, client.server_capabilities().textDocumentSync.change)
eq(true, client.server_capabilities().textDocumentSync.openClose)
- client.notify('finish')
+ client:notify('finish')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1286,7 +1325,7 @@ describe('LSP', function()
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1333,11 +1372,11 @@ describe('LSP', function()
end,
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1378,11 +1417,11 @@ describe('LSP', function()
end,
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1428,11 +1467,11 @@ describe('LSP', function()
'boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1479,11 +1518,11 @@ describe('LSP', function()
'boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1529,7 +1568,7 @@ describe('LSP', function()
end,
on_init = function(_client)
client = _client
- eq(true, client.supports_method('textDocument/inlayHint'))
+ eq(true, client:supports_method('textDocument/inlayHint'))
exec_lua(function()
assert(vim.lsp.buf_attach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID))
end)
@@ -1545,11 +1584,11 @@ describe('LSP', function()
end)
end
if ctx.method == 'textDocument/inlayHint' then
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1598,11 +1637,11 @@ describe('LSP', function()
'123boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1652,11 +1691,11 @@ describe('LSP', function()
'123boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1699,11 +1738,11 @@ describe('LSP', function()
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
n.command('normal! 1Go')
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1752,11 +1791,11 @@ describe('LSP', function()
'boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1806,15 +1845,29 @@ describe('LSP', function()
})
vim.api.nvim_command(_G.BUFFER .. 'bwipeout')
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
end)
+
+ it('vim.lsp.start when existing client has no workspace_folders', function()
+ exec_lua(create_server_definition)
+ eq(
+ { 2, 'foo', 'foo' },
+ exec_lua(function()
+ local server = _G._create_server()
+ vim.lsp.start { cmd = server.cmd, name = 'foo' }
+ vim.lsp.start { cmd = server.cmd, name = 'foo', root_dir = 'bar' }
+ local foos = vim.lsp.get_clients()
+ return { #foos, foos[1].name, foos[2].name }
+ end)
+ )
+ end)
end)
describe('parsing tests', function()
@@ -1830,7 +1883,7 @@ describe('LSP', function()
on_setup = function() end,
on_init = function(_client)
client = _client
- client.stop(true)
+ client:stop(true)
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1882,7 +1935,7 @@ describe('LSP', function()
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -2348,7 +2401,7 @@ describe('LSP', function()
test_rpc_server {
test_name = 'basic_init',
on_init = function(client, _)
- client.stop()
+ client:stop()
end,
-- If the program timed out, then code will be nil.
on_exit = function(code, signal)
@@ -3448,6 +3501,19 @@ describe('LSP', function()
end)
)
end)
+ it('handles empty line', function()
+ exec_lua(function()
+ _G.contents = {
+ '',
+ }
+ end)
+ eq(
+ { 20, 1 },
+ exec_lua(function()
+ return { vim.lsp.util._make_floating_popup_size(_G.contents, { width = 20 }) }
+ end)
+ )
+ end)
end)
describe('lsp.util.trim.trim_empty_lines', function()
@@ -3499,7 +3565,7 @@ describe('LSP', function()
}
return vim.lsp.util.convert_signature_help_to_markdown_lines(signature_help, 'zig', { '(' })
end)
- -- Note that although the higlight positions below are 0-indexed, the 2nd parameter
+ -- Note that although the highlight positions below are 0-indexed, the 2nd parameter
-- corresponds to the 3rd line because the first line is the ``` from the
-- Markdown block.
local expected = { 3, 4, 3, 11 }
@@ -4284,7 +4350,7 @@ describe('LSP', function()
end)
)
end
- client.stop()
+ client:stop()
end
end,
}
@@ -4331,7 +4397,7 @@ describe('LSP', function()
return type(vim.lsp.commands['dummy2'])
end)
)
- client.stop()
+ client:stop()
end
end,
}
@@ -4372,7 +4438,7 @@ describe('LSP', function()
vim.lsp.buf.code_action()
end)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
})
@@ -4447,7 +4513,7 @@ describe('LSP', function()
return type(vim.lsp.commands['executed_type_annotate'])
end)
)
- client.stop()
+ client:stop()
end
end,
}
@@ -4564,7 +4630,7 @@ describe('LSP', function()
end)
eq({ command = 'Dummy', title = 'Lens1' }, cmd)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4653,7 +4719,7 @@ describe('LSP', function()
end)
eq({ command = 'Dummy', title = 'Lens2' }, response)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4762,7 +4828,7 @@ describe('LSP', function()
return notify_msg
end)
eq('[LSP] Format request failed, no matching language servers.', notify_msg)
- client.stop()
+ client:stop()
end,
}
end)
@@ -4795,7 +4861,7 @@ describe('LSP', function()
end)
eq(nil, notify_msg)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4836,7 +4902,7 @@ describe('LSP', function()
end)
eq(nil, notify_msg)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4883,7 +4949,7 @@ describe('LSP', function()
end)
eq(nil, notify_msg)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4930,7 +4996,7 @@ describe('LSP', function()
end)
eq({ handler_called = true }, result)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -5145,8 +5211,8 @@ describe('LSP', function()
local win = vim.api.nvim_get_current_win()
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { 'local x = 10', '', 'print(x)' })
vim.api.nvim_win_set_cursor(win, { 3, 6 })
- local client_id1 = assert(vim.lsp.start({ name = 'dummy', cmd = server1.cmd }))
- local client_id2 = assert(vim.lsp.start({ name = 'dummy', cmd = server2.cmd }))
+ local client_id1 = assert(vim.lsp.start({ name = 'dummy1', cmd = server1.cmd }))
+ local client_id2 = assert(vim.lsp.start({ name = 'dummy2', cmd = server2.cmd }))
local response
vim.lsp.buf.definition({
on_list = function(r)
@@ -5477,7 +5543,7 @@ describe('LSP', function()
result[#result + 1] = {
method = method,
fname = fname,
- supported = client.supports_method(method, { bufnr = bufnr }),
+ supported = client:supports_method(method, { bufnr = bufnr }),
}
end
@@ -6059,15 +6125,6 @@ describe('LSP', function()
end
eq(is_os('mac') or is_os('win'), check_registered(nil)) -- start{_client}() defaults to make_client_capabilities().
- eq(false, check_registered(vim.empty_dict()))
- eq(
- false,
- check_registered({
- workspace = {
- ignoreMe = true,
- },
- })
- )
eq(
false,
check_registered({
@@ -6090,4 +6147,154 @@ describe('LSP', function()
)
end)
end)
+
+ describe('vim.lsp.config() and vim.lsp.enable()', function()
+ it('can merge settings from "*"', function()
+ eq(
+ {
+ name = 'foo',
+ cmd = { 'foo' },
+ root_markers = { '.git' },
+ },
+ exec_lua(function()
+ vim.lsp.config('*', { root_markers = { '.git' } })
+ vim.lsp.config('foo', { cmd = { 'foo' } })
+
+ return vim.lsp.config['foo']
+ end)
+ )
+ end)
+
+ it('sets up an autocmd', function()
+ eq(
+ 1,
+ exec_lua(function()
+ vim.lsp.config('foo', {
+ cmd = { 'foo' },
+ root_markers = { '.foorc' },
+ })
+ vim.lsp.enable('foo')
+ return #vim.api.nvim_get_autocmds({
+ group = 'nvim.lsp.enable',
+ event = 'FileType',
+ })
+ end)
+ )
+ end)
+
+ it('attaches to buffers', function()
+ exec_lua(create_server_definition)
+
+ local tmp1 = t.tmpname(true)
+ local tmp2 = t.tmpname(true)
+
+ exec_lua(function()
+ local server = _G._create_server({
+ handlers = {
+ initialize = function(_, _, callback)
+ callback(nil, { capabilities = {} })
+ end,
+ },
+ })
+
+ vim.lsp.config('foo', {
+ cmd = server.cmd,
+ filetypes = { 'foo' },
+ root_markers = { '.foorc' },
+ })
+
+ vim.lsp.config('bar', {
+ cmd = server.cmd,
+ filetypes = { 'bar' },
+ root_markers = { '.foorc' },
+ })
+
+ vim.lsp.enable('foo')
+ vim.lsp.enable('bar')
+
+ vim.cmd.edit(tmp1)
+ vim.bo.filetype = 'foo'
+ _G.foo_buf = vim.api.nvim_get_current_buf()
+
+ vim.cmd.edit(tmp2)
+ vim.bo.filetype = 'bar'
+ _G.bar_buf = vim.api.nvim_get_current_buf()
+ end)
+
+ eq(
+ { 1, 'foo', 1, 'bar' },
+ exec_lua(function()
+ local foos = vim.lsp.get_clients({ bufnr = assert(_G.foo_buf) })
+ local bars = vim.lsp.get_clients({ bufnr = assert(_G.bar_buf) })
+ return { #foos, foos[1].name, #bars, bars[1].name }
+ end)
+ )
+ end)
+
+ it('does not attach to buffers more than once if no root_dir', function()
+ exec_lua(create_server_definition)
+
+ local tmp1 = t.tmpname(true)
+
+ eq(
+ 1,
+ exec_lua(function()
+ local server = _G._create_server({
+ handlers = {
+ initialize = function(_, _, callback)
+ callback(nil, { capabilities = {} })
+ end,
+ },
+ })
+
+ vim.lsp.config('foo', { cmd = server.cmd, filetypes = { 'foo' } })
+ vim.lsp.enable('foo')
+
+ vim.cmd.edit(assert(tmp1))
+ vim.bo.filetype = 'foo'
+ vim.bo.filetype = 'foo'
+
+ return #vim.lsp.get_clients({ bufnr = vim.api.nvim_get_current_buf() })
+ end)
+ )
+ end)
+
+ it('supports async function for root_dir', function()
+ exec_lua(create_server_definition)
+
+ local tmp1 = t.tmpname(true)
+ exec_lua(function()
+ local server = _G._create_server({
+ handlers = {
+ initialize = function(_, _, callback)
+ callback(nil, { capabilities = {} })
+ end,
+ },
+ })
+
+ vim.lsp.config('foo', {
+ cmd = server.cmd,
+ filetypes = { 'foo' },
+ root_dir = function(cb)
+ vim.system({ 'sleep', '0' }, {}, function()
+ cb('some_dir')
+ end)
+ end,
+ })
+ vim.lsp.enable('foo')
+
+ vim.cmd.edit(assert(tmp1))
+ vim.bo.filetype = 'foo'
+ end)
+
+ retry(nil, 1000, function()
+ eq(
+ 'some_dir',
+ exec_lua(function()
+ return vim.lsp.get_clients({ bufnr = vim.api.nvim_get_current_buf() })[1].root_dir
+ end)
+ )
+ end)
+ end)
+ end)
end)
diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua
index 8906e60dce..c1dbc6dac3 100644
--- a/test/functional/plugin/man_spec.lua
+++ b/test/functional/plugin/man_spec.lua
@@ -21,13 +21,12 @@ local function get_search_history(name)
local man = require('man')
local res = {}
--- @diagnostic disable-next-line:duplicate-set-field
- man.find_path = function(sect, name0)
+ man._find_path = function(name0, sect)
table.insert(res, { sect, name0 })
return nil
end
- local ok, rv = pcall(man.open_page, -1, { tab = 0 }, args)
- assert(not ok)
- assert(rv and rv:match('no manual entry'))
+ local err = man.open_page(-1, { tab = 0 }, args)
+ assert(err and err:match('no manual entry'))
return res
end)
end
@@ -225,7 +224,7 @@ describe(':Man', function()
matches('^/.+', actual_file)
local args = { nvim_prog, '--headless', '+:Man ' .. actual_file, '+q' }
matches(
- ('Error detected while processing command line:\r\n' .. 'man.lua: "no manual entry for %s"'):format(
+ ('Error detected while processing command line:\r\n' .. 'man.lua: no manual entry for %s'):format(
pesc(actual_file)
),
fn.system(args, { '' })
@@ -235,8 +234,8 @@ describe(':Man', function()
it('tries variants with spaces, underscores #22503', function()
eq({
- { '', 'NAME WITH SPACES' },
- { '', 'NAME_WITH_SPACES' },
+ { vim.NIL, 'NAME WITH SPACES' },
+ { vim.NIL, 'NAME_WITH_SPACES' },
}, get_search_history('NAME WITH SPACES'))
eq({
{ '3', 'some other man' },
@@ -255,12 +254,21 @@ describe(':Man', function()
{ 'n', 'some_other_man' },
}, get_search_history('n some other man'))
eq({
- { '', '123some other man' },
- { '', '123some_other_man' },
+ { vim.NIL, '123some other man' },
+ { vim.NIL, '123some_other_man' },
}, get_search_history('123some other man'))
eq({
{ '1', 'other_man' },
{ '1', 'other_man' },
}, get_search_history('other_man(1)'))
end)
+
+ it('can complete', function()
+ eq(
+ true,
+ exec_lua(function()
+ return #require('man').man_complete('f', 'Man f') > 0
+ end)
+ )
+ end)
end)