diff options
Diffstat (limited to 'test/functional/plugin/lsp')
-rw-r--r-- | test/functional/plugin/lsp/completion_spec.lua | 54 | ||||
-rw-r--r-- | test/functional/plugin/lsp/diagnostic_spec.lua | 61 | ||||
-rw-r--r-- | test/functional/plugin/lsp/folding_range_spec.lua | 647 | ||||
-rw-r--r-- | test/functional/plugin/lsp/handler_spec.lua | 42 | ||||
-rw-r--r-- | test/functional/plugin/lsp/incremental_sync_spec.lua | 10 | ||||
-rw-r--r-- | test/functional/plugin/lsp/semantic_tokens_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/plugin/lsp/testutil.lua | 13 | ||||
-rw-r--r-- | test/functional/plugin/lsp/utils_spec.lua | 62 |
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) |