mirror of
https://github.com/neovim/neovim.git
synced 2025-11-15 06:49:01 +00:00
fix(lsp): don't overlay insertion-style inline completions (#36477)
* feat(lua): `Range:is_empty()` to check vim.range emptiness * fix(lsp): don't overlay insertion-style inline completions **Problem:** Some servers commonly respond with an empty inline completion range which acts as a position where text should be inserted. However, the inline completion module assumes that all responses with a range are deletions + insertions that thus require an `overlay` display style. This causes an incorrect preview, because the virtual text should have the `inline` display style (to reflect that this is purely an insertion). **Solution:** Only use `overlay` for non-empty replacement ranges.
This commit is contained in:
@@ -4136,6 +4136,7 @@ Provides operations to compare, calculate, and convert ranges represented by
|
||||
Fields: ~
|
||||
• {start} (`vim.Pos`) Start position.
|
||||
• {end_} (`vim.Pos`) End position, exclusive.
|
||||
• {is_empty} (`fun(self: vim.Range): boolean`) See |Range:is_empty()|.
|
||||
• {has} (`fun(outer: vim.Range, inner: vim.Range): boolean`) See
|
||||
|Range:has()|.
|
||||
• {intersect} (`fun(r1: vim.Range, r2: vim.Range): vim.Range?`) See
|
||||
@@ -4167,6 +4168,12 @@ Range:intersect({r1}, {r2}) *Range:intersect()*
|
||||
(`vim.Range?`) range that is present inside both `r1` and `r2`. `nil`
|
||||
if such range does not exist. See |vim.Range|.
|
||||
|
||||
Range:is_empty() *Range:is_empty()*
|
||||
Checks whether the given range is empty; i.e., start >= end.
|
||||
|
||||
Return: ~
|
||||
(`boolean`) `true` if the given range is empty
|
||||
|
||||
Range:lsp({buf}, {range}, {position_encoding}) *Range:lsp()*
|
||||
Creates a new |vim.Range| from `lsp.Range`.
|
||||
|
||||
|
||||
@@ -280,6 +280,7 @@ LUA
|
||||
• Experimental `vim.pos` and `vim.range` for Position/Range abstraction.
|
||||
• |vim.json.encode()| has an `indent` option for pretty-formatting.
|
||||
• |vim.json.encode()| has an `sort_keys` option.
|
||||
• |Range:is_empty()| to check if a |vim.Range| is empty.
|
||||
|
||||
OPTIONS
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ function Completor:show(hint)
|
||||
api.nvim_buf_set_extmark(self.bufnr, namespace, row, col, {
|
||||
virt_text = virt_text,
|
||||
virt_lines = virt_lines,
|
||||
virt_text_pos = current.range and 'overlay' or 'inline',
|
||||
virt_text_pos = (current.range and not current.range:is_empty() and 'overlay') or 'inline',
|
||||
hl_mode = 'combine',
|
||||
})
|
||||
end
|
||||
|
||||
@@ -99,6 +99,13 @@ function Range.__eq(r1, r2)
|
||||
return r1.start == r2.start and r1.end_ == r2.end_
|
||||
end
|
||||
|
||||
--- Checks whether the given range is empty; i.e., start >= end.
|
||||
---
|
||||
---@return boolean `true` if the given range is empty
|
||||
function Range:is_empty()
|
||||
return self.start >= self.end_
|
||||
end
|
||||
|
||||
--- Checks whether {outer} range contains {inner} range.
|
||||
---
|
||||
---@param outer vim.Range
|
||||
|
||||
@@ -83,6 +83,27 @@ describe('vim.lsp.inline_completion', function()
|
||||
},
|
||||
handlers = {
|
||||
['textDocument/inlineCompletion'] = function(_, _, callback)
|
||||
if _G.empty then
|
||||
callback(nil, {
|
||||
items = {
|
||||
{
|
||||
insertText = 'foobar',
|
||||
range = {
|
||||
start = {
|
||||
line = 0,
|
||||
character = 19,
|
||||
},
|
||||
['end'] = {
|
||||
line = 0,
|
||||
character = 19,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
callback(nil, {
|
||||
items = {
|
||||
{
|
||||
@@ -198,6 +219,19 @@ describe('vim.lsp.inline_completion', function()
|
||||
screen:expect({ grid = grid_applied_candidates })
|
||||
end)
|
||||
|
||||
it('correctly displays with absent/empty range', function()
|
||||
exec_lua(function()
|
||||
_G.empty = true
|
||||
end)
|
||||
feed('I')
|
||||
screen:expect([[
|
||||
function fibonacci({1:foobar}) |
|
||||
^ |
|
||||
{1:~ }|*11
|
||||
{3:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('accepts on_accept callback', function()
|
||||
feed('i')
|
||||
screen:expect({ grid = grid_with_candidates })
|
||||
|
||||
Reference in New Issue
Block a user