Skip to content

Commit

Permalink
feat(lsp): support completion itemDefaults
Browse files Browse the repository at this point in the history
  • Loading branch information
MariaSolOs authored and clason committed Feb 27, 2024
1 parent 3d96e3f commit 63f9c2d
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 1 deletion.
14 changes: 14 additions & 0 deletions runtime/doc/news.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ The following changes may require adaptations in user config or plugins.
• Returning any truthy value from a callback passed to |nvim_create_autocmd()|
(rather than just `true`) will delete the autocommand.

|vim.lsp.util.extract_completion_items()| will no longer return reliable
results, since it does not apply `itemDefaults` when its input is a
`CompletionList`.
Moreover, since support for LSP `completionList.itemDefaults` was added,
some third party plugins might be negatively impacted in case the language
servers support the feature but the plugin does not.
If necessary, the respective capability can be
removed when calling |vim.lsp.protocol.make_client_capabilities()|.

==============================================================================
BREAKING CHANGES IN HEAD *news-breaking-dev*

Expand Down Expand Up @@ -213,6 +222,11 @@ The following new APIs and features were added.
the original LSP `Location` or `LocationLink`.
• Added support for connecting to servers using named pipes (Windows) or
unix domain sockets (Unix) via |vim.lsp.rpc.domain_socket_connect()|.
• Added support for `completionList.itemDefaults`, reducing overhead when
computing completion items where properties often share the same value
(e.g. `commitCharacters`). Note that this might affect plugins and
language servers that don't support the feature, and in such cases the
respective capability can be unset.

• Treesitter
• Bundled parsers and queries (highlight, folds) for Markdown, Python, and
Expand Down
40 changes: 39 additions & 1 deletion runtime/lua/vim/lsp/_completion.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ local ms = protocol.Methods

--- @alias vim.lsp.CompletionResult lsp.CompletionList | lsp.CompletionItem[]

-- TODO(mariasolos): Remove this declaration once we figure out a better way to handle
-- literal/anonymous types (see https://github.com/neovim/neovim/pull/27542/files#r1495259331).
--- @class lsp.ItemDefaults
--- @field editRange lsp.Range | { insert: lsp.Range, replace: lsp.Range } | nil
--- @field insertTextFormat lsp.InsertTextFormat?
--- @field insertTextMode lsp.InsertTextMode?
--- @field data any

---@param input string unparsed snippet
---@return string parsed snippet
local function parse_snippet(input)
Expand Down Expand Up @@ -39,13 +47,43 @@ local function get_completion_word(item)
return item.label
end

--- Applies the given defaults to the completion item, modifying it in place.
---
--- @param item lsp.CompletionItem
--- @param defaults lsp.ItemDefaults?
local function apply_defaults(item, defaults)
if not defaults then
return
end

item.insertTextFormat = item.insertTextFormat or defaults.insertTextFormat
item.insertTextMode = item.insertTextMode or defaults.insertTextMode
item.data = item.data or defaults.data
if defaults.editRange then
local textEdit = item.textEdit or {}
item.textEdit = textEdit
textEdit.newText = textEdit.newText or item.textEditText or item.insertText
if defaults.editRange.start then
textEdit.range = textEdit.range or defaults.editRange
elseif defaults.editRange.insert then
textEdit.insert = defaults.editRange.insert
textEdit.replace = defaults.editRange.replace
end
end
end

---@param result vim.lsp.CompletionResult
---@return lsp.CompletionItem[]
local function get_items(result)
if result.items then
for _, item in ipairs(result.items) do
---@diagnostic disable-next-line: param-type-mismatch
apply_defaults(item, result.itemDefaults)
end
return result.items
else
return result
end
return result
end

--- Turns the result of a `textDocument/completion` request into vim-compatible
Expand Down
8 changes: 8 additions & 0 deletions runtime/lua/vim/lsp/protocol.lua
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,14 @@ function protocol.make_client_capabilities()
return res
end)(),
},
completionList = {
itemDefaults = {
'editRange',
'insertTextFormat',
'insertTextMode',
'data',
},
},
-- TODO(tjdevries): Implement this
contextSupport = false,
Expand Down
4 changes: 4 additions & 0 deletions runtime/lua/vim/lsp/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@ end
--- Can be used to extract the completion items from a
--- `textDocument/completion` request, which may return one of
--- `CompletionItem[]`, `CompletionList` or null.
---
--- Note that this method doesn't apply `itemDefaults` to `CompletionList`s, and hence the returned
--- results might be incorrect.
---
---@deprecated
---@param result table The result of a `textDocument/completion` request
---@return lsp.CompletionItem[] List of completion items
Expand Down
28 changes: 28 additions & 0 deletions test/functional/plugin/lsp/completion_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,32 @@ describe('vim.lsp._completion', function()
item.user_data = nil
eq(expected, item)
end)

it('uses defaults from itemDefaults', function()
--- @type lsp.CompletionList
local completion_list = {
isIncomplete = false,
itemDefaults = {
editRange = {
start = { line = 1, character = 1 },
['end'] = { line = 1, character = 4 },
},
insertTextFormat = 2,
data = 'foobar',
},
items = {
{
label = 'hello',
data = 'item-property-has-priority',
textEditText = 'hello',
},
},
}
local result = complete('|', completion_list)
eq(1, #result.items)
local item = result.items[1].user_data.nvim.lsp.completion_item --- @type lsp.CompletionItem
eq(2, item.insertTextFormat)
eq('item-property-has-priority', item.data)
eq({ line = 1, character = 1 }, item.textEdit.range.start)
end)
end)

0 comments on commit 63f9c2d

Please sign in to comment.