aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/_buf.lua23
-rw-r--r--runtime/lua/vim/_defaults.lua16
-rw-r--r--runtime/lua/vim/_editor.lua13
-rw-r--r--runtime/lua/vim/_meta/api.lua30
-rw-r--r--runtime/lua/vim/_meta/options.lua7
-rw-r--r--runtime/lua/vim/filetype.lua9
-rw-r--r--runtime/lua/vim/filetype/detect.lua10
-rw-r--r--runtime/lua/vim/lsp.lua62
-rw-r--r--runtime/lua/vim/lsp/_changetracking.lua6
-rw-r--r--runtime/lua/vim/lsp/_dynamic.lua110
-rw-r--r--runtime/lua/vim/lsp/_tagfunc.lua2
-rw-r--r--runtime/lua/vim/lsp/_watchfiles.lua12
-rw-r--r--runtime/lua/vim/lsp/buf.lua80
-rw-r--r--runtime/lua/vim/lsp/client.lua384
-rw-r--r--runtime/lua/vim/lsp/codelens.lua2
-rw-r--r--runtime/lua/vim/lsp/completion.lua6
-rw-r--r--runtime/lua/vim/lsp/handlers.lua50
-rw-r--r--runtime/lua/vim/lsp/inlay_hint.lua10
-rw-r--r--runtime/lua/vim/lsp/log.lua9
-rw-r--r--runtime/lua/vim/lsp/semantic_tokens.lua8
-rw-r--r--runtime/lua/vim/lsp/util.lua46
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua12
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua2
23 files changed, 483 insertions, 426 deletions
diff --git a/runtime/lua/vim/_buf.lua b/runtime/lua/vim/_buf.lua
new file mode 100644
index 0000000000..0631c96f77
--- /dev/null
+++ b/runtime/lua/vim/_buf.lua
@@ -0,0 +1,23 @@
+local M = {}
+
+--- Adds one or more blank lines above or below the cursor.
+-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672
+--- @param above? boolean Place blank line(s) above the cursor
+local function add_blank(above)
+ local offset = above and 1 or 0
+ local repeated = vim.fn['repeat']({ '' }, vim.v.count1)
+ local linenr = vim.api.nvim_win_get_cursor(0)[1]
+ vim.api.nvim_buf_set_lines(0, linenr - offset, linenr - offset, true, repeated)
+end
+
+-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672
+function M.space_above()
+ add_blank(true)
+end
+
+-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672
+function M.space_below()
+ add_blank()
+end
+
+return M
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 25afd83145..06f6ed6829 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -366,16 +366,16 @@ do
-- Add empty lines
vim.keymap.set('n', '[<Space>', function()
- local repeated = vim.fn['repeat']({ '' }, vim.v.count1)
- local linenr = vim.api.nvim_win_get_cursor(0)[1]
- vim.api.nvim_buf_set_lines(0, linenr - 1, linenr - 1, true, repeated)
- end, { desc = 'Add empty line above cursor' })
+ -- TODO: update once it is possible to assign a Lua function to options #25672
+ vim.go.operatorfunc = "v:lua.require'vim._buf'.space_above"
+ return 'g@l'
+ end, { expr = true, desc = 'Add empty line above cursor' })
vim.keymap.set('n', ']<Space>', function()
- local repeated = vim.fn['repeat']({ '' }, vim.v.count1)
- local linenr = vim.api.nvim_win_get_cursor(0)[1]
- vim.api.nvim_buf_set_lines(0, linenr, linenr, true, repeated)
- end, { desc = 'Add empty line below cursor' })
+ -- TODO: update once it is possible to assign a Lua function to options #25672
+ vim.go.operatorfunc = "v:lua.require'vim._buf'.space_below"
+ return 'g@l'
+ end, { expr = true, desc = 'Add empty line below cursor' })
end
end
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index b4a1e0fc15..44f17b3f85 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -1151,21 +1151,16 @@ end
--- @param ... any
--- @return any # given arguments.
function vim.print(...)
- if vim.in_fast_event() then
- print(...)
- return ...
- end
-
+ local msg = {}
for i = 1, select('#', ...) do
local o = select(i, ...)
if type(o) == 'string' then
- vim.api.nvim_out_write(o)
+ table.insert(msg, o)
else
- vim.api.nvim_out_write(vim.inspect(o, { newline = '\n', indent = ' ' }))
+ table.insert(msg, vim.inspect(o, { newline = '\n', indent = ' ' }))
end
- vim.api.nvim_out_write('\n')
end
-
+ print(table.concat(msg, '\n'))
return ...
end
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index c28af7bbff..3c9b9d4f44 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -592,8 +592,9 @@ function vim.api.nvim_buf_line_count(buffer) end
--- - id : id of the extmark to edit.
--- - end_row : ending line of the mark, 0-based inclusive.
--- - end_col : ending col of the mark, 0-based exclusive.
---- - hl_group : name of the highlight group used to highlight
---- this mark.
+--- - hl_group : highlight group used for the text range. This and below
+--- highlight groups can be supplied either as a string or as an integer,
+--- the latter of which can be obtained using `nvim_get_hl_id_by_name()`.
--- - hl_eol : when true, for a multiline highlight covering the
--- EOL of a line, continue the highlight for the rest
--- of the screen line (just like for diff and
@@ -603,9 +604,7 @@ function vim.api.nvim_buf_line_count(buffer) end
--- text chunk with specified highlight. `highlight` element
--- can either be a single highlight group, or an array of
--- multiple highlight groups that will be stacked
---- (highest priority last). A highlight group can be supplied
---- either as a string or as an integer, the latter which
---- can be obtained using `nvim_get_hl_id_by_name()`.
+--- (highest priority last).
--- - virt_text_pos : position of virtual text. Possible values:
--- - "eol": right after eol character (default).
--- - "overlay": display over the specified column, without
@@ -676,15 +675,12 @@ function vim.api.nvim_buf_line_count(buffer) end
--- buffer or end of the line respectively. Defaults to true.
--- - sign_text: string of length 1-2 used to display in the
--- sign column.
---- - sign_hl_group: name of the highlight group used to
---- highlight the sign column text.
---- - number_hl_group: name of the highlight group used to
---- highlight the number column.
---- - line_hl_group: name of the highlight group used to
---- highlight the whole line.
---- - cursorline_hl_group: name of the highlight group used to
---- highlight the sign column text when the cursor is on
---- the same line as the mark and 'cursorline' is enabled.
+--- - sign_hl_group: highlight group used for the sign column text.
+--- - number_hl_group: highlight group used for the number column.
+--- - line_hl_group: highlight group used for the whole line.
+--- - cursorline_hl_group: highlight group used for the sign
+--- column text when the cursor is on the same line as the
+--- mark and 'cursorline' is enabled.
--- - conceal: string which should be either empty or a single
--- character. Enable concealing similar to `:syn-conceal`.
--- When a character is supplied it is used as `:syn-cchar`.
@@ -1106,8 +1102,8 @@ function vim.api.nvim_del_var(name) end
--- Echo a message.
---
--- @param chunks any[] A list of `[text, hl_group]` arrays, each representing a
---- text chunk with specified highlight. `hl_group` element
---- can be omitted for no highlight.
+--- text chunk with specified highlight group name or ID.
+--- `hl_group` element can be omitted for no highlight.
--- @param history boolean if true, add to `message-history`.
--- @param opts vim.api.keyset.echo_opts Optional parameters.
--- - verbose: Message is printed as a result of 'verbose' option.
@@ -1768,7 +1764,7 @@ function vim.api.nvim_open_term(buffer, opts) end
--- - focusable: Enable focus by user actions (wincmds, mouse events).
--- Defaults to true. Non-focusable windows can be entered by
--- `nvim_set_current_win()`, or, when the `mouse` field is set to true,
---- by mouse events.
+--- by mouse events. See `focusable`.
--- - mouse: Specify how this window interacts with mouse events.
--- Defaults to `focusable` value.
--- - If false, mouse events pass through this window.
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index cb783720ac..e485009ca2 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -7122,6 +7122,13 @@ vim.go.titleold = vim.o.titleold
--- expanded according to the rules used for 'statusline'. If it contains
--- an invalid '%' format, the value is used as-is and no error or warning
--- will be given when the value is set.
+---
+--- The default behaviour is equivalent to:
+---
+--- ```vim
+--- set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim
+--- ```
+---
--- This option cannot be set in a modeline when 'modelineexpr' is off.
---
--- Example:
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 3b3c4481f2..4383c0983e 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -275,6 +275,9 @@ local extension = {
mdh = 'c',
epro = 'c',
qc = 'c',
+ c3 = 'c3',
+ c3i = 'c3',
+ c3t = 'c3',
cabal = 'cabal',
cairo = 'cairo',
capnp = 'capnp',
@@ -300,6 +303,7 @@ local extension = {
cho = 'chordpro',
chordpro = 'chordpro',
ck = 'chuck',
+ cl = detect.cl,
eni = 'cl',
icl = 'clean',
cljx = 'clojure',
@@ -653,6 +657,8 @@ local extension = {
jsp = 'jsp',
jl = 'julia',
just = 'just',
+ kl = 'karel',
+ KL = 'karel',
kdl = 'kdl',
kv = 'kivy',
kix = 'kix',
@@ -686,7 +692,6 @@ local extension = {
ily = 'lilypond',
liquid = 'liquid',
liq = 'liquidsoap',
- cl = 'lisp',
L = 'lisp',
lisp = 'lisp',
el = 'lisp',
@@ -792,6 +797,7 @@ local extension = {
mof = 'msidl',
odl = 'msidl',
msql = 'msql',
+ mss = 'mss',
mu = 'mupad',
mush = 'mush',
mustache = 'mustache',
@@ -2211,6 +2217,7 @@ local pattern = {
['/screengrab/.*%.conf$'] = 'dosini',
['^${GNUPGHOME}/gpg%.conf$'] = 'gpg',
['/boot/grub/grub%.conf$'] = 'grub',
+ ['/hypr/.*%.conf$'] = 'hyprlang',
['^lilo%.conf'] = starsetf('lilo'),
['^named.*%.conf$'] = 'named',
['^rndc.*%.conf$'] = 'named',
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 98b001bd51..81b94c69db 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -181,6 +181,16 @@ function M.changelog(_, bufnr)
end
--- @type vim.filetype.mapfn
+function M.cl(_, bufnr)
+ local lines = table.concat(getlines(bufnr, 1, 4))
+ if lines:match('/%*') then
+ return 'opencl'
+ else
+ return 'lisp'
+ end
+end
+
+--- @type vim.filetype.mapfn
function M.class(_, bufnr)
-- Check if not a Java class (starts with '\xca\xfe\xba\xbe')
if not getline(bufnr, 1):find('^\202\254\186\190') then
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 42a0ccc3d4..c032d25cb1 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -3,7 +3,6 @@ local validate = vim.validate
local lsp = vim._defer_require('vim.lsp', {
_changetracking = ..., --- @module 'vim.lsp._changetracking'
- _dynamic = ..., --- @module 'vim.lsp._dynamic'
_snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar'
_tagfunc = ..., --- @module 'vim.lsp._tagfunc'
_watchfiles = ..., --- @module 'vim.lsp._watchfiles'
@@ -31,6 +30,13 @@ local changetracking = lsp._changetracking
---@nodoc
lsp.rpc_response_error = lsp.rpc.rpc_response_error
+lsp._resolve_to_request = {
+ [ms.codeAction_resolve] = ms.textDocument_codeAction,
+ [ms.codeLens_resolve] = ms.textDocument_codeLens,
+ [ms.documentLink_resolve] = ms.textDocument_documentLink,
+ [ms.inlayHint_resolve] = ms.textDocument_inlayHint,
+}
+
-- maps request name to the required server_capability in the client.
lsp._request_name_to_capability = {
[ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' },
@@ -343,17 +349,17 @@ end
---@param bufnr integer
function lsp._set_defaults(client, bufnr)
if
- client.supports_method(ms.textDocument_definition) and is_empty_or_default(bufnr, 'tagfunc')
+ client:supports_method(ms.textDocument_definition) and is_empty_or_default(bufnr, 'tagfunc')
then
vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc'
end
if
- client.supports_method(ms.textDocument_completion) and is_empty_or_default(bufnr, 'omnifunc')
+ client:supports_method(ms.textDocument_completion) and is_empty_or_default(bufnr, 'omnifunc')
then
vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
end
if
- client.supports_method(ms.textDocument_rangeFormatting)
+ client:supports_method(ms.textDocument_rangeFormatting)
and is_empty_or_default(bufnr, 'formatprg')
and is_empty_or_default(bufnr, 'formatexpr')
then
@@ -361,14 +367,14 @@ function lsp._set_defaults(client, bufnr)
end
vim._with({ buf = bufnr }, function()
if
- client.supports_method(ms.textDocument_hover)
+ client:supports_method(ms.textDocument_hover)
and is_empty_or_default(bufnr, 'keywordprg')
and vim.fn.maparg('K', 'n', false, false) == ''
then
vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' })
end
end)
- if client.supports_method(ms.textDocument_diagnostic) then
+ if client:supports_method(ms.textDocument_diagnostic) then
lsp.diagnostic._enable(bufnr)
end
end
@@ -479,12 +485,12 @@ local function text_document_did_save_handler(bufnr)
local name = api.nvim_buf_get_name(bufnr)
local old_name = changetracking._get_and_set_name(client, bufnr, name)
if old_name and name ~= old_name then
- client.notify(ms.textDocument_didClose, {
+ client:notify(ms.textDocument_didClose, {
textDocument = {
uri = vim.uri_from_fname(old_name),
},
})
- client.notify(ms.textDocument_didOpen, {
+ client:notify(ms.textDocument_didOpen, {
textDocument = {
version = 0,
uri = uri,
@@ -500,7 +506,7 @@ local function text_document_did_save_handler(bufnr)
if type(save_capability) == 'table' and save_capability.includeText then
included_text = text(bufnr)
end
- client.notify(ms.textDocument_didSave, {
+ client:notify(ms.textDocument_didSave, {
textDocument = {
uri = uri,
},
@@ -521,10 +527,10 @@ local function buf_detach_client(bufnr, client)
changetracking.reset_buf(client, bufnr)
- if client.supports_method(ms.textDocument_didClose) then
+ if client:supports_method(ms.textDocument_didClose) then
local uri = vim.uri_from_bufnr(bufnr)
local params = { textDocument = { uri = uri } }
- client.notify(ms.textDocument_didClose, params)
+ client:notify(ms.textDocument_didClose, params)
end
client.attached_buffers[bufnr] = nil
@@ -558,12 +564,12 @@ local function buf_attach(bufnr)
},
reason = protocol.TextDocumentSaveReason.Manual, ---@type integer
}
- if client.supports_method(ms.textDocument_willSave) then
- client.notify(ms.textDocument_willSave, params)
+ if client:supports_method(ms.textDocument_willSave) then
+ client:notify(ms.textDocument_willSave, params)
end
- if client.supports_method(ms.textDocument_willSaveWaitUntil) then
+ if client:supports_method(ms.textDocument_willSaveWaitUntil) then
local result, err =
- client.request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf)
+ client:request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf)
if result and result.result then
util.apply_text_edits(result.result, ctx.buf, client.offset_encoding)
elseif err then
@@ -597,8 +603,8 @@ local function buf_attach(bufnr)
local params = { textDocument = { uri = uri } }
for _, client in ipairs(clients) do
changetracking.reset_buf(client, bufnr)
- if client.supports_method(ms.textDocument_didClose) then
- client.notify(ms.textDocument_didClose, params)
+ if client:supports_method(ms.textDocument_didClose) then
+ client:notify(ms.textDocument_didClose, params)
end
end
for _, client in ipairs(clients) do
@@ -656,7 +662,7 @@ function lsp.buf_attach_client(bufnr, client_id)
-- Send didOpen for the client if it is initialized. If it isn't initialized
-- then it will send didOpen on initialize.
if client.initialized then
- client:_on_attach(bufnr)
+ client:on_attach(bufnr)
end
return true
end
@@ -734,13 +740,13 @@ function lsp.stop_client(client_id, force)
for _, id in ipairs(ids) do
if type(id) == 'table' then
if id.stop then
- id.stop(force)
+ id:stop(force)
end
else
--- @cast id -vim.lsp.Client
local client = all_clients[id]
if client then
- client.stop(force)
+ client:stop(force)
end
end
end
@@ -784,7 +790,7 @@ function lsp.get_clients(filter)
and (filter.id == nil or client.id == filter.id)
and (filter.bufnr == nil or client.attached_buffers[bufnr])
and (filter.name == nil or client.name == filter.name)
- and (filter.method == nil or client.supports_method(filter.method, { bufnr = filter.bufnr }))
+ and (filter.method == nil or client:supports_method(filter.method, filter.bufnr))
and (filter._uninitialized or client.initialized)
then
clients[#clients + 1] = client
@@ -806,7 +812,7 @@ api.nvim_create_autocmd('VimLeavePre', {
local active_clients = lsp.get_clients()
log.info('exit_handler', active_clients)
for _, client in pairs(all_clients) do
- client.stop()
+ client:stop()
end
local timeouts = {} --- @type table<integer,integer>
@@ -841,7 +847,7 @@ api.nvim_create_autocmd('VimLeavePre', {
if not vim.wait(max_timeout, check_clients_closed, poll_time) then
for client_id, client in pairs(active_clients) do
if timeouts[client_id] ~= nil then
- client.stop(true)
+ client:stop(true)
end
end
end
@@ -877,11 +883,11 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
local clients = lsp.get_clients({ bufnr = bufnr })
local client_request_ids = {} --- @type table<integer,integer>
for _, client in ipairs(clients) do
- if client.supports_method(method, { bufnr = bufnr }) then
+ if client:supports_method(method, bufnr) then
method_supported = true
local cparams = type(params) == 'function' and params(client, bufnr) or params --[[@as table?]]
- local request_success, request_id = client.request(method, cparams, handler, bufnr)
+ local request_success, request_id = client:request(method, cparams, handler, bufnr)
-- This could only fail if the client shut down in the time since we looked
-- it up and we did the request, which should be rare.
if request_success then
@@ -904,7 +910,7 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
local function _cancel_all_requests()
for client_id, request_id in pairs(client_request_ids) do
local client = all_clients[client_id]
- client.cancel_request(request_id)
+ client:cancel_request(request_id)
end
end
@@ -1043,7 +1049,7 @@ function lsp.formatexpr(opts)
end
local bufnr = api.nvim_get_current_buf()
for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do
- if client.supports_method(ms.textDocument_rangeFormatting) then
+ if client:supports_method(ms.textDocument_rangeFormatting) then
local params = util.make_formatting_params()
local end_line = vim.fn.getline(end_lnum) --[[@as string]]
local end_col = vim.str_utfindex(end_line, client.offset_encoding)
@@ -1059,7 +1065,7 @@ function lsp.formatexpr(opts)
},
}
local response =
- client.request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr)
+ client:request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr)
if response and response.result then
lsp.util.apply_text_edits(response.result, bufnr, client.offset_encoding)
return 0
diff --git a/runtime/lua/vim/lsp/_changetracking.lua b/runtime/lua/vim/lsp/_changetracking.lua
index b2be53269f..8588502697 100644
--- a/runtime/lua/vim/lsp/_changetracking.lua
+++ b/runtime/lua/vim/lsp/_changetracking.lua
@@ -40,7 +40,7 @@ local M = {}
--- @class vim.lsp.CTGroupState
--- @field buffers table<integer,vim.lsp.CTBufferState>
--- @field debounce integer debounce duration in ms
---- @field clients table<integer, table> clients using this state. {client_id, client}
+--- @field clients table<integer, vim.lsp.Client> clients using this state. {client_id, client}
---@param group vim.lsp.CTGroup
---@return string
@@ -273,8 +273,8 @@ local function send_changes(bufnr, sync_kind, state, buf_state)
end
local uri = vim.uri_from_bufnr(bufnr)
for _, client in pairs(state.clients) do
- if not client.is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then
- client.notify(protocol.Methods.textDocument_didChange, {
+ if not client:is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then
+ client:notify(protocol.Methods.textDocument_didChange, {
textDocument = {
uri = uri,
version = util.buf_versions[bufnr],
diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua
deleted file mode 100644
index 27113c0e74..0000000000
--- a/runtime/lua/vim/lsp/_dynamic.lua
+++ /dev/null
@@ -1,110 +0,0 @@
-local glob = vim.glob
-
---- @class lsp.DynamicCapabilities
---- @field capabilities table<string, lsp.Registration[]>
---- @field client_id number
-local M = {}
-
---- @param client_id number
---- @return lsp.DynamicCapabilities
-function M.new(client_id)
- return setmetatable({
- capabilities = {},
- client_id = client_id,
- }, { __index = M })
-end
-
-function M:supports_registration(method)
- local client = vim.lsp.get_client_by_id(self.client_id)
- if not client then
- return false
- end
- local capability = vim.tbl_get(client.capabilities, unpack(vim.split(method, '/')))
- return type(capability) == 'table' and capability.dynamicRegistration
-end
-
---- @param registrations lsp.Registration[]
-function M:register(registrations)
- -- remove duplicates
- self:unregister(registrations)
- for _, reg in ipairs(registrations) do
- local method = reg.method
- if not self.capabilities[method] then
- self.capabilities[method] = {}
- end
- table.insert(self.capabilities[method], reg)
- end
-end
-
---- @param unregisterations lsp.Unregistration[]
-function M:unregister(unregisterations)
- for _, unreg in ipairs(unregisterations) do
- local method = unreg.method
- if not self.capabilities[method] then
- return
- end
- local id = unreg.id
- for i, reg in ipairs(self.capabilities[method]) do
- if reg.id == id then
- table.remove(self.capabilities[method], i)
- break
- end
- end
- end
-end
-
---- @param method string
---- @param opts? {bufnr: integer?}
---- @return lsp.Registration? (table|nil) the registration if found
-function M:get(method, opts)
- opts = opts or {}
- opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf()
- for _, reg in ipairs(self.capabilities[method] or {}) do
- if not reg.registerOptions then
- return reg
- end
- local documentSelector = reg.registerOptions.documentSelector
- if not documentSelector then
- return reg
- end
- if self:match(opts.bufnr, documentSelector) then
- return reg
- end
- end
-end
-
---- @param method string
---- @param opts? {bufnr: integer?}
-function M:supports(method, opts)
- return self:get(method, opts) ~= nil
-end
-
---- @param bufnr number
---- @param documentSelector lsp.DocumentSelector
---- @private
-function M:match(bufnr, documentSelector)
- local client = vim.lsp.get_client_by_id(self.client_id)
- if not client then
- return false
- end
- local language = client.get_language_id(bufnr, vim.bo[bufnr].filetype)
- local uri = vim.uri_from_bufnr(bufnr)
- local fname = vim.uri_to_fname(uri)
- for _, filter in ipairs(documentSelector) do
- local matches = true
- if filter.language and language ~= filter.language then
- matches = false
- end
- if matches and filter.scheme and not vim.startswith(uri, filter.scheme .. ':') then
- matches = false
- end
- if matches and filter.pattern and not glob.to_lpeg(filter.pattern):match(fname) then
- matches = false
- end
- if matches then
- return true
- end
- end
-end
-
-return M
diff --git a/runtime/lua/vim/lsp/_tagfunc.lua b/runtime/lua/vim/lsp/_tagfunc.lua
index f75d43f373..f6ffc63824 100644
--- a/runtime/lua/vim/lsp/_tagfunc.lua
+++ b/runtime/lua/vim/lsp/_tagfunc.lua
@@ -59,7 +59,7 @@ local function query_definition(pattern)
remaining = remaining - 1
end
local params = util.make_position_params(win, client.offset_encoding)
- client.request(ms.textDocument_definition, params, on_response, bufnr)
+ client:request(ms.textDocument_definition, params, on_response, bufnr)
end
vim.wait(1000, function()
return remaining == 0
diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua
index 98e9818bcd..248969885c 100644
--- a/runtime/lua/vim/lsp/_watchfiles.lua
+++ b/runtime/lua/vim/lsp/_watchfiles.lua
@@ -44,9 +44,8 @@ M._poll_exclude_pattern = glob.to_lpeg('**/.git/{objects,subtree-cache}/**')
--- Registers the workspace/didChangeWatchedFiles capability dynamically.
---
---@param reg lsp.Registration LSP Registration object.
----@param ctx lsp.HandlerContext Context from the |lsp-handler|.
-function M.register(reg, ctx)
- local client_id = ctx.client_id
+---@param client_id integer Client ID.
+function M.register(reg, client_id)
local client = assert(vim.lsp.get_client_by_id(client_id), 'Client must be running')
-- Ill-behaved servers may not honor the client capability and try to register
-- anyway, so ignore requests when the user has opted out of the feature.
@@ -117,7 +116,7 @@ function M.register(reg, ctx)
local params = {
changes = change_queues[client_id],
}
- client.notify(ms.workspace_didChangeWatchedFiles, params)
+ client:notify(ms.workspace_didChangeWatchedFiles, params)
queue_timers[client_id] = nil
change_queues[client_id] = nil
change_cache[client_id] = nil
@@ -155,9 +154,8 @@ end
--- Unregisters the workspace/didChangeWatchedFiles capability dynamically.
---
---@param unreg lsp.Unregistration LSP Unregistration object.
----@param ctx lsp.HandlerContext Context from the |lsp-handler|.
-function M.unregister(unreg, ctx)
- local client_id = ctx.client_id
+---@param client_id integer Client ID.
+function M.unregister(unreg, client_id)
local client_cancels = cancels[client_id]
local reg_cancels = client_cancels[unreg.id]
while #reg_cancels > 0 do
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 6d7597c5ff..10c0dbefdc 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -20,6 +20,8 @@ local function client_positional_params(params)
end
end
+local hover_ns = api.nvim_create_namespace('vim_lsp_hover_range')
+
--- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts
--- @field silent? boolean
@@ -30,13 +32,24 @@ end
--- In the floating window, all commands and mappings are available as usual,
--- except that "q" dismisses the window.
--- You can scroll the contents the same as you would any other buffer.
+---
+--- Note: to disable hover highlights, add the following to your config:
+---
+--- ```lua
+--- vim.api.nvim_create_autocmd('ColorScheme', {
+--- callback = function()
+--- vim.api.nvim_set_hl(0, 'LspReferenceTarget', {})
+--- end,
+--- })
+--- ```
--- @param config? vim.lsp.buf.hover.Opts
function M.hover(config)
config = config or {}
config.focus_id = ms.textDocument_hover
lsp.buf_request_all(0, ms.textDocument_hover, client_positional_params(), function(results, ctx)
- if api.nvim_get_current_buf() ~= ctx.bufnr then
+ local bufnr = assert(ctx.bufnr)
+ if api.nvim_get_current_buf() ~= bufnr then
-- Ignore result since buffer changed. This happens for slow language servers.
return
end
@@ -67,9 +80,10 @@ function M.hover(config)
local format = 'markdown'
for client_id, result in pairs(results1) do
+ local client = assert(lsp.get_client_by_id(client_id))
if nresults > 1 then
-- Show client name if there are multiple clients
- contents[#contents + 1] = string.format('# %s', lsp.get_client_by_id(client_id).name)
+ contents[#contents + 1] = string.format('# %s', client.name)
end
if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then
if #results1 == 1 then
@@ -87,6 +101,22 @@ function M.hover(config)
else
vim.list_extend(contents, util.convert_input_to_markdown_lines(result.contents))
end
+ local range = result.range
+ if range then
+ local start = range.start
+ local end_ = range['end']
+ local start_idx = util._get_line_byte_from_position(bufnr, start, client.offset_encoding)
+ local end_idx = util._get_line_byte_from_position(bufnr, end_, client.offset_encoding)
+
+ vim.hl.range(
+ bufnr,
+ hover_ns,
+ 'LspReferenceTarget',
+ { start.line, start_idx },
+ { end_.line, end_idx },
+ { priority = vim.hl.priorities.user }
+ )
+ end
contents[#contents + 1] = '---'
end
@@ -100,7 +130,16 @@ function M.hover(config)
return
end
- lsp.util.open_floating_preview(contents, format, config)
+ local _, winid = lsp.util.open_floating_preview(contents, format, config)
+
+ api.nvim_create_autocmd('WinClosed', {
+ pattern = tostring(winid),
+ once = true,
+ callback = function()
+ api.nvim_buf_clear_namespace(bufnr, hover_ns, 0, -1)
+ return true
+ end,
+ })
end)
end
@@ -193,7 +232,7 @@ local function get_locations(method, opts)
end
for _, client in ipairs(clients) do
local params = util.make_position_params(win, client.offset_encoding)
- client.request(method, params, function(_, result)
+ client:request(method, params, function(_, result)
on_response(_, result, client)
end)
end
@@ -529,12 +568,14 @@ function M.format(opts)
end
if opts.async then
+ --- @param idx integer
+ --- @param client vim.lsp.Client
local function do_format(idx, client)
if not client then
return
end
local params = set_range(client, util.make_formatting_params(opts.formatting_options))
- client.request(method, params, function(...)
+ client:request(method, params, function(...)
local handler = client.handlers[method] or lsp.handlers[method]
handler(...)
do_format(next(clients, idx))
@@ -545,7 +586,7 @@ function M.format(opts)
local timeout_ms = opts.timeout_ms or 1000
for _, client in pairs(clients) do
local params = set_range(client, util.make_formatting_params(opts.formatting_options))
- local result, err = client.request_sync(method, params, timeout_ms, bufnr)
+ local result, err = client:request_sync(method, params, timeout_ms, bufnr)
if result and result.result then
util.apply_text_edits(result.result, bufnr, client.offset_encoding)
elseif err then
@@ -609,6 +650,8 @@ function M.rename(new_name, opts)
)[1]
end
+ --- @param idx integer
+ --- @param client? vim.lsp.Client
local function try_use_client(idx, client)
if not client then
return
@@ -620,15 +663,15 @@ function M.rename(new_name, opts)
params.newName = name
local handler = client.handlers[ms.textDocument_rename]
or lsp.handlers[ms.textDocument_rename]
- client.request(ms.textDocument_rename, params, function(...)
+ client:request(ms.textDocument_rename, params, function(...)
handler(...)
try_use_client(next(clients, idx))
end, bufnr)
end
- if client.supports_method(ms.textDocument_prepareRename) then
+ if client:supports_method(ms.textDocument_prepareRename) then
local params = util.make_position_params(win, client.offset_encoding)
- client.request(ms.textDocument_prepareRename, params, function(err, result)
+ client:request(ms.textDocument_prepareRename, params, function(err, result)
if err or result == nil then
if next(clients, idx) then
try_use_client(next(clients, idx))
@@ -667,7 +710,7 @@ function M.rename(new_name, opts)
end, bufnr)
else
assert(
- client.supports_method(ms.textDocument_rename),
+ client:supports_method(ms.textDocument_rename),
'Client must support textDocument/rename'
)
if new_name then
@@ -742,7 +785,7 @@ function M.references(context, opts)
params.context = context or {
includeDeclaration = true,
}
- client.request(ms.textDocument_references, params, function(_, result)
+ client:request(ms.textDocument_references, params, function(_, result)
local items = util.locations_to_items(result or {}, client.offset_encoding)
vim.list_extend(all_items, items)
remaining = remaining - 1
@@ -774,7 +817,7 @@ local function request_with_id(client_id, method, params, handler, bufnr)
)
return
end
- client.request(method, params, handler, bufnr)
+ client:request(method, params, handler, bufnr)
end
--- @param item lsp.TypeHierarchyItem|lsp.CallHierarchyItem
@@ -841,7 +884,7 @@ local function hierarchy(method)
for _, client in ipairs(clients) do
local params = util.make_position_params(win, client.offset_encoding)
--- @param result lsp.CallHierarchyItem[]|lsp.TypeHierarchyItem[]?
- client.request(prepare_method, params, function(err, result, ctx)
+ client:request(prepare_method, params, function(err, result, ctx)
if err then
vim.notify(err.message, vim.log.levels.WARN)
elseif result then
@@ -1092,13 +1135,8 @@ local function on_code_action_results(results, opts)
local action = choice.action
local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number')
- local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = bufnr })
-
- local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider')
- or client.supports_method(ms.codeAction_resolve)
-
- if not action.edit and client and supports_resolve then
- client.request(ms.codeAction_resolve, action, function(err, resolved_action)
+ if not action.edit and client:supports_method(ms.codeAction_resolve) then
+ client:request(ms.codeAction_resolve, action, function(err, resolved_action)
if err then
if action.command then
apply_action(action, client, choice.ctx)
@@ -1219,7 +1257,7 @@ function M.code_action(opts)
})
end
- client.request(ms.textDocument_codeAction, params, on_result, bufnr)
+ client:request(ms.textDocument_codeAction, params, on_result, bufnr)
end
end
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 2718f40c96..3b79da99ea 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -91,7 +91,7 @@ local validate = vim.validate
--- (default: client-id)
--- @field name? string
---
---- Language ID as string. Defaults to the filetype.
+--- Language ID as string. Defaults to the buffer filetype.
--- @field get_language_id? fun(bufnr: integer, filetype: string): string
---
--- The encoding that the LSP server expects. Client does not verify this is correct.
@@ -216,72 +216,31 @@ local validate = vim.validate
---
--- The capabilities provided by the client (editor or tool)
--- @field capabilities lsp.ClientCapabilities
+--- @field private registrations table<string,lsp.Registration[]>
--- @field dynamic_capabilities lsp.DynamicCapabilities
---
---- Sends a request to the server.
---- This is a thin wrapper around {client.rpc.request} with some additional
---- checking.
---- If {handler} is not specified and if there's no respective global
---- handler, then an error will occur.
---- Returns: {status}, {client_id}?. {status} is a boolean indicating if
---- the notification was successful. If it is `false`, then it will always
---- be `false` (the client has shutdown).
---- If {status} is `true`, the function returns {request_id} as the second
---- result. You can use this with `client.cancel_request(request_id)` to cancel
---- the request.
---- @field request fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?
----
---- Sends a request to the server and synchronously waits for the response.
---- This is a wrapper around {client.request}
---- Returns: { err=err, result=result }, a dict, where `err` and `result`
---- come from the |lsp-handler|. On timeout, cancel or error, returns `(nil,
---- err)` where `err` is a string describing the failure reason. If the request
---- was unsuccessful returns `nil`.
---- @field request_sync fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict
----
---- Sends a notification to an LSP server.
---- Returns: a boolean to indicate if the notification was successful. If
---- it is false, then it will always be false (the client has shutdown).
---- @field notify fun(method: string, params: table?): boolean
----
---- Cancels a request with a given request id.
---- Returns: same as `notify()`.
---- @field cancel_request fun(id: integer): boolean
----
---- Stops a client, optionally with force.
---- By default, it will just ask the server to shutdown without force.
---- If you request to stop a client which has previously been requested to
---- shutdown, it will automatically escalate and force shutdown.
---- @field stop fun(force?: boolean)
----
---- Runs the on_attach function from the client's config if it was defined.
---- Useful for buffer-local setup.
---- @field on_attach fun(bufnr: integer)
----
--- @field private _before_init_cb? vim.lsp.client.before_init_cb
--- @field private _on_attach_cbs vim.lsp.client.on_attach_cb[]
--- @field private _on_init_cbs vim.lsp.client.on_init_cb[]
--- @field private _on_exit_cbs vim.lsp.client.on_exit_cb[]
--- @field private _on_error_cb? fun(code: integer, err: string)
----
---- Checks if a client supports a given method.
---- Always returns true for unknown off-spec methods.
---- {opts} is a optional `{bufnr?: integer}` table.
---- Some language server capabilities can be file specific.
---- @field supports_method fun(method: string, opts?: {bufnr: integer?}): boolean
----
---- Checks whether a client is stopped.
---- Returns: true if the client is fully stopped.
---- @field is_stopped fun(): boolean
local Client = {}
Client.__index = Client
---- @param cls table
---- @param meth any
---- @return function
-local function method_wrapper(cls, meth)
- return function(...)
- return meth(cls, ...)
+--- @param obj table<string,any>
+--- @param cls table<string,function>
+--- @param name string
+local function method_wrapper(obj, cls, name)
+ local meth = assert(cls[name])
+ obj[name] = function(...)
+ local arg = select(1, ...)
+ if arg and getmetatable(arg) == cls then
+ -- First argument is self, call meth directly
+ return meth(...)
+ end
+ vim.deprecate('client.' .. name, 'client:' .. name, '0.13')
+ -- First argument is not self, insert it
+ return meth(obj, ...)
end
end
@@ -403,18 +362,16 @@ local function get_name(id, config)
return tostring(id)
end
---- @param workspace_folders lsp.WorkspaceFolder[]?
---- @param root_dir string?
+--- @param workspace_folders string|lsp.WorkspaceFolder[]?
--- @return lsp.WorkspaceFolder[]?
-local function get_workspace_folders(workspace_folders, root_dir)
- if workspace_folders then
+local function get_workspace_folders(workspace_folders)
+ if type(workspace_folders) == 'table' then
return workspace_folders
- end
- if root_dir then
+ elseif type(workspace_folders) == 'string' then
return {
{
- uri = vim.uri_from_fname(root_dir),
- name = root_dir,
+ uri = vim.uri_from_fname(workspace_folders),
+ name = workspace_folders,
},
}
end
@@ -451,13 +408,13 @@ function Client.create(config)
requests = {},
attached_buffers = {},
server_capabilities = {},
- dynamic_capabilities = lsp._dynamic.new(id),
+ registrations = {},
commands = config.commands or {},
settings = config.settings or {},
flags = config.flags or {},
get_language_id = config.get_language_id or default_get_language_id,
capabilities = config.capabilities or lsp.protocol.make_client_capabilities(),
- workspace_folders = get_workspace_folders(config.workspace_folders, config.root_dir),
+ workspace_folders = get_workspace_folders(config.workspace_folders or config.root_dir),
root_dir = config.root_dir,
_before_init_cb = config.before_init,
_on_init_cbs = ensure_list(config.on_init),
@@ -478,24 +435,45 @@ function Client.create(config)
messages = { name = name, messages = {}, progress = {}, status = {} },
}
- self.request = method_wrapper(self, Client._request)
- self.request_sync = method_wrapper(self, Client._request_sync)
- self.notify = method_wrapper(self, Client._notify)
- self.cancel_request = method_wrapper(self, Client._cancel_request)
- self.stop = method_wrapper(self, Client._stop)
- self.is_stopped = method_wrapper(self, Client._is_stopped)
- self.on_attach = method_wrapper(self, Client._on_attach)
- self.supports_method = method_wrapper(self, Client._supports_method)
+ --- @class lsp.DynamicCapabilities
+ --- @nodoc
+ self.dynamic_capabilities = {
+ capabilities = self.registrations,
+ client_id = id,
+ register = function(_, registrations)
+ return self:_register_dynamic(registrations)
+ end,
+ unregister = function(_, unregistrations)
+ return self:_unregister_dynamic(unregistrations)
+ end,
+ get = function(_, method, opts)
+ return self:_get_registration(method, opts and opts.bufnr)
+ end,
+ supports_registration = function(_, method)
+ return self:_supports_registration(method)
+ end,
+ supports = function(_, method, opts)
+ return self:_get_registration(method, opts and opts.bufnr) ~= nil
+ end,
+ }
--- @type table<string|integer, string> title of unfinished progress sequences by token
self.progress.pending = {}
--- @type vim.lsp.rpc.Dispatchers
local dispatchers = {
- notification = method_wrapper(self, Client._notification),
- server_request = method_wrapper(self, Client._server_request),
- on_error = method_wrapper(self, Client._on_error),
- on_exit = method_wrapper(self, Client._on_exit),
+ notification = function(...)
+ return self:_notification(...)
+ end,
+ server_request = function(...)
+ return self:_server_request(...)
+ end,
+ on_error = function(...)
+ return self:_on_error(...)
+ end,
+ on_exit = function(...)
+ return self:_on_exit(...)
+ end,
}
-- Start the RPC client.
@@ -512,6 +490,15 @@ function Client.create(config)
setmetatable(self, Client)
+ method_wrapper(self, Client, 'request')
+ method_wrapper(self, Client, 'request_sync')
+ method_wrapper(self, Client, 'notify')
+ method_wrapper(self, Client, 'cancel_request')
+ method_wrapper(self, Client, 'stop')
+ method_wrapper(self, Client, 'is_stopped')
+ method_wrapper(self, Client, 'on_attach')
+ method_wrapper(self, Client, 'supports_method')
+
return self
end
@@ -595,7 +582,7 @@ function Client:initialize()
end
if next(self.settings) then
- self:_notify(ms.workspace_didChangeConfiguration, { settings = self.settings })
+ self:notify(ms.workspace_didChangeConfiguration, { settings = self.settings })
end
-- If server is being restarted, make sure to re-attach to any previously attached buffers.
@@ -607,7 +594,7 @@ function Client:initialize()
for buf in pairs(reattach_bufs) do
-- The buffer may have been detached in the on_init callback.
if self.attached_buffers[buf] then
- self:_on_attach(buf)
+ self:on_attach(buf)
end
end
@@ -624,14 +611,14 @@ end
--- Returns the default handler if the user hasn't set a custom one.
---
--- @param method (string) LSP method name
---- @return lsp.Handler|nil handler for the given method, if defined, or the default from |vim.lsp.handlers|
+--- @return lsp.Handler? handler for the given method, if defined, or the default from |vim.lsp.handlers|
function Client:_resolve_handler(method)
return self.handlers[method] or lsp.handlers[method]
end
--- Returns the buffer number for the given {bufnr}.
---
---- @param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer
+--- @param bufnr integer? Buffer number to resolve. Defaults to current buffer
--- @return integer bufnr
local function resolve_bufnr(bufnr)
validate('bufnr', bufnr, 'number', true)
@@ -641,7 +628,6 @@ local function resolve_bufnr(bufnr)
return bufnr
end
---- @private
--- Sends a request to the server.
---
--- This is a thin wrapper around {client.rpc.request} with some additional
@@ -650,15 +636,14 @@ end
--- @param method string LSP method name.
--- @param params? table LSP request params.
--- @param handler? lsp.Handler Response |lsp-handler| for this method.
---- @param bufnr integer Buffer handle (0 for current).
---- @return boolean status, integer? request_id {status} is a bool indicating
---- whether the request was successful. If it is `false`, then it will
---- always be `false` (the client has shutdown). If it was
---- successful, then it will return {request_id} as the
---- second result. You can use this with `client.cancel_request(request_id)`
+--- @param bufnr? integer Buffer handle. 0 for current (default).
+--- @return boolean status indicates whether the request was successful.
+--- If it is `false`, then it will always be `false` (the client has shutdown).
+--- @return integer? request_id Can be used with |Client:cancel_request()|.
+--- `nil` is request failed.
--- to cancel the-request.
--- @see |vim.lsp.buf_request_all()|
-function Client:_request(method, params, handler, bufnr)
+function Client:request(method, params, handler, bufnr)
if not handler then
handler = assert(
self:_resolve_handler(method),
@@ -667,8 +652,8 @@ function Client:_request(method, params, handler, bufnr)
end
-- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
changetracking.flush(self, bufnr)
- local version = lsp.util.buf_versions[bufnr]
bufnr = resolve_bufnr(bufnr)
+ local version = lsp.util.buf_versions[bufnr]
log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr)
local success, request_id = self.rpc.request(method, params, function(err, result)
local context = {
@@ -722,29 +707,27 @@ local function err_message(...)
end
end
---- @private
--- Sends a request to the server and synchronously waits for the response.
---
---- This is a wrapper around {client.request}
+--- This is a wrapper around |Client:request()|
---
---- @param method (string) LSP method name.
---- @param params (table) LSP request params.
---- @param timeout_ms (integer|nil) Maximum time in milliseconds to wait for
+--- @param method string LSP method name.
+--- @param params table LSP request params.
+--- @param timeout_ms integer? Maximum time in milliseconds to wait for
--- a result. Defaults to 1000
---- @param bufnr (integer) Buffer handle (0 for current).
---- @return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict, where
---- `err` and `result` come from the |lsp-handler|.
---- On timeout, cancel or error, returns `(nil, err)` where `err` is a
---- string describing the failure reason. If the request was unsuccessful
---- returns `nil`.
+--- @param bufnr integer Buffer handle (0 for current).
+--- @return {err: lsp.ResponseError?, result:any}? `result` and `err` from the |lsp-handler|.
+--- `nil` is the request was unsuccessful
+--- @return string? err On timeout, cancel or error, where `err` is a
+--- string describing the failure reason.
--- @see |vim.lsp.buf_request_sync()|
-function Client:_request_sync(method, params, timeout_ms, bufnr)
+function Client:request_sync(method, params, timeout_ms, bufnr)
local request_result = nil
local function _sync_handler(err, result)
request_result = { err = err, result = result }
end
- local success, request_id = self:_request(method, params, _sync_handler, bufnr)
+ local success, request_id = self:request(method, params, _sync_handler, bufnr)
if not success then
return nil
end
@@ -755,22 +738,20 @@ function Client:_request_sync(method, params, timeout_ms, bufnr)
if not wait_result then
if request_id then
- self:_cancel_request(request_id)
+ self:cancel_request(request_id)
end
return nil, wait_result_reason[reason]
end
return request_result
end
---- @package
--- Sends a notification to an LSP server.
---
--- @param method string LSP method name.
---- @param params table|nil LSP request params.
---- @return boolean status true if the notification was successful.
---- If it is false, then it will always be false
---- (the client has shutdown).
-function Client:_notify(method, params)
+--- @param params table? LSP request params.
+--- @return boolean status indicating if the notification was successful.
+--- If it is false, then the client has shutdown.
+function Client:notify(method, params)
if method ~= ms.textDocument_didChange then
changetracking.flush(self)
end
@@ -793,13 +774,12 @@ function Client:_notify(method, params)
return client_active
end
---- @private
--- Cancels a request with a given request id.
---
---- @param id (integer) id of request to cancel
---- @return boolean status true if notification was successful. false otherwise
---- @see |vim.lsp.client.notify()|
-function Client:_cancel_request(id)
+--- @param id integer id of request to cancel
+--- @return boolean status indicating if the notification was successful.
+--- @see |Client:notify()|
+function Client:cancel_request(id)
validate('id', id, 'number')
local request = self.requests[id]
if request and request.type == 'pending' then
@@ -813,15 +793,14 @@ function Client:_cancel_request(id)
return self.rpc.notify(ms.dollar_cancelRequest, { id = id })
end
---- @private
--- Stops a client, optionally with force.
---
---- By default, it will just ask the - server to shutdown without force. If
+--- By default, it will just request the server to shutdown without force. If
--- you request to stop a client which has previously been requested to
--- shutdown, it will automatically escalate and force shutdown.
---
---- @param force boolean|nil
-function Client:_stop(force)
+--- @param force? boolean
+function Client:stop(force)
local rpc = self.rpc
if rpc.is_closing() then
@@ -846,12 +825,110 @@ function Client:_stop(force)
end)
end
+--- Get options for a method that is registered dynamically.
+--- @param method string
+function Client:_supports_registration(method)
+ local capability = vim.tbl_get(self.capabilities, unpack(vim.split(method, '/')))
+ return type(capability) == 'table' and capability.dynamicRegistration
+end
+
+--- @private
+--- @param registrations lsp.Registration[]
+function Client:_register_dynamic(registrations)
+ -- remove duplicates
+ self:_unregister_dynamic(registrations)
+ for _, reg in ipairs(registrations) do
+ local method = reg.method
+ if not self.registrations[method] then
+ self.registrations[method] = {}
+ end
+ table.insert(self.registrations[method], reg)
+ end
+end
+
+--- @param registrations lsp.Registration[]
+function Client:_register(registrations)
+ self:_register_dynamic(registrations)
+
+ local unsupported = {} --- @type string[]
+
+ for _, reg in ipairs(registrations) do
+ local method = reg.method
+ if method == ms.workspace_didChangeWatchedFiles then
+ vim.lsp._watchfiles.register(reg, self.id)
+ elseif not self:_supports_registration(method) then
+ unsupported[#unsupported + 1] = method
+ end
+ end
+
+ if #unsupported > 0 then
+ local warning_tpl = 'The language server %s triggers a registerCapability '
+ .. 'handler for %s despite dynamicRegistration set to false. '
+ .. 'Report upstream, this warning is harmless'
+ log.warn(string.format(warning_tpl, self.name, table.concat(unsupported, ', ')))
+ end
+end
+
+--- @private
+--- @param unregistrations lsp.Unregistration[]
+function Client:_unregister_dynamic(unregistrations)
+ for _, unreg in ipairs(unregistrations) do
+ local sreg = self.registrations[unreg.method]
+ -- Unegister dynamic capability
+ for i, reg in ipairs(sreg or {}) do
+ if reg.id == unreg.id then
+ table.remove(sreg, i)
+ break
+ end
+ end
+ end
+end
+
+--- @param unregistrations lsp.Unregistration[]
+function Client:_unregister(unregistrations)
+ self:_unregister_dynamic(unregistrations)
+ for _, unreg in ipairs(unregistrations) do
+ if unreg.method == ms.workspace_didChangeWatchedFiles then
+ vim.lsp._watchfiles.unregister(unreg, self.id)
+ end
+ end
+end
+
--- @private
+function Client:_get_language_id(bufnr)
+ return self.get_language_id(bufnr, vim.bo[bufnr].filetype)
+end
+
+--- @param method string
+--- @param bufnr? integer
+--- @return lsp.Registration?
+function Client:_get_registration(method, bufnr)
+ bufnr = bufnr or vim.api.nvim_get_current_buf()
+ for _, reg in ipairs(self.registrations[method] or {}) do
+ if not reg.registerOptions or not reg.registerOptions.documentSelector then
+ return reg
+ end
+ local documentSelector = reg.registerOptions.documentSelector
+ local language = self:_get_language_id(bufnr)
+ local uri = vim.uri_from_bufnr(bufnr)
+ local fname = vim.uri_to_fname(uri)
+ for _, filter in ipairs(documentSelector) do
+ if
+ not (filter.language and language ~= filter.language)
+ and not (filter.scheme and not vim.startswith(uri, filter.scheme .. ':'))
+ and not (filter.pattern and not vim.glob.to_lpeg(filter.pattern):match(fname))
+ then
+ return reg
+ end
+ end
+ end
+end
+
--- Checks whether a client is stopped.
---
--- @return boolean # true if client is stopped or in the process of being
--- stopped; false otherwise
-function Client:_is_stopped()
+function Client:is_stopped()
return self.rpc.is_closing()
end
@@ -893,7 +970,7 @@ function Client:exec_cmd(command, context, handler)
command = cmdname,
arguments = command.arguments,
}
- self.request(ms.workspace_executeCommand, params, handler, context.bufnr)
+ self:request(ms.workspace_executeCommand, params, handler, context.bufnr)
end
--- Default handler for the 'textDocument/didOpen' LSP notification.
@@ -901,19 +978,18 @@ end
--- @param bufnr integer Number of the buffer, or 0 for current
function Client:_text_document_did_open_handler(bufnr)
changetracking.init(self, bufnr)
- if not self.supports_method(ms.textDocument_didOpen) then
+ if not self:supports_method(ms.textDocument_didOpen) then
return
end
if not api.nvim_buf_is_loaded(bufnr) then
return
end
- local filetype = vim.bo[bufnr].filetype
- self.notify(ms.textDocument_didOpen, {
+ self:notify(ms.textDocument_didOpen, {
textDocument = {
version = lsp.util.buf_versions[bufnr],
uri = vim.uri_from_bufnr(bufnr),
- languageId = self.get_language_id(bufnr, filetype),
+ languageId = self:_get_language_id(bufnr),
text = lsp._buf_get_full_text(bufnr),
},
})
@@ -930,8 +1006,9 @@ function Client:_text_document_did_open_handler(bufnr)
end
--- Runs the on_attach function from the client's config if it was defined.
+--- Useful for buffer-local setup.
--- @param bufnr integer Buffer number
-function Client:_on_attach(bufnr)
+function Client:on_attach(bufnr)
self:_text_document_did_open_handler(bufnr)
lsp._set_defaults(self, bufnr)
@@ -966,10 +1043,18 @@ function Client:write_error(code, err)
err_message(self._log_prefix, ': Error ', client_error, ': ', vim.inspect(err))
end
---- @private
+--- Checks if a client supports a given method.
+--- Always returns true for unknown off-spec methods.
+---
+--- Note: Some language server capabilities can be file specific.
--- @param method string
---- @param opts? {bufnr: integer?}
-function Client:_supports_method(method, opts)
+--- @param bufnr? integer
+function Client:supports_method(method, bufnr)
+ -- Deprecated form
+ if type(bufnr) == 'table' then
+ --- @diagnostic disable-next-line:no-unknown
+ bufnr = bufnr.bufnr
+ end
local required_capability = lsp._request_name_to_capability[method]
-- if we don't know about the method, assume that the client supports it.
if not required_capability then
@@ -978,12 +1063,37 @@ function Client:_supports_method(method, opts)
if vim.tbl_get(self.server_capabilities, unpack(required_capability)) then
return true
end
- if self.dynamic_capabilities:supports_registration(method) then
- return self.dynamic_capabilities:supports(method, opts)
+
+ local rmethod = lsp._resolve_to_request[method]
+ if rmethod then
+ if self:_supports_registration(rmethod) then
+ local reg = self:_get_registration(rmethod, bufnr)
+ return vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') or false
+ end
+ else
+ if self:_supports_registration(method) then
+ return self:_get_registration(method, bufnr) ~= nil
+ end
end
return false
end
+--- Get options for a method that is registered dynamically.
+--- @param method string
+--- @param bufnr? integer
+--- @return lsp.LSPAny?
+function Client:_get_registration_options(method, bufnr)
+ if not self:_supports_registration(method) then
+ return
+ end
+
+ local reg = self:_get_registration(method, bufnr)
+
+ if reg then
+ return reg.registerOptions
+ end
+end
+
--- @private
--- Handles a notification sent by an LSP server by invoking the
--- corresponding handler.
@@ -1061,9 +1171,9 @@ function Client:_add_workspace_folder(dir)
end
end
- local wf = assert(get_workspace_folders(nil, dir))
+ local wf = assert(get_workspace_folders(dir))
- self:_notify(ms.workspace_didChangeWorkspaceFolders, {
+ self:notify(ms.workspace_didChangeWorkspaceFolders, {
event = { added = wf, removed = {} },
})
@@ -1076,9 +1186,9 @@ end
--- Remove a directory to the workspace folders.
--- @param dir string?
function Client:_remove_workspace_folder(dir)
- local wf = assert(get_workspace_folders(nil, dir))
+ local wf = assert(get_workspace_folders(dir))
- self:_notify(ms.workspace_didChangeWorkspaceFolders, {
+ self:notify(ms.workspace_didChangeWorkspaceFolders, {
event = { added = {}, removed = wf },
})
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index fdbdda695a..a11f84d6c6 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -231,7 +231,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
countdown()
else
assert(client)
- client.request(ms.codeLens_resolve, lens, function(_, result)
+ client:request(ms.codeLens_resolve, lens, function(_, result)
if api.nvim_buf_is_loaded(bufnr) and result and result.command then
lens.command = result.command
-- Eager display to have some sort of incremental feedback
diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua
index 92bc110a97..0f388a88fd 100644
--- a/runtime/lua/vim/lsp/completion.lua
+++ b/runtime/lua/vim/lsp/completion.lua
@@ -404,7 +404,7 @@ local function request(clients, bufnr, win, callback)
for _, client in pairs(clients) do
local client_id = client.id
local params = lsp.util.make_position_params(win, client.offset_encoding)
- local ok, request_id = client.request(ms.textDocument_completion, params, function(err, result)
+ local ok, request_id = client:request(ms.textDocument_completion, params, function(err, result)
responses[client_id] = { err = err, result = result }
remaining_requests = remaining_requests - 1
if remaining_requests == 0 then
@@ -421,7 +421,7 @@ local function request(clients, bufnr, win, callback)
for client_id, request_id in pairs(request_ids) do
local client = lsp.get_client_by_id(client_id)
if client then
- client.cancel_request(request_id)
+ client:cancel_request(request_id)
end
end
end
@@ -582,7 +582,7 @@ local function on_complete_done()
local changedtick = vim.b[bufnr].changedtick
--- @param result lsp.CompletionItem
- client.request(ms.completionItem_resolve, completion_item, function(err, result)
+ client:request(ms.completionItem_resolve, completion_item, function(err, result)
if changedtick ~= vim.b[bufnr].changedtick then
return
end
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 2b7aefe0e1..5c28d88b38 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -122,46 +122,19 @@ end
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
--- @param params lsp.RegistrationParams
RSC[ms.client_registerCapability] = function(_, params, ctx)
- local client_id = ctx.client_id
- local client = assert(vim.lsp.get_client_by_id(client_id))
-
- client.dynamic_capabilities:register(params.registrations)
- for bufnr, _ in pairs(client.attached_buffers) do
+ local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
+ client:_register(params.registrations)
+ for bufnr in pairs(client.attached_buffers) do
vim.lsp._set_defaults(client, bufnr)
end
-
- ---@type string[]
- local unsupported = {}
- for _, reg in ipairs(params.registrations) do
- if reg.method == ms.workspace_didChangeWatchedFiles then
- vim.lsp._watchfiles.register(reg, ctx)
- elseif not client.dynamic_capabilities:supports_registration(reg.method) then
- unsupported[#unsupported + 1] = reg.method
- end
- end
- if #unsupported > 0 then
- local warning_tpl = 'The language server %s triggers a registerCapability '
- .. 'handler for %s despite dynamicRegistration set to false. '
- .. 'Report upstream, this warning is harmless'
- local client_name = client and client.name or string.format('id=%d', client_id)
- local warning = string.format(warning_tpl, client_name, table.concat(unsupported, ', '))
- log.warn(warning)
- end
return vim.NIL
end
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability
--- @param params lsp.UnregistrationParams
RSC[ms.client_unregisterCapability] = function(_, params, ctx)
- local client_id = ctx.client_id
- local client = assert(vim.lsp.get_client_by_id(client_id))
- client.dynamic_capabilities:unregister(params.unregisterations)
-
- for _, unreg in ipairs(params.unregisterations) do
- if unreg.method == ms.workspace_didChangeWatchedFiles then
- vim.lsp._watchfiles.unregister(unreg, ctx)
- end
- end
+ local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
+ client:_unregister(params.unregisterations)
return vim.NIL
end
@@ -173,8 +146,7 @@ RSC[ms.workspace_applyEdit] = function(_, params, ctx)
'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification'
)
-- TODO(ashkan) Do something more with label?
- local client_id = ctx.client_id
- local client = assert(vim.lsp.get_client_by_id(client_id))
+ local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
if params.label then
print('Workspace edit', params.label)
end
@@ -196,12 +168,11 @@ end
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration
--- @param params lsp.ConfigurationParams
RSC[ms.workspace_configuration] = function(_, params, ctx)
- local client_id = ctx.client_id
- local client = vim.lsp.get_client_by_id(client_id)
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
if not client then
err_message(
'LSP[',
- client_id,
+ ctx.client_id,
'] client has shut down after sending a workspace/configuration request'
)
return
@@ -229,10 +200,9 @@ end
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders
RSC[ms.workspace_workspaceFolders] = function(_, _, ctx)
- local client_id = ctx.client_id
- local client = vim.lsp.get_client_by_id(client_id)
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
if not client then
- err_message('LSP[id=', client_id, '] client has shut down after sending the message')
+ err_message('LSP[id=', ctx.client_id, '] client has shut down after sending the message')
return
end
return client.workspace_folders or vim.NIL
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua
index ca7bc3b022..f1a58de621 100644
--- a/runtime/lua/vim/lsp/inlay_hint.lua
+++ b/runtime/lua/vim/lsp/inlay_hint.lua
@@ -65,7 +65,7 @@ function M.on_inlayhint(err, result, ctx)
if num_unprocessed == 0 then
client_hints[client_id] = {}
bufstate.version = ctx.version
- api.nvim__redraw({ buf = bufnr, valid = true })
+ api.nvim__redraw({ buf = bufnr, valid = true, flush = false })
return
end
@@ -81,7 +81,7 @@ function M.on_inlayhint(err, result, ctx)
client_hints[client_id] = new_lnum_hints
bufstate.version = ctx.version
- api.nvim__redraw({ buf = bufnr, valid = true })
+ api.nvim__redraw({ buf = bufnr, valid = true, flush = false })
end
--- |lsp-handler| for the method `workspace/inlayHint/refresh`
@@ -122,12 +122,12 @@ end
--- 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)
---- local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0)
+--- local resp = client:request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0)
--- local resolved_hint = assert(resp and resp.result, resp.err)
--- vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding)
---
--- location = resolved_hint.label[1].location
---- client.request('textDocument/hover', {
+--- client:request('textDocument/hover', {
--- textDocument = { uri = location.uri },
--- position = location.range.start,
--- })
@@ -215,7 +215,7 @@ local function clear(bufnr)
end
end
api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)
- api.nvim__redraw({ buf = bufnr, valid = true })
+ api.nvim__redraw({ buf = bufnr, valid = true, flush = false })
end
--- Disable inlay hints for a buffer
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index 4f177b47fd..ec78dd3dc5 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -32,12 +32,12 @@ local function notify(msg, level)
end
end
-local logfilename = vim.fs.joinpath(vim.fn.stdpath('log'), 'lsp.log')
+local logfilename = vim.fs.joinpath(vim.fn.stdpath('log') --[[@as string]], 'lsp.log')
-- TODO: Ideally the directory should be created in open_logfile(), right
-- before opening the log file, but open_logfile() can be called from libuv
-- callbacks, where using fn.mkdir() is not allowed.
-vim.fn.mkdir(vim.fn.stdpath('log'), 'p')
+vim.fn.mkdir(vim.fn.stdpath('log') --[[@as string]], 'p')
--- Returns the log filename.
---@return string log filename
@@ -82,6 +82,7 @@ end
for level, levelnr in pairs(log_levels) do
-- Also export the log level on the root object.
+ ---@diagnostic disable-next-line: no-unknown
log[level] = levelnr
-- Add a reverse lookup.
@@ -93,7 +94,7 @@ end
--- @return fun(...:any): boolean?
local function create_logger(level, levelnr)
return function(...)
- if levelnr < current_log_level then
+ if not log.should_log(levelnr) then
return false
end
local argc = select('#', ...)
@@ -169,7 +170,7 @@ end
--- Checks whether the level is sufficient for logging.
---@param level integer log level
----@return bool : true if would log, false if not
+---@return boolean : true if would log, false if not
function log.should_log(level)
return level >= current_log_level
end
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
index 215e5f41aa..01421fea29 100644
--- a/runtime/lua/vim/lsp/semantic_tokens.lua
+++ b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -273,7 +273,7 @@ function STHighlighter:send_request()
if client and current_result.version ~= version and active_request.version ~= version then
-- cancel stale in-flight request
if active_request.request_id then
- client.cancel_request(active_request.request_id)
+ client:cancel_request(active_request.request_id)
active_request = {}
state.active_request = active_request
end
@@ -288,7 +288,7 @@ function STHighlighter:send_request()
method = method .. '/delta'
params.previousResultId = current_result.result_id
end
- local success, request_id = client.request(method, params, function(err, response, ctx)
+ local success, request_id = client:request(method, params, function(err, response, ctx)
-- look client up again using ctx.client_id instead of using a captured
-- client object
local c = vim.lsp.get_client_by_id(ctx.client_id)
@@ -519,7 +519,7 @@ function STHighlighter:reset()
if state.active_request.request_id then
local client = vim.lsp.get_client_by_id(client_id)
assert(client)
- client.cancel_request(state.active_request.request_id)
+ client:cancel_request(state.active_request.request_id)
state.active_request = {}
end
end
@@ -547,7 +547,7 @@ function STHighlighter:mark_dirty(client_id)
if state.active_request.request_id then
local client = vim.lsp.get_client_by_id(client_id)
assert(client)
- client.cancel_request(state.active_request.request_id)
+ client:cancel_request(state.active_request.request_id)
state.active_request = {}
end
end
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 6eab0f3da4..cfa8a194d9 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1848,12 +1848,11 @@ function M.try_trim_markdown_code_blocks(lines)
end
---@param window integer?: window handle or 0 for current, defaults to current
----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window`
+---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'
local function make_position_param(window, offset_encoding)
window = window or 0
local buf = api.nvim_win_get_buf(window)
local row, col = unpack(api.nvim_win_get_cursor(window))
- offset_encoding = offset_encoding or M._get_offset_encoding(buf)
row = row - 1
local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1]
if not line then
@@ -1868,13 +1867,19 @@ end
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
---
---@param window integer?: window handle or 0 for current, defaults to current
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window`
+---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'
---@return lsp.TextDocumentPositionParams
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
function M.make_position_params(window, offset_encoding)
window = window or 0
local buf = api.nvim_win_get_buf(window)
- offset_encoding = offset_encoding or M._get_offset_encoding(buf)
+ if offset_encoding == nil then
+ vim.notify_once(
+ 'warning: offset_encoding is required, using the offset_encoding from the first client',
+ vim.log.levels.WARN
+ )
+ offset_encoding = M._get_offset_encoding(buf)
+ end
return {
textDocument = M.make_text_document_params(buf),
position = make_position_param(window, offset_encoding),
@@ -1882,6 +1887,7 @@ function M.make_position_params(window, offset_encoding)
end
--- Utility function for getting the encoding of the first LSP client on the given buffer.
+---@deprecated
---@param bufnr integer buffer handle or 0 for current, defaults to current
---@return string encoding first client if there is one, nil otherwise
function M._get_offset_encoding(bufnr)
@@ -1904,7 +1910,7 @@ function M._get_offset_encoding(bufnr)
offset_encoding = this_offset_encoding
elseif offset_encoding ~= this_offset_encoding then
vim.notify_once(
- 'warning: multiple different client offset_encodings detected for buffer, this is not supported yet',
+ 'warning: multiple different client offset_encodings detected for buffer, vim.lsp.util._get_offset_encoding() uses the offset_encoding from the first client',
vim.log.levels.WARN
)
end
@@ -1919,12 +1925,17 @@ end
--- `textDocument/rangeFormatting`.
---
---@param window integer? window handle or 0 for current, defaults to current
----@param offset_encoding "utf-8"|"utf-16"|"utf-32"? defaults to `offset_encoding` of first client of buffer of `window`
----@return table { textDocument = { uri = `current_file_uri` }, range = { start =
----`current_position`, end = `current_position` } }
+---@param offset_encoding "utf-8"|"utf-16"|"utf-32"
+---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }
function M.make_range_params(window, offset_encoding)
local buf = api.nvim_win_get_buf(window or 0)
- offset_encoding = offset_encoding or M._get_offset_encoding(buf)
+ if offset_encoding == nil then
+ vim.notify_once(
+ 'warning: offset_encoding is required, using the offset_encoding from the first client',
+ vim.log.levels.WARN
+ )
+ offset_encoding = M._get_offset_encoding(buf)
+ end
local position = make_position_param(window, offset_encoding)
return {
textDocument = M.make_text_document_params(buf),
@@ -1940,15 +1951,20 @@ end
---@param end_pos [integer,integer]? {row,col} mark-indexed position.
--- Defaults to the end of the last visual selection.
---@param bufnr integer? buffer handle or 0 for current, defaults to current
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of `bufnr`
----@return table { textDocument = { uri = `current_file_uri` }, range = { start =
----`start_position`, end = `end_position` } }
+---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'
+---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }
function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
validate('start_pos', start_pos, 'table', true)
validate('end_pos', end_pos, 'table', true)
validate('offset_encoding', offset_encoding, 'string', true)
bufnr = bufnr or api.nvim_get_current_buf()
- offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
+ if offset_encoding == nil then
+ vim.notify_once(
+ 'warning: offset_encoding is required, using the offset_encoding from the first client',
+ vim.log.levels.WARN
+ )
+ offset_encoding = M._get_offset_encoding(bufnr)
+ end
--- @type [integer, integer]
local A = { unpack(start_pos or api.nvim_buf_get_mark(bufnr, '<')) }
--- @type [integer, integer]
@@ -2122,7 +2138,7 @@ function M._refresh(method, opts)
local first = vim.fn.line('w0', window)
local last = vim.fn.line('w$', window)
for _, client in ipairs(clients) do
- client.request(method, {
+ client:request(method, {
textDocument = textDocument,
range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding),
}, nil, bufnr)
@@ -2131,7 +2147,7 @@ function M._refresh(method, opts)
end
else
for _, client in ipairs(clients) do
- client.request(method, {
+ client:request(method, {
textDocument = textDocument,
range = make_line_range_params(
bufnr,
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 7bdcdc774a..8ce8652f7d 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -93,9 +93,6 @@ function TSHighlighter.new(tree, opts)
opts = opts or {} ---@type { queries: table<string,string> }
self.tree = tree
tree:register_cbs({
- on_bytes = function(...)
- self:on_bytes(...)
- end,
on_detach = function()
self:on_detach()
end,
@@ -215,13 +212,6 @@ function TSHighlighter:for_each_highlight_state(fn)
end
---@package
----@param start_row integer
----@param new_end integer
-function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end)
- api.nvim__redraw({ buf = self.bufnr, range = { start_row, start_row + new_end + 1 } })
-end
-
----@package
function TSHighlighter:on_detach()
self:destroy()
end
@@ -230,7 +220,7 @@ end
---@param changes Range6[]
function TSHighlighter:on_changedtree(changes)
for _, ch in ipairs(changes) do
- api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 } })
+ api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 }, flush = false })
end
end
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index fd68c2b910..4b42164dc8 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -1037,7 +1037,7 @@ end
--- Registers callbacks for the [LanguageTree].
---@param cbs table<TSCallbackNameOn,function> An [nvim_buf_attach()]-like table argument with the following handlers:
---- - `on_bytes` : see [nvim_buf_attach()], but this will be called _after_ the parsers callback.
+--- - `on_bytes` : see [nvim_buf_attach()].
--- - `on_changedtree` : a callback that will be called every time the tree has syntactical changes.
--- It will be passed two arguments: a table of the ranges (as node ranges) that
--- changed and the changed tree.