diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index d199634bae..06fec15f3c 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1611,8 +1611,8 @@ enable({enable}, {filter}) *vim.lsp.inlay_hint.enable()* Parameters: ~ • {enable} (`boolean?`) true/nil to enable, false to disable • {filter} (`table?`) Optional filters |kwargs|, or `nil` for all. - • {bufnr} (`integer?`) Buffer number, or 0/nil for current - buffer. + • {bufnr} (`integer?`) Buffer number, or 0 for current + buffer, or nil for all. get({filter}) *vim.lsp.inlay_hint.get()* Get the list of inlay hints, (optionally) restricted by buffer or range. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index bae0030a14..4d06f3df8d 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -148,6 +148,8 @@ BREAKING CHANGES IN HEAD *news-breaking-dev* The following changes to UNRELEASED features were made during the development cycle (Nvim HEAD, the "master" branch). +• `vim.lsp.inlay_hint.enable()` now take effect on all buffers by default. + • Removed `vim.treesitter.foldtext` as transparent foldtext is now supported https://github.com/neovim/neovim/pull/20750 diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 3d8b54ee3d..985cbef5ff 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -4,13 +4,26 @@ local ms = require('vim.lsp.protocol').Methods local api = vim.api local M = {} ----@class (private) vim.lsp.inlay_hint.bufstate +---@class (private) vim.lsp.inlay_hint.globalstate Global state for inlay hints +---@field enabled boolean Whether inlay hints are enabled for this scope +---@type vim.lsp.inlay_hint.globalstate +local globalstate = { + enabled = false, +} + +---@class (private) vim.lsp.inlay_hint.bufstate: vim.lsp.inlay_hint.globalstate Buffer local state for inlay hints ---@field version? integer ---@field client_hints? table> client_id -> (lnum -> hints) ---@field applied table Last version of hints applied to this line ----@field enabled boolean Whether inlay hints are enabled for this buffer ---@type table -local bufstates = {} +local bufstates = vim.defaulttable(function(_) + return setmetatable({ applied = {} }, { + __index = globalstate, + __newindex = function(state, key, value) + rawset(state, key, (globalstate[key] ~= value) and value or nil) + end, + }) +end) local namespace = api.nvim_create_namespace('vim_lsp_inlayhint') local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {}) @@ -34,7 +47,7 @@ function M.on_inlayhint(err, result, ctx, _) return end local bufstate = bufstates[bufnr] - if not bufstate or not bufstate.enabled then + if not bufstate.enabled then return end if not (bufstate.client_hints and bufstate.version) then @@ -91,11 +104,7 @@ function M.on_refresh(err, _, ctx, _) for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do for _, winid in ipairs(api.nvim_list_wins()) do if api.nvim_win_get_buf(winid) == bufnr then - local bufstate = bufstates[bufnr] - if bufstate then - util._refresh(ms.textDocument_inlayHint, { bufnr = bufnr }) - break - end + util._refresh(ms.textDocument_inlayHint, { bufnr = bufnr }) end end end @@ -154,7 +163,7 @@ function M.get(filter) end local bufstate = bufstates[bufnr] - if not (bufstate and bufstate.client_hints) then + if not bufstate.client_hints then return {} end @@ -203,12 +212,9 @@ end --- Clear inlay hints ---@param bufnr (integer) Buffer handle, or 0 for current local function clear(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end - if not bufstates[bufnr] then - return - end local bufstate = bufstates[bufnr] local client_lens = (bufstate or {}).client_hints or {} local client_ids = vim.tbl_keys(client_lens) --- @type integer[] @@ -222,15 +228,14 @@ local function clear(bufnr) end --- Disable inlay hints for a buffer ----@param bufnr (integer|nil) Buffer handle, or 0 or nil for current +---@param bufnr (integer) Buffer handle, or 0 for current local function _disable(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end clear(bufnr) - if bufstates[bufnr] then - bufstates[bufnr] = { enabled = false, applied = {} } - end + bufstates[bufnr] = nil + bufstates[bufnr].enabled = false end --- Refresh inlay hints, only if we have attached clients that support it @@ -244,30 +249,38 @@ local function _refresh(bufnr, opts) end --- Enable inlay hints for a buffer ----@param bufnr (integer|nil) Buffer handle, or 0 or nil for current +---@param bufnr (integer) Buffer handle, or 0 for current local function _enable(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end - local bufstate = bufstates[bufnr] - if not bufstate then - bufstates[bufnr] = { applied = {}, enabled = true } - api.nvim_create_autocmd('LspNotify', { - buffer = bufnr, - callback = function(opts) - if - opts.data.method ~= ms.textDocument_didChange - and opts.data.method ~= ms.textDocument_didOpen - then - return - end - if bufstates[bufnr] and bufstates[bufnr].enabled then - _refresh(bufnr, { client_id = opts.data.client_id }) - end - end, - group = augroup, - }) - _refresh(bufnr) + bufstates[bufnr] = nil + bufstates[bufnr].enabled = true + _refresh(bufnr) +end + +api.nvim_create_autocmd('LspNotify', { + callback = function(args) + ---@type integer + local bufnr = args.buf + + if + args.data.method ~= ms.textDocument_didChange + and args.data.method ~= ms.textDocument_didOpen + then + return + end + if bufstates[bufnr].enabled then + _refresh(bufnr, { client_id = args.data.client_id }) + end + end, + group = augroup, +}) +api.nvim_create_autocmd('LspAttach', { + callback = function(args) + ---@type integer + local bufnr = args.buf + api.nvim_buf_attach(bufnr, false, { on_reload = function(_, cb_bufnr) clear(cb_bufnr) @@ -278,32 +291,30 @@ local function _enable(bufnr) end, on_detach = function(_, cb_bufnr) _disable(cb_bufnr) + bufstates[cb_bufnr] = nil end, }) - api.nvim_create_autocmd('LspDetach', { - buffer = bufnr, - callback = function(args) - local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_inlayHint }) - - if - not vim.iter(clients):any(function(c) - return c.id ~= args.data.client_id - end) - then - _disable(bufnr) - end - end, - group = augroup, - }) - else - bufstate.enabled = true - _refresh(bufnr) - end -end + end, + group = augroup, +}) +api.nvim_create_autocmd('LspDetach', { + callback = function(args) + ---@type integer + local bufnr = args.buf + local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_inlayHint }) + if not vim.iter(clients):any(function(c) + return c.id ~= args.data.client_id + end) then + _disable(bufnr) + end + end, + group = augroup, +}) api.nvim_set_decoration_provider(namespace, { on_win = function(_, _, bufnr, topline, botline) - local bufstate = bufstates[bufnr] + ---@type vim.lsp.inlay_hint.bufstate + local bufstate = rawget(bufstates, bufnr) if not bufstate then return end @@ -361,13 +372,13 @@ function M.is_enabled(bufnr) if bufnr == nil or bufnr == 0 then bufnr = api.nvim_get_current_buf() end - return bufstates[bufnr] and bufstates[bufnr].enabled or false + return bufstates[bufnr].enabled end --- Optional filters |kwargs|, or `nil` for all. --- @class vim.lsp.inlay_hint.enable.Filter --- @inlinedoc ---- Buffer number, or 0/nil for current buffer. +--- Buffer number, or 0 for current buffer, or nil for all. --- @field bufnr integer? --- Enables or disables inlay hints for a buffer. @@ -392,11 +403,28 @@ function M.enable(enable, filter) end vim.validate({ enable = { enable, 'boolean', true }, filter = { filter, 'table', true } }) + enable = enable == nil or enable filter = filter or {} - if enable == false then - _disable(filter.bufnr) + + if filter.bufnr == nil then + globalstate.enabled = enable + for bufnr, _ in pairs(bufstates) do + if api.nvim_buf_is_loaded(bufnr) then + if enable == false then + _disable(bufnr) + else + _enable(bufnr) + end + else + bufstates[bufnr] = nil + end + end else - _enable(filter.bufnr) + if enable == false then + _disable(filter.bufnr) + else + _enable(filter.bufnr) + end end end diff --git a/test/functional/plugin/lsp/inlay_hint_spec.lua b/test/functional/plugin/lsp/inlay_hint_spec.lua index 0aaf94d6da..94e7fbb97c 100644 --- a/test/functional/plugin/lsp/inlay_hint_spec.lua +++ b/test/functional/plugin/lsp/inlay_hint_spec.lua @@ -137,20 +137,51 @@ describe('vim.lsp.inlay_hint', function() ) end) - it('clears/applies inlay hints when passed false/true/nil', function() - exec_lua([[vim.lsp.inlay_hint.enable(false, { bufnr = bufnr })]]) - screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + describe('clears/applies inlay hints when passed false/true/nil', function() + before_each(function() + exec_lua([[ + bufnr2 = vim.api.nvim_create_buf(true, false) + vim.lsp.buf_attach_client(bufnr2, client_id) + vim.api.nvim_win_set_buf(0, bufnr2) + ]]) + insert(text) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr2 })]]) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_with_inlay_hints }) + end) - exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) - screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + it('for one single buffer', function() + exec_lua([[ + vim.lsp.inlay_hint.enable(false, { bufnr = bufnr }) + vim.api.nvim_win_set_buf(0, bufnr2) + ]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) - exec_lua( - [[vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled(bufnr), { bufnr = bufnr })]] - ) - screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) - exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) - screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua( + [[vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled(bufnr), { bufnr = bufnr })]] + ) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + end) + + it('for all buffers', function() + exec_lua([[vim.lsp.inlay_hint.enable(false)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr2)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + + exec_lua([[vim.lsp.inlay_hint.enable(true)]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + end) end) end)