mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07:00
feat(lsp)!: vim.lsp.inlay_hint.get(), enable(), is_enabled() #25512
refactor!: `vim.lsp.inlay_hint()` -> `vim.lsp.inlay_hint.enable()` Problem: The LSP specification allows inlay hints to include tooltips, clickable label parts, and code actions; but Neovim provides no API to query for these. Solution: Add minimal viable extension point from which plugins can query for inlay hints in a range, in order to build functionality on top of. Possible Next Steps --- - Add `virt_text_idx` field to `vim.fn.getmousepos()` return value, for usage in mappings of `<LeftMouse>`, `<C-LeftMouse>`, etc
This commit is contained in:
parent
ad3568a701
commit
448907f65d
@ -856,13 +856,6 @@ get_log_path() *vim.lsp.get_log_path()*
|
|||||||
Return: ~
|
Return: ~
|
||||||
(string) path to log file
|
(string) path to log file
|
||||||
|
|
||||||
inlay_hint({bufnr}, {enable}) *vim.lsp.inlay_hint()*
|
|
||||||
Enable/disable/toggle inlay hints for a buffer
|
|
||||||
|
|
||||||
Parameters: ~
|
|
||||||
• {bufnr} (integer) Buffer handle, or 0 for current
|
|
||||||
• {enable} (boolean|nil) true/false to enable/disable, nil to toggle
|
|
||||||
|
|
||||||
omnifunc({findstart}, {base}) *vim.lsp.omnifunc()*
|
omnifunc({findstart}, {base}) *vim.lsp.omnifunc()*
|
||||||
Implements 'omnifunc' compatible LSP completion.
|
Implements 'omnifunc' compatible LSP completion.
|
||||||
|
|
||||||
@ -1469,6 +1462,53 @@ save({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.save()*
|
|||||||
• {client_id} (integer)
|
• {client_id} (integer)
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
Lua module: vim.lsp.inlay_hint *lsp-inlay_hint*
|
||||||
|
|
||||||
|
enable({bufnr}, {enable}) *vim.lsp.inlay_hint.enable()*
|
||||||
|
Enable/disable/toggle inlay hints for a buffer
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {bufnr} (integer|nil) Buffer handle, or 0 or nil for current
|
||||||
|
• {enable} (boolean|nil) true/nil to enable, false to disable
|
||||||
|
|
||||||
|
get({filter}) *vim.lsp.inlay_hint.get()*
|
||||||
|
Get the list of inlay hints, (optionally) restricted by buffer, client, or
|
||||||
|
range.
|
||||||
|
|
||||||
|
Example usage: >lua
|
||||||
|
local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer
|
||||||
|
|
||||||
|
local client = vim.lsp.get_client_by_id(hint.client_id)
|
||||||
|
resolved_hint = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0).result
|
||||||
|
vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding)
|
||||||
|
|
||||||
|
location = resolved_hint.label[1].location
|
||||||
|
client.request("textDocument/hover", {
|
||||||
|
textDocument = { uri = location.uri },
|
||||||
|
position = location.range.start,
|
||||||
|
})
|
||||||
|
<
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {filter} vim.lsp.inlay_hint.get.filter ? Optional filters |kwargs|:
|
||||||
|
• bufnr (integer?): 0 for current buffer
|
||||||
|
• range (lsp.Range?)
|
||||||
|
|
||||||
|
Return: ~
|
||||||
|
vim.lsp.inlay_hint.get.ret [] Each list item is a table with the following fields:
|
||||||
|
• bufnr (integer)
|
||||||
|
• client_id (integer)
|
||||||
|
• inlay_hint (lsp.InlayHint)
|
||||||
|
|
||||||
|
is_enabled({bufnr}) *vim.lsp.inlay_hint.is_enabled()*
|
||||||
|
Parameters: ~
|
||||||
|
• {bufnr} (integer|nil) Buffer handle, or 0 or nil for current
|
||||||
|
|
||||||
|
Return: ~
|
||||||
|
(boolean)
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
Lua module: vim.lsp.semantic_tokens *lsp-semantic_tokens*
|
Lua module: vim.lsp.semantic_tokens *lsp-semantic_tokens*
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ The following new APIs and features were added.
|
|||||||
|
|
||||||
• LSP
|
• LSP
|
||||||
• LSP method names are available in |vim.lsp.protocol.Methods|.
|
• LSP method names are available in |vim.lsp.protocol.Methods|.
|
||||||
• Implemented LSP inlay hints: |vim.lsp.inlay_hint()|
|
• Implemented LSP inlay hints: |lsp-inlay_hint|
|
||||||
https://microsoft.github.io/language-server-protocol/specification/#textDocument_inlayHint
|
https://microsoft.github.io/language-server-protocol/specification/#textDocument_inlayHint
|
||||||
• Implemented pull diagnostic textDocument/diagnostic: |vim.lsp.diagnostic.on_diagnostic()|
|
• Implemented pull diagnostic textDocument/diagnostic: |vim.lsp.diagnostic.on_diagnostic()|
|
||||||
https://microsoft.github.io/language-server-protocol/specification/#textDocument_diagnostic
|
https://microsoft.github.io/language-server-protocol/specification/#textDocument_diagnostic
|
||||||
|
@ -24,6 +24,7 @@ local lsp = {
|
|||||||
buf = require('vim.lsp.buf'),
|
buf = require('vim.lsp.buf'),
|
||||||
diagnostic = require('vim.lsp.diagnostic'),
|
diagnostic = require('vim.lsp.diagnostic'),
|
||||||
codelens = require('vim.lsp.codelens'),
|
codelens = require('vim.lsp.codelens'),
|
||||||
|
inlay_hint = require('vim.lsp.inlay_hint'),
|
||||||
semantic_tokens = semantic_tokens,
|
semantic_tokens = semantic_tokens,
|
||||||
util = util,
|
util = util,
|
||||||
|
|
||||||
@ -2439,13 +2440,6 @@ function lsp.with(handler, override_config)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Enable/disable/toggle inlay hints for a buffer
|
|
||||||
---@param bufnr (integer) Buffer handle, or 0 for current
|
|
||||||
---@param enable (boolean|nil) true/false to enable/disable, nil to toggle
|
|
||||||
function lsp.inlay_hint(bufnr, enable)
|
|
||||||
return require('vim.lsp.inlay_hint')(bufnr, enable)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Helper function to use when implementing a handler.
|
--- Helper function to use when implementing a handler.
|
||||||
--- This will check that all of the keys in the user configuration
|
--- This will check that all of the keys in the user configuration
|
||||||
--- are valid keys and make sense to include for this handler.
|
--- are valid keys and make sense to include for this handler.
|
||||||
|
@ -98,6 +98,107 @@ function M.on_refresh(err, _, ctx, _)
|
|||||||
return vim.NIL
|
return vim.NIL
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @class vim.lsp.inlay_hint.get.filter
|
||||||
|
--- @field bufnr integer?
|
||||||
|
--- @field range lsp.Range?
|
||||||
|
---
|
||||||
|
--- @class vim.lsp.inlay_hint.get.ret
|
||||||
|
--- @field bufnr integer
|
||||||
|
--- @field client_id integer
|
||||||
|
--- @field inlay_hint lsp.InlayHint
|
||||||
|
|
||||||
|
--- Get the list of inlay hints, (optionally) restricted by buffer, client, or range.
|
||||||
|
---
|
||||||
|
--- Example usage:
|
||||||
|
---
|
||||||
|
--- ```lua
|
||||||
|
--- local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer
|
||||||
|
---
|
||||||
|
--- local client = vim.lsp.get_client_by_id(hint.client_id)
|
||||||
|
--- resolved_hint = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0).result
|
||||||
|
--- vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding)
|
||||||
|
---
|
||||||
|
--- location = resolved_hint.label[1].location
|
||||||
|
--- client.request('textDocument/hover', {
|
||||||
|
--- textDocument = { uri = location.uri },
|
||||||
|
--- position = location.range.start,
|
||||||
|
--- })
|
||||||
|
--- ```
|
||||||
|
---
|
||||||
|
--- @param filter vim.lsp.inlay_hint.get.filter?
|
||||||
|
--- Optional filters |kwargs|:
|
||||||
|
--- - bufnr (integer?): 0 for current buffer
|
||||||
|
--- - range (lsp.Range?)
|
||||||
|
---
|
||||||
|
--- @return vim.lsp.inlay_hint.get.ret[]
|
||||||
|
--- Each list item is a table with the following fields:
|
||||||
|
--- - bufnr (integer)
|
||||||
|
--- - client_id (integer)
|
||||||
|
--- - inlay_hint (lsp.InlayHint)
|
||||||
|
function M.get(filter)
|
||||||
|
vim.validate({ filter = { filter, 'table', true } })
|
||||||
|
filter = filter or {}
|
||||||
|
|
||||||
|
local bufnr = filter.bufnr
|
||||||
|
if not bufnr then
|
||||||
|
--- @type vim.lsp.inlay_hint.get.ret[]
|
||||||
|
local hints = {}
|
||||||
|
--- @param buf integer
|
||||||
|
vim.tbl_map(function(buf)
|
||||||
|
vim.list_extend(hints, M.get(vim.tbl_extend('keep', { bufnr = buf }, filter)))
|
||||||
|
end, vim.api.nvim_list_bufs())
|
||||||
|
return hints
|
||||||
|
elseif bufnr == 0 then
|
||||||
|
bufnr = api.nvim_get_current_buf()
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufstate = bufstates[bufnr]
|
||||||
|
if not (bufstate and bufstate.client_hint) then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local clients = vim.lsp.get_clients({
|
||||||
|
bufnr = bufnr,
|
||||||
|
method = ms.textDocument_inlayHint,
|
||||||
|
})
|
||||||
|
if #clients == 0 then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local range = filter.range
|
||||||
|
if not range then
|
||||||
|
range = {
|
||||||
|
start = { line = 0, character = 0 },
|
||||||
|
['end'] = { line = api.nvim_buf_line_count(bufnr), character = 0 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @type vim.lsp.inlay_hint.get.ret[]
|
||||||
|
local hints = {}
|
||||||
|
for _, client in pairs(clients) do
|
||||||
|
local hints_by_lnum = bufstate.client_hint[client.id]
|
||||||
|
if hints_by_lnum then
|
||||||
|
for lnum = range.start.line, range['end'].line do
|
||||||
|
local line_hints = hints_by_lnum[lnum] or {}
|
||||||
|
for _, hint in pairs(line_hints) do
|
||||||
|
local line, char = hint.position.line, hint.position.character
|
||||||
|
if
|
||||||
|
(line > range.start.line or char >= range.start.character)
|
||||||
|
and (line < range['end'].line or char <= range['end'].character)
|
||||||
|
then
|
||||||
|
table.insert(hints, {
|
||||||
|
bufnr = bufnr,
|
||||||
|
client_id = client.id,
|
||||||
|
inlay_hint = hint,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return hints
|
||||||
|
end
|
||||||
|
|
||||||
--- Clear inlay hints
|
--- Clear inlay hints
|
||||||
---@param bufnr (integer) Buffer handle, or 0 for current
|
---@param bufnr (integer) Buffer handle, or 0 for current
|
||||||
local function clear(bufnr)
|
local function clear(bufnr)
|
||||||
@ -120,8 +221,8 @@ local function clear(bufnr)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Disable inlay hints for a buffer
|
--- Disable inlay hints for a buffer
|
||||||
---@param bufnr (integer) Buffer handle, or 0 for current
|
---@param bufnr (integer|nil) Buffer handle, or 0 or nil for current
|
||||||
local function disable(bufnr)
|
local function _disable(bufnr)
|
||||||
if bufnr == nil or bufnr == 0 then
|
if bufnr == nil or bufnr == 0 then
|
||||||
bufnr = api.nvim_get_current_buf()
|
bufnr = api.nvim_get_current_buf()
|
||||||
end
|
end
|
||||||
@ -142,8 +243,8 @@ local function _refresh(bufnr, opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Enable inlay hints for a buffer
|
--- Enable inlay hints for a buffer
|
||||||
---@param bufnr (integer) Buffer handle, or 0 for current
|
---@param bufnr (integer|nil) Buffer handle, or 0 or nil for current
|
||||||
local function enable(bufnr)
|
local function _enable(bufnr)
|
||||||
if bufnr == nil or bufnr == 0 then
|
if bufnr == nil or bufnr == 0 then
|
||||||
bufnr = api.nvim_get_current_buf()
|
bufnr = api.nvim_get_current_buf()
|
||||||
end
|
end
|
||||||
@ -175,7 +276,7 @@ local function enable(bufnr)
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_detach = function(_, cb_bufnr)
|
on_detach = function(_, cb_bufnr)
|
||||||
disable(cb_bufnr)
|
_disable(cb_bufnr)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
api.nvim_create_autocmd('LspDetach', {
|
api.nvim_create_autocmd('LspDetach', {
|
||||||
@ -188,7 +289,7 @@ local function enable(bufnr)
|
|||||||
return c.id ~= args.data.client_id
|
return c.id ~= args.data.client_id
|
||||||
end)
|
end)
|
||||||
then
|
then
|
||||||
disable(bufnr)
|
_disable(bufnr)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
group = augroup,
|
group = augroup,
|
||||||
@ -199,20 +300,6 @@ local function enable(bufnr)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Toggle inlay hints for a buffer
|
|
||||||
---@param bufnr (integer) Buffer handle, or 0 for current
|
|
||||||
local function toggle(bufnr)
|
|
||||||
if bufnr == nil or bufnr == 0 then
|
|
||||||
bufnr = api.nvim_get_current_buf()
|
|
||||||
end
|
|
||||||
local bufstate = bufstates[bufnr]
|
|
||||||
if bufstate and bufstate.enabled then
|
|
||||||
disable(bufnr)
|
|
||||||
else
|
|
||||||
enable(bufnr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
api.nvim_set_decoration_provider(namespace, {
|
api.nvim_set_decoration_provider(namespace, {
|
||||||
on_win = function(_, _, bufnr, topline, botline)
|
on_win = function(_, _, bufnr, topline, botline)
|
||||||
local bufstate = bufstates[bufnr]
|
local bufstate = bufstates[bufnr]
|
||||||
@ -260,15 +347,27 @@ api.nvim_set_decoration_provider(namespace, {
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
return setmetatable(M, {
|
--- @param bufnr (integer|nil) Buffer handle, or 0 or nil for current
|
||||||
__call = function(_, bufnr, enable_)
|
--- @return boolean
|
||||||
vim.validate({ enable = { enable_, { 'boolean', 'nil' } }, bufnr = { bufnr, 'number' } })
|
function M.is_enabled(bufnr)
|
||||||
if enable_ then
|
vim.validate({ bufnr = { bufnr, 'number', true } })
|
||||||
enable(bufnr)
|
if bufnr == nil or bufnr == 0 then
|
||||||
elseif enable_ == false then
|
bufnr = api.nvim_get_current_buf()
|
||||||
disable(bufnr)
|
end
|
||||||
else
|
return bufstates[bufnr] and bufstates[bufnr].enabled or false
|
||||||
toggle(bufnr)
|
end
|
||||||
end
|
|
||||||
end,
|
--- Enable/disable/toggle inlay hints for a buffer
|
||||||
})
|
---
|
||||||
|
--- @param bufnr (integer|nil) Buffer handle, or 0 or nil for current
|
||||||
|
--- @param enable (boolean|nil) true/nil to enable, false to disable
|
||||||
|
function M.enable(bufnr, enable)
|
||||||
|
vim.validate({ enable = { enable, 'boolean', true }, bufnr = { bufnr, 'number', true } })
|
||||||
|
if enable == false then
|
||||||
|
_disable(bufnr)
|
||||||
|
else
|
||||||
|
_enable(bufnr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
@ -648,7 +648,12 @@ function protocol.make_client_capabilities()
|
|||||||
inlayHint = {
|
inlayHint = {
|
||||||
dynamicRegistration = true,
|
dynamicRegistration = true,
|
||||||
resolveSupport = {
|
resolveSupport = {
|
||||||
properties = {},
|
properties = {
|
||||||
|
'textEdits',
|
||||||
|
'tooltip',
|
||||||
|
'location',
|
||||||
|
'command',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
semanticTokens = {
|
semanticTokens = {
|
||||||
|
@ -259,6 +259,7 @@ CONFIG = {
|
|||||||
'buf.lua',
|
'buf.lua',
|
||||||
'diagnostic.lua',
|
'diagnostic.lua',
|
||||||
'codelens.lua',
|
'codelens.lua',
|
||||||
|
'inlay_hint.lua',
|
||||||
'tagfunc.lua',
|
'tagfunc.lua',
|
||||||
'semantic_tokens.lua',
|
'semantic_tokens.lua',
|
||||||
'handlers.lua',
|
'handlers.lua',
|
||||||
|
@ -18,8 +18,7 @@ local sleep = global_helpers.sleep
|
|||||||
local tbl_contains = global_helpers.tbl_contains
|
local tbl_contains = global_helpers.tbl_contains
|
||||||
local fail = global_helpers.fail
|
local fail = global_helpers.fail
|
||||||
|
|
||||||
local module = {
|
local module = {}
|
||||||
}
|
|
||||||
|
|
||||||
local start_dir = luv.cwd()
|
local start_dir = luv.cwd()
|
||||||
local runtime_set = 'set runtimepath^=./build/lib/nvim/'
|
local runtime_set = 'set runtimepath^=./build/lib/nvim/'
|
||||||
@ -834,6 +833,8 @@ function module.exec_capture(code)
|
|||||||
return module.meths.exec2(code, { output = true }).output
|
return module.meths.exec2(code, { output = true }).output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @param code string
|
||||||
|
--- @return any
|
||||||
function module.exec_lua(code, ...)
|
function module.exec_lua(code, ...)
|
||||||
return module.meths.exec_lua(code, {...})
|
return module.meths.exec_lua(code, {...})
|
||||||
end
|
end
|
||||||
@ -948,8 +949,10 @@ function module.mkdir_p(path)
|
|||||||
or 'mkdir -p '..path))
|
or 'mkdir -p '..path))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @class test.functional.helpers: test.helpers
|
||||||
module = global_helpers.tbl_extend('error', module, global_helpers)
|
module = global_helpers.tbl_extend('error', module, global_helpers)
|
||||||
|
|
||||||
|
--- @return test.functional.helpers
|
||||||
return function(after_each)
|
return function(after_each)
|
||||||
if after_each then
|
if after_each then
|
||||||
after_each(function()
|
after_each(function()
|
||||||
|
@ -10,228 +10,195 @@ local insert = helpers.insert
|
|||||||
local clear_notrace = lsp_helpers.clear_notrace
|
local clear_notrace = lsp_helpers.clear_notrace
|
||||||
local create_server_definition = lsp_helpers.create_server_definition
|
local create_server_definition = lsp_helpers.create_server_definition
|
||||||
|
|
||||||
|
local text = dedent([[
|
||||||
|
auto add(int a, int b) { return a + b; }
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int x = 1;
|
||||||
|
int y = 2;
|
||||||
|
return add(x,y);
|
||||||
|
}
|
||||||
|
}]])
|
||||||
|
|
||||||
|
local response = [==[
|
||||||
|
[
|
||||||
|
{"kind":1,"paddingLeft":false,"label":"-> int","position":{"character":22,"line":0},"paddingRight":false},
|
||||||
|
{"kind":2,"paddingLeft":false,"label":"a:","position":{"character":15,"line":5},"paddingRight":true},
|
||||||
|
{"kind":2,"paddingLeft":false,"label":"b:","position":{"character":17,"line":5},"paddingRight":true}
|
||||||
|
]
|
||||||
|
]==]
|
||||||
|
|
||||||
|
local grid_without_inlay_hints = [[
|
||||||
|
auto add(int a, int b) { return a + b; } |
|
||||||
|
|
|
||||||
|
int main() { |
|
||||||
|
int x = 1; |
|
||||||
|
int y = 2; |
|
||||||
|
return add(x,y); |
|
||||||
|
} |
|
||||||
|
^} |
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
local grid_with_inlay_hints = [[
|
||||||
|
auto add(int a, int b)-> int { return a + b; } |
|
||||||
|
|
|
||||||
|
int main() { |
|
||||||
|
int x = 1; |
|
||||||
|
int y = 2; |
|
||||||
|
return add(a: x,b: y); |
|
||||||
|
} |
|
||||||
|
^} |
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
--- @type test.functional.ui.screen
|
||||||
|
local screen
|
||||||
before_each(function()
|
before_each(function()
|
||||||
clear_notrace()
|
clear_notrace()
|
||||||
|
screen = Screen.new(50, 9)
|
||||||
|
screen:attach()
|
||||||
|
|
||||||
|
exec_lua(create_server_definition)
|
||||||
|
exec_lua([[
|
||||||
|
local response = ...
|
||||||
|
server = _create_server({
|
||||||
|
capabilities = {
|
||||||
|
inlayHintProvider = true,
|
||||||
|
},
|
||||||
|
handlers = {
|
||||||
|
['textDocument/inlayHint'] = function()
|
||||||
|
return vim.json.decode(response)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
vim.api.nvim_win_set_buf(0, bufnr)
|
||||||
|
|
||||||
|
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
||||||
|
]], response)
|
||||||
|
|
||||||
|
insert(text)
|
||||||
|
exec_lua([[vim.lsp.inlay_hint.enable(bufnr)]])
|
||||||
|
screen:expect({ grid = grid_with_inlay_hints })
|
||||||
end)
|
end)
|
||||||
|
|
||||||
after_each(function()
|
after_each(function()
|
||||||
exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })")
|
exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('inlay hints', function()
|
describe('vim.lsp.inlay_hint', function()
|
||||||
local screen
|
it('clears inlay hints when sole client detaches', function()
|
||||||
before_each(function()
|
exec_lua([[vim.lsp.stop_client(client_id)]])
|
||||||
screen = Screen.new(50, 9)
|
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
|
||||||
screen:attach()
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('general', function()
|
it('does not clear inlay hints when one of several clients detaches', function()
|
||||||
local text = dedent([[
|
exec_lua([[
|
||||||
auto add(int a, int b) { return a + b; }
|
server2 = _create_server({
|
||||||
|
capabilities = {
|
||||||
|
inlayHintProvider = true,
|
||||||
|
},
|
||||||
|
handlers = {
|
||||||
|
['textDocument/inlayHint'] = function()
|
||||||
|
return {}
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd })
|
||||||
|
vim.lsp.inlay_hint.enable(bufnr)
|
||||||
|
]])
|
||||||
|
|
||||||
int main() {
|
exec_lua([[ vim.lsp.stop_client(client2) ]])
|
||||||
int x = 1;
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
||||||
int y = 2;
|
end)
|
||||||
return add(x,y);
|
|
||||||
}
|
|
||||||
}]])
|
|
||||||
|
|
||||||
|
describe('enable()', function()
|
||||||
|
it('clears/applies inlay hints when passed false/true/nil', function()
|
||||||
|
exec_lua([[vim.lsp.inlay_hint.enable(bufnr, false)]])
|
||||||
|
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
|
||||||
|
|
||||||
local response = [==[
|
exec_lua([[vim.lsp.inlay_hint.enable(bufnr, true)]])
|
||||||
[
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
||||||
{"kind":1,"paddingLeft":false,"label":"-> int","position":{"character":22,"line":0},"paddingRight":false},
|
|
||||||
{"kind":2,"paddingLeft":false,"label":"a:","position":{"character":15,"line":5},"paddingRight":true},
|
|
||||||
{"kind":2,"paddingLeft":false,"label":"b:","position":{"character":17,"line":5},"paddingRight":true}
|
|
||||||
]
|
|
||||||
]==]
|
|
||||||
|
|
||||||
|
exec_lua([[vim.lsp.inlay_hint.enable(bufnr, not vim.lsp.inlay_hint.is_enabled(bufnr))]])
|
||||||
|
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
|
||||||
|
|
||||||
|
exec_lua([[vim.lsp.inlay_hint.enable(bufnr)]])
|
||||||
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('get()', function()
|
||||||
|
it('returns filtered inlay hints', function()
|
||||||
|
--- @type lsp.InlayHint[]
|
||||||
|
local expected = vim.json.decode(response)
|
||||||
|
local expected2 = {
|
||||||
|
kind = 1,
|
||||||
|
paddingLeft = false,
|
||||||
|
label = ': int',
|
||||||
|
position = {
|
||||||
|
character = 10,
|
||||||
|
line = 2,
|
||||||
|
},
|
||||||
|
paddingRight = false,
|
||||||
|
}
|
||||||
|
|
||||||
before_each(function()
|
|
||||||
exec_lua(create_server_definition)
|
|
||||||
exec_lua([[
|
exec_lua([[
|
||||||
local response = ...
|
local expected2 = ...
|
||||||
server = _create_server({
|
server2 = _create_server({
|
||||||
capabilities = {
|
capabilities = {
|
||||||
inlayHintProvider = true,
|
inlayHintProvider = true,
|
||||||
},
|
},
|
||||||
handlers = {
|
handlers = {
|
||||||
['textDocument/inlayHint'] = function()
|
['textDocument/inlayHint'] = function()
|
||||||
return vim.json.decode(response)
|
return { expected2 }
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
]], response)
|
client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd })
|
||||||
|
vim.lsp.inlay_hint.enable(bufnr)
|
||||||
|
]], expected2)
|
||||||
|
|
||||||
|
--- @type vim.lsp.inlay_hint.get.ret
|
||||||
|
local res = exec_lua([[return vim.lsp.inlay_hint.get()]])
|
||||||
|
eq(res, {
|
||||||
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[1] },
|
||||||
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[2] },
|
||||||
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[3] },
|
||||||
|
{ bufnr = 1, client_id = 2, inlay_hint = expected2 },
|
||||||
|
})
|
||||||
|
|
||||||
|
--- @type vim.lsp.inlay_hint.get.ret
|
||||||
|
res = exec_lua([[return vim.lsp.inlay_hint.get({
|
||||||
|
range = {
|
||||||
|
start = { line = 2, character = 10 },
|
||||||
|
["end"] = { line = 2, character = 10 },
|
||||||
|
},
|
||||||
|
})]])
|
||||||
|
eq(res, {
|
||||||
|
{ bufnr = 1, client_id = 2, inlay_hint = expected2 },
|
||||||
|
})
|
||||||
|
|
||||||
|
--- @type vim.lsp.inlay_hint.get.ret
|
||||||
|
res = exec_lua([[return vim.lsp.inlay_hint.get({
|
||||||
|
bufnr = vim.api.nvim_get_current_buf(),
|
||||||
|
range = {
|
||||||
|
start = { line = 4, character = 18 },
|
||||||
|
["end"] = { line = 5, character = 17 },
|
||||||
|
},
|
||||||
|
})]])
|
||||||
|
eq(res, {
|
||||||
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[2] },
|
||||||
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[3] },
|
||||||
|
})
|
||||||
|
|
||||||
|
--- @type vim.lsp.inlay_hint.get.ret
|
||||||
|
res = exec_lua([[return vim.lsp.inlay_hint.get({
|
||||||
|
bufnr = vim.api.nvim_get_current_buf() + 1,
|
||||||
|
})]])
|
||||||
|
eq(res, {})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it(
|
|
||||||
'inlay hints are applied when vim.lsp.inlay_hint(true) is called',
|
|
||||||
function()
|
|
||||||
local res = exec_lua([[
|
|
||||||
bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
vim.api.nvim_win_set_buf(0, bufnr)
|
|
||||||
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
|
||||||
return {
|
|
||||||
supports_method = client.supports_method("textDocument/inlayHint")
|
|
||||||
}
|
|
||||||
]])
|
|
||||||
eq(res, { supports_method = true })
|
|
||||||
|
|
||||||
|
|
||||||
insert(text)
|
|
||||||
exec_lua([[vim.lsp.inlay_hint(bufnr, true)]])
|
|
||||||
screen:expect({
|
|
||||||
grid = [[
|
|
||||||
auto add(int a, int b)-> int { return a + b; } |
|
|
||||||
|
|
|
||||||
int main() { |
|
|
||||||
int x = 1; |
|
|
||||||
int y = 2; |
|
|
||||||
return add(a: x,b: y); |
|
|
||||||
} |
|
|
||||||
^} |
|
|
||||||
|
|
|
||||||
]]
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it(
|
|
||||||
'inlay hints are cleared when vim.lsp.inlay_hint(false) is called',
|
|
||||||
function()
|
|
||||||
exec_lua([[
|
|
||||||
bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
vim.api.nvim_win_set_buf(0, bufnr)
|
|
||||||
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
|
||||||
]])
|
|
||||||
|
|
||||||
insert(text)
|
|
||||||
exec_lua([[vim.lsp.inlay_hint(bufnr, true)]])
|
|
||||||
screen:expect({
|
|
||||||
grid = [[
|
|
||||||
auto add(int a, int b)-> int { return a + b; } |
|
|
||||||
|
|
|
||||||
int main() { |
|
|
||||||
int x = 1; |
|
|
||||||
int y = 2; |
|
|
||||||
return add(a: x,b: y); |
|
|
||||||
} |
|
|
||||||
^} |
|
|
||||||
|
|
|
||||||
]]
|
|
||||||
})
|
|
||||||
exec_lua([[vim.lsp.inlay_hint(bufnr, false)]])
|
|
||||||
screen:expect({
|
|
||||||
grid = [[
|
|
||||||
auto add(int a, int b) { return a + b; } |
|
|
||||||
|
|
|
||||||
int main() { |
|
|
||||||
int x = 1; |
|
|
||||||
int y = 2; |
|
|
||||||
return add(x,y); |
|
|
||||||
} |
|
|
||||||
^} |
|
|
||||||
|
|
|
||||||
]],
|
|
||||||
unchanged = true
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it(
|
|
||||||
'inlay hints are cleared when the client detaches',
|
|
||||||
function()
|
|
||||||
exec_lua([[
|
|
||||||
bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
vim.api.nvim_win_set_buf(0, bufnr)
|
|
||||||
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
|
||||||
]])
|
|
||||||
|
|
||||||
insert(text)
|
|
||||||
exec_lua([[vim.lsp.inlay_hint(bufnr, true)]])
|
|
||||||
screen:expect({
|
|
||||||
grid = [[
|
|
||||||
auto add(int a, int b)-> int { return a + b; } |
|
|
||||||
|
|
|
||||||
int main() { |
|
|
||||||
int x = 1; |
|
|
||||||
int y = 2; |
|
|
||||||
return add(a: x,b: y); |
|
|
||||||
} |
|
|
||||||
^} |
|
|
||||||
|
|
|
||||||
]]
|
|
||||||
})
|
|
||||||
exec_lua([[vim.lsp.stop_client(client_id)]])
|
|
||||||
screen:expect({
|
|
||||||
grid = [[
|
|
||||||
auto add(int a, int b) { return a + b; } |
|
|
||||||
|
|
|
||||||
int main() { |
|
|
||||||
int x = 1; |
|
|
||||||
int y = 2; |
|
|
||||||
return add(x,y); |
|
|
||||||
} |
|
|
||||||
^} |
|
|
||||||
|
|
|
||||||
]],
|
|
||||||
unchanged = true
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it(
|
|
||||||
'inlay hints are not cleared when one of several clients detaches',
|
|
||||||
function()
|
|
||||||
-- Start two clients
|
|
||||||
exec_lua([[
|
|
||||||
bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
vim.api.nvim_win_set_buf(0, bufnr)
|
|
||||||
server2 = _create_server({
|
|
||||||
capabilities = {
|
|
||||||
inlayHintProvider = true,
|
|
||||||
},
|
|
||||||
handlers = {
|
|
||||||
['textDocument/inlayHint'] = function()
|
|
||||||
return {}
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
client1 = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
|
||||||
client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd })
|
|
||||||
]])
|
|
||||||
|
|
||||||
insert(text)
|
|
||||||
exec_lua([[vim.lsp.inlay_hint(bufnr, true)]])
|
|
||||||
screen:expect({
|
|
||||||
grid = [[
|
|
||||||
auto add(int a, int b)-> int { return a + b; } |
|
|
||||||
|
|
|
||||||
int main() { |
|
|
||||||
int x = 1; |
|
|
||||||
int y = 2; |
|
|
||||||
return add(a: x,b: y); |
|
|
||||||
} |
|
|
||||||
^} |
|
|
||||||
|
|
|
||||||
]]
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Now stop one client
|
|
||||||
exec_lua([[ vim.lsp.stop_client(client2) ]])
|
|
||||||
|
|
||||||
-- We should still see the hints
|
|
||||||
screen:expect({
|
|
||||||
grid = [[
|
|
||||||
auto add(int a, int b)-> int { return a + b; } |
|
|
||||||
|
|
|
||||||
int main() { |
|
|
||||||
int x = 1; |
|
|
||||||
int y = 2; |
|
|
||||||
return add(a: x,b: y); |
|
|
||||||
} |
|
|
||||||
^} |
|
|
||||||
|
|
|
||||||
]],
|
|
||||||
unchanged = true
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -1291,7 +1291,7 @@ describe('LSP', function()
|
|||||||
on_handler = function(err, result, ctx)
|
on_handler = function(err, result, ctx)
|
||||||
if ctx.method == 'start' then
|
if ctx.method == 'start' then
|
||||||
exec_lua [[
|
exec_lua [[
|
||||||
vim.lsp.inlay_hint(BUFFER, true)
|
vim.lsp.inlay_hint.enable(BUFFER)
|
||||||
]]
|
]]
|
||||||
end
|
end
|
||||||
if ctx.method == 'textDocument/inlayHint' then
|
if ctx.method == 'textDocument/inlayHint' then
|
||||||
|
@ -88,6 +88,7 @@ local function isempty(v)
|
|||||||
return type(v) == 'table' and next(v) == nil
|
return type(v) == 'table' and next(v) == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @class test.functional.ui.screen
|
||||||
local Screen = {}
|
local Screen = {}
|
||||||
Screen.__index = Screen
|
Screen.__index = Screen
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user