diff options
author | Mathias Fußenegger <mfussenegger@users.noreply.github.com> | 2023-07-19 07:10:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-19 07:10:11 +0200 |
commit | 2f22ed6a00db10c4852a8fa232b8782f8b6a6646 (patch) | |
tree | 6cf9ce9275c21c7baee3000c0381f591c6001262 /runtime/lua/vim/lsp.lua | |
parent | ab5cdbd167353a0c6a0ef0b864d78af13029339c (diff) | |
download | rneovim-2f22ed6a00db10c4852a8fa232b8782f8b6a6646.tar.gz rneovim-2f22ed6a00db10c4852a8fa232b8782f8b6a6646.tar.bz2 rneovim-2f22ed6a00db10c4852a8fa232b8782f8b6a6646.zip |
feat(lsp): handle multiple clients in omnifunc (#24381)
Also fixes https://github.com/neovim/neovim/issues/24369 by adding an
extra `vim.schedule` to ensure the `vim.fn.complete` call happens
outside of a luv callback
Diffstat (limited to 'runtime/lua/vim/lsp.lua')
-rw-r--r-- | runtime/lua/vim/lsp.lua | 88 |
1 files changed, 54 insertions, 34 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 35e4bc9dd8..65cce6af47 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2268,8 +2268,9 @@ function lsp.omnifunc(findstart, base) end local bufnr = resolve_bufnr() - local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {}) - if not has_buffer_clients then + local clients = lsp.get_clients({ bufnr = bufnr, method = 'textDocument/completion' }) + local remaining = #clients + if remaining == 0 then return findstart == 1 and -1 or {} end @@ -2278,47 +2279,66 @@ function lsp.omnifunc(findstart, base) log.info('base ', base) end - local pos = api.nvim_win_get_cursor(0) + local win = api.nvim_get_current_win() + local pos = api.nvim_win_get_cursor(win) local line = api.nvim_get_current_line() local line_to_cursor = line:sub(1, pos[2]) local _ = log.trace() and log.trace('omnifunc.line', pos, line) -- Get the start position of the current keyword - local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local match_pos = vim.fn.match(line_to_cursor, '\\k*$') + 1 + local items = {} - local params = util.make_position_params() + local startbyte - local items = {} - lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result, ctx) - if err or not result or vim.fn.mode() ~= 'i' then - return + ---@private + local function on_done() + local mode = api.nvim_get_mode()['mode'] + if mode == 'i' or mode == 'ic' then + vim.fn.complete(startbyte or match_pos, items) end + end - -- Completion response items may be relative to a position different than `textMatch`. - -- Concrete example, with sumneko/lua-language-server: - -- - -- require('plenary.asy| - -- ▲ ▲ ▲ - -- │ │ └── cursor_pos: 20 - -- │ └────── textMatch: 17 - -- └────────────── textEdit.range.start.character: 9 - -- .newText = 'plenary.async' - -- ^^^ - -- prefix (We'd remove everything not starting with `asy`, - -- so we'd eliminate the `plenary.async` result - -- - -- `adjust_start_col` is used to prefer the language server boundary. - -- - local client = lsp.get_client_by_id(ctx.client_id) - local encoding = client and client.offset_encoding or 'utf-16' - local candidates = util.extract_completion_items(result) - local startbyte = adjust_start_col(pos[1], line, candidates, encoding) or textMatch - local prefix = line:sub(startbyte + 1, pos[2]) - local matches = util.text_document_completion_list_to_complete_items(result, prefix) - -- TODO(ashkan): is this the best way to do this? - vim.list_extend(items, matches) - vim.fn.complete(startbyte + 1, items) - end) + for _, client in ipairs(clients) do + local params = util.make_position_params(win, client.offset_encoding) + client.request('textDocument/completion', params, function(err, result) + if err then + log.warn(err.message) + end + if result and vim.fn.mode() == 'i' then + -- Completion response items may be relative to a position different than `textMatch`. + -- Concrete example, with sumneko/lua-language-server: + -- + -- require('plenary.asy| + -- ▲ ▲ ▲ + -- │ │ └── cursor_pos: 20 + -- │ └────── textMatch: 17 + -- └────────────── textEdit.range.start.character: 9 + -- .newText = 'plenary.async' + -- ^^^ + -- prefix (We'd remove everything not starting with `asy`, + -- so we'd eliminate the `plenary.async` result + -- + -- `adjust_start_col` is used to prefer the language server boundary. + -- + local encoding = client.offset_encoding + local candidates = util.extract_completion_items(result) + local curstartbyte = adjust_start_col(pos[1], line, candidates, encoding) + if startbyte == nil then + startbyte = curstartbyte + elseif curstartbyte ~= nil and curstartbyte ~= startbyte then + startbyte = match_pos + end + local prefix = startbyte and line:sub(startbyte + 1) or line_to_cursor:sub(match_pos) + local matches = util.text_document_completion_list_to_complete_items(result, prefix) + vim.list_extend(items, matches) + end + remaining = remaining - 1 + if remaining == 0 then + vim.schedule(on_done) + end + end, bufnr) + end -- Return -2 to signal that we should continue completion so that we can -- async complete. |