aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp.lua
diff options
context:
space:
mode:
authorMathias Fußenegger <mfussenegger@users.noreply.github.com>2021-10-08 17:47:59 +0200
committerGitHub <noreply@github.com>2021-10-08 08:47:59 -0700
commite9d6f7ca6c7739960e8f571f7e5dbe5e5c5ffc0a (patch)
tree2dc12dc477e0f46bc5135a29445d126cc343eafc /runtime/lua/vim/lsp.lua
parent93d33ed02eddd38771bfc1ab0d69e63a9491ace4 (diff)
downloadrneovim-e9d6f7ca6c7739960e8f571f7e5dbe5e5c5ffc0a.tar.gz
rneovim-e9d6f7ca6c7739960e8f571f7e5dbe5e5c5ffc0a.tar.bz2
rneovim-e9d6f7ca6c7739960e8f571f7e5dbe5e5c5ffc0a.zip
feat(lsp): utilize textEdit.range for startbyte in omnifunc (#15957)
Closes https://github.com/neovim/neovim/issues/15784
Diffstat (limited to 'runtime/lua/vim/lsp.lua')
-rw-r--r--runtime/lua/vim/lsp.lua49
1 files changed, 46 insertions, 3 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index c7a88a0993..ac8657dbd7 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1383,6 +1383,29 @@ function lsp.buf_notify(bufnr, method, params)
return resp
end
+
+---@private
+local function adjust_start_col(lnum, line, items, encoding)
+ local min_start_char = nil
+ for _, item in pairs(items) do
+ if item.textEdit and item.textEdit.range.start.line == lnum - 1 then
+ if min_start_char and min_start_char ~= item.textEdit.range.start.character then
+ return nil
+ end
+ min_start_char = item.textEdit.range.start.character
+ end
+ end
+ if min_start_char then
+ if encoding == 'utf-8' then
+ return min_start_char
+ else
+ return vim.str_byteindex(line, min_start_char, encoding == 'utf-16')
+ end
+ else
+ return nil
+ end
+end
+
--- Implements 'omnifunc' compatible LSP completion.
---
---@see |complete-functions|
@@ -1418,17 +1441,37 @@ function lsp.omnifunc(findstart, base)
-- Get the start position of the current keyword
local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
- local prefix = line_to_cursor:sub(textMatch+1)
local params = util.make_position_params()
local items = {}
- lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result)
+ lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result, ctx)
if err or not result or vim.fn.mode() ~= "i" then return 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(textMatch+1, items)
+ vim.fn.complete(startbyte + 1, items)
end)
-- Return -2 to signal that we should continue completion so that we can