aboutsummaryrefslogtreecommitdiff
path: root/test/functional/plugin/lsp
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
committerJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
commitd5f194ce780c95821a855aca3c19426576d28ae0 (patch)
treed45f461b19f9118ad2bb1f440a7a08973ad18832 /test/functional/plugin/lsp
parentc5d770d311841ea5230426cc4c868e8db27300a8 (diff)
parent44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff)
downloadrneovim-rahm.tar.gz
rneovim-rahm.tar.bz2
rneovim-rahm.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309HEADrahm
Diffstat (limited to 'test/functional/plugin/lsp')
-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
8 files changed, 827 insertions, 64 deletions
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)