From de9ed1ca5478481679df23f894b92b6756be7aed Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 19 Nov 2024 22:29:18 +0100 Subject: vim-patch:9.1.0874: filetype: karel files are not detected Problem: filetype: karel files are not detected Solution: detect '*.kl' files as karel filetype, include syntax and filetype plugin (Kirill Morozov) closes: vim/vim#16075 https://github.com/vim/vim/commit/fdac54d7bbf6d68a8bf741e734b86d0f1998ac86 Co-authored-by: Kirill Morozov Co-authored-by: KnoP-01 --- runtime/lua/vim/filetype.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index e1e73d63fe..9010adf777 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -653,6 +653,8 @@ local extension = { jsp = 'jsp', jl = 'julia', just = 'just', + kl = 'karel', + KL = 'karel', kdl = 'kdl', kv = 'kivy', kix = 'kix', -- cgit From 4c85c217d384c79013401f09b329467c53c8a01c Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 19 Nov 2024 22:30:50 +0100 Subject: vim-patch:9.1.0875: filetype: hyprlang detection can be improved Problem: filetype: hyprlang detection can be improved Solution: detect '/hypr/*.conf' files as hyprlang filetype, include basic syntax highlighting (Luca Saccarola) fixes: vim/vim#15875 closes: vim/vim#16064 https://github.com/vim/vim/commit/a13bd294ab2d9ab38634c9ec51fa76205af6eb62 Co-authored-by: Luca Saccarola --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 9010adf777..c23beb960c 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2214,6 +2214,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', -- cgit From 23ead4f2cb19b05d83f1c515b35bd5896ee77365 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 19 Nov 2024 22:33:30 +0100 Subject: vim-patch:9.1.0876: filetype: openCL files are not recognized Problem: filetype: openCL files are not recognized Solution: detect '*.cl' files as opencl or lisp filetype, include a opencl syntax and filetype plugin (Wu, Zhenyu) closes: vim/vim#15825 https://github.com/vim/vim/commit/e2c27ca8eff7cc8ec852b531d5a7f328a343a761 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 2 +- runtime/lua/vim/filetype/detect.lua | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index c23beb960c..285b7e2328 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -300,6 +300,7 @@ local extension = { cho = 'chordpro', chordpro = 'chordpro', ck = 'chuck', + cl = detect.cl, eni = 'cl', icl = 'clean', cljx = 'clojure', @@ -688,7 +689,6 @@ local extension = { ily = 'lilypond', liquid = 'liquid', liq = 'liquidsoap', - cl = 'lisp', L = 'lisp', lisp = 'lisp', el = 'lisp', 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 @@ -180,6 +180,16 @@ function M.changelog(_, bufnr) return 'changelog' 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') -- cgit From f55c842ec7eabd2e12749411babdcadba47438bc Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 20 Nov 2024 08:51:39 +0100 Subject: vim-patch:9.1.0880: filetype: C3 files are not recognized Problem: filetype: C3 files are not recognized Solution: detect '*.c3*' files as c3 filetype (Turiiya) closes: vim/vim#16087 https://github.com/vim/vim/commit/c8dfcfc53ba5ed69b5d4e534fd7e8694de014e6a Co-authored-by: Turiiya <34311583+ttytm@users.noreply.github.com> --- runtime/lua/vim/filetype.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 285b7e2328..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', -- cgit From 454ae672aad172a299dcff7c33c5e61a3b631c90 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 14 Nov 2024 11:53:20 +0000 Subject: feat(lsp): deprecate non-method client functions Deprecated: - `client.request()` -> `client:request()` - `client.request_sync()` -> `client:request_sync()` - `client.notify()` -> `client:notify()` - `client.cancel_request()` -> `client:cancel_request()` - `client.stop()` -> `client:stop()` - `client.is_stopped()` `client:is_stopped()` - `client.supports_method()` -> `client:supports_method()` - `client.on_attach()` -> `client:on_attach()` Fixed docgen to link class fields to the full function doc. --- runtime/lua/vim/lsp.lua | 54 ++++---- runtime/lua/vim/lsp/_changetracking.lua | 6 +- runtime/lua/vim/lsp/_tagfunc.lua | 2 +- runtime/lua/vim/lsp/_watchfiles.lua | 2 +- runtime/lua/vim/lsp/buf.lua | 30 +++-- runtime/lua/vim/lsp/client.lua | 214 ++++++++++++++------------------ runtime/lua/vim/lsp/codelens.lua | 2 +- runtime/lua/vim/lsp/completion.lua | 6 +- runtime/lua/vim/lsp/inlay_hint.lua | 4 +- runtime/lua/vim/lsp/semantic_tokens.lua | 8 +- runtime/lua/vim/lsp/util.lua | 4 +- 11 files changed, 151 insertions(+), 181 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 0de3b4ee4d..c032d25cb1 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -349,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 @@ -367,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 @@ -485,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, @@ -506,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, }, @@ -527,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 @@ -564,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 @@ -603,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 @@ -662,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 @@ -740,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 @@ -790,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 @@ -812,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 @@ -847,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 @@ -883,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 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 @@ -910,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 @@ -1049,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) @@ -1065,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 --- @field debounce integer debounce duration in ms ---- @field clients table clients using this state. {client_id, client} +--- @field clients table 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/_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 c4cdb5aea8..248969885c 100644 --- a/runtime/lua/vim/lsp/_watchfiles.lua +++ b/runtime/lua/vim/lsp/_watchfiles.lua @@ -116,7 +116,7 @@ function M.register(reg, client_id) 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 diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6383855a30..10c0dbefdc 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -232,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 @@ -568,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)) @@ -584,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 @@ -648,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 @@ -659,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)) @@ -706,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 @@ -781,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 @@ -813,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 @@ -880,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 @@ -1131,8 +1135,8 @@ local function on_code_action_results(results, opts) local action = choice.action local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number') - if not action.edit and client.supports_method(ms.codeAction_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) @@ -1253,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 11ecb87507..3b79da99ea 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -219,70 +219,28 @@ local validate = vim.validate --- @field private registrations table --- @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 +--- @param cls table +--- @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 @@ -499,24 +457,23 @@ function Client.create(config) end, } - 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) - --- @type table 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. @@ -533,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 @@ -616,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. @@ -628,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 @@ -645,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) @@ -662,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 @@ -671,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), @@ -688,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 = { @@ -743,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 @@ -776,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 @@ -814,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 @@ -834,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 @@ -966,12 +924,11 @@ function Client:_get_registration(method, bufnr) end end ---- @private --- 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 @@ -1013,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. @@ -1021,14 +978,14 @@ 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 - self.notify(ms.textDocument_didOpen, { + self:notify(ms.textDocument_didOpen, { textDocument = { version = lsp.util.buf_versions[bufnr], uri = vim.uri_from_bufnr(bufnr), @@ -1049,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) @@ -1085,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 @@ -1101,12 +1067,12 @@ function Client:_supports_method(method, opts) local rmethod = lsp._resolve_to_request[method] if rmethod then if self:_supports_registration(rmethod) then - local reg = self:_get_registration(rmethod, opts and opts.bufnr) + 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, opts and opts.bufnr) ~= nil + return self:_get_registration(method, bufnr) ~= nil end end return false @@ -1207,7 +1173,7 @@ function Client:_add_workspace_folder(dir) local wf = assert(get_workspace_folders(dir)) - self:_notify(ms.workspace_didChangeWorkspaceFolders, { + self:notify(ms.workspace_didChangeWorkspaceFolders, { event = { added = wf, removed = {} }, }) @@ -1222,7 +1188,7 @@ end function Client:_remove_workspace_folder(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/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index f1ae9a8e9e..f1a58de621 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -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, --- }) 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..9e352dbcfd 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2122,7 +2122,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 +2131,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, -- cgit From 629483e24eed3f2c07e55e0540c553361e0345a2 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Thu, 21 Nov 2024 04:19:07 +0800 Subject: feat(lsp): require `offset_encoding` param #31249 Problem: Since [version 3.17](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocuments), LSP supports specifying the position encoding (aka offset encoding) supported by the client through `positionEncoding`. Since #31209, Nvim fully supports `utf-8`, `utf-16`, and `utf-32` encodings. Previously, nvim assumed all clients for a buffer had the same `offset_encoding`, so: * Nvim provides `vim.lsp._get_offset_encoding()` to get `offset_encoding`, but this function is incorrect because `offset_encoding` is per-client, not per-buffer. * Based on the strategy of `vim.lsp._get_offset_encoding()`, `vim.lsp.util.make_position_params()`, `vim.lsp.util.make_range_params()`, and `vim.lsp.util.make_given_range_params()` do not require the caller to pass `offset_encoding`, which is invalid. * https://github.com/neovim/neovim/issues/25272 Solution: * Mark `vim.lsp._get_offset_encoding()` as `@deprecated`. * Change the type annotations of `vim.lsp.util.make_position_params()`, `vim.lsp.util.make_range_params()`, `vim.lsp.util.make_given_range_params()` to require the `offset_encoding` param. --- runtime/lua/vim/lsp/util.lua | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 9e352dbcfd..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] -- cgit From cedf155fb5c4d687747e59f5d3fdad76a40aa069 Mon Sep 17 00:00:00 2001 From: zshuzh <40901142+zshuzh@users.noreply.github.com> Date: Wed, 20 Nov 2024 22:01:59 +0000 Subject: refactor(options): impl default 'titlestring' by format flags #30843 Problem: Unnecessary C impl of default 'titlestring'. Solutin: Define it using format flags. --- runtime/lua/vim/_meta/options.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime/lua/vim') 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: -- cgit From 7d8db544417e7811ae6e3c5398e9d0481a0ada22 Mon Sep 17 00:00:00 2001 From: glepnir Date: Fri, 22 Nov 2024 15:34:49 +0800 Subject: fix(lsp): delete b:lsp_floating_preview buf var after win close Problem: After floating preview window closed the buf var still exist Solution: delete after floating window closed. --- runtime/lua/vim/lsp/util.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index cfa8a194d9..24837c3b44 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1344,6 +1344,10 @@ local function close_preview_window(winnr, bufnrs) local augroup = 'preview_window_' .. winnr pcall(api.nvim_del_augroup_by_name, augroup) + local buf = vim.w[winnr].buf_hold_win + if buf and api.nvim_buf_is_valid(buf) then + vim.b[buf].lsp_floating_window = nil + end pcall(api.nvim_win_close, winnr, true) end) end @@ -1609,6 +1613,7 @@ function M.open_floating_preview(contents, syntax, opts) { silent = true, noremap = true, nowait = true } ) close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr }) + vim.w[floating_winnr].buf_hold_win = bufnr -- save focus_id if opts.focus_id then -- cgit From 9a681ad09e2add96d47bf3f39cca8029f3bf09df Mon Sep 17 00:00:00 2001 From: andrew snelling <72226000+snelling-a@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:51:30 +0100 Subject: fix(lsp): hover keymap (#31208) * fix: use function call in keymap * fix: test --- runtime/lua/vim/lsp.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index c032d25cb1..6d29c9e4df 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -371,7 +371,9 @@ function lsp._set_defaults(client, bufnr) 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()' }) + vim.keymap.set('n', 'K', function() + vim.lsp.buf.hover() + end, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' }) end end) if client:supports_method(ms.textDocument_diagnostic) then -- cgit From 2a1f604c77a161f076f7d520d66fc6f051b625e7 Mon Sep 17 00:00:00 2001 From: glepnir Date: Sat, 23 Nov 2024 19:11:30 +0800 Subject: fix(lsp): delete bufvar inside WinClosed event Problem: floaing preview window can be closed by some ex commands like `only` `fclose` which will not clean the bufvar Solution: use WinClosed event with floating_winnr for clean bufnr, and add test cases for vim.lsp.util.open_floating_preview --- runtime/lua/vim/lsp/util.lua | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 24837c3b44..0f608ffbd1 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1344,10 +1344,6 @@ local function close_preview_window(winnr, bufnrs) local augroup = 'preview_window_' .. winnr pcall(api.nvim_del_augroup_by_name, augroup) - local buf = vim.w[winnr].buf_hold_win - if buf and api.nvim_buf_is_valid(buf) then - vim.b[buf].lsp_floating_window = nil - end pcall(api.nvim_win_close, winnr, true) end) end @@ -1613,7 +1609,6 @@ function M.open_floating_preview(contents, syntax, opts) { silent = true, noremap = true, nowait = true } ) close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr }) - vim.w[floating_winnr].buf_hold_win = bufnr -- save focus_id if opts.focus_id then @@ -1622,6 +1617,22 @@ function M.open_floating_preview(contents, syntax, opts) api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) end + local augroup_name = ('closing_floating_preview_%d'):format(floating_winnr) + local ok = + pcall(api.nvim_get_autocmds, { group = augroup_name, pattern = tostring(floating_winnr) }) + if not ok then + api.nvim_create_autocmd('WinClosed', { + group = api.nvim_create_augroup(augroup_name, {}), + pattern = tostring(floating_winnr), + callback = function() + if api.nvim_buf_is_valid(bufnr) then + vim.b[bufnr].lsp_floating_preview = nil + end + api.nvim_del_augroup_by_name(augroup_name) + end, + }) + end + if do_stylize then vim.wo[floating_winnr].conceallevel = 2 end -- cgit From 5c603064421b8829cf106c845902fcc41d3e31f2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 24 Nov 2024 22:36:33 +0800 Subject: vim-patch:9.1.0883: message history cleanup is missing some tests (#31331) Problem: message history cleanup is missing some tests Solution: Add tests, refactor common code into did_set_msghistory() (Shougo Matsushita) closes: vim/vim#16078 https://github.com/vim/vim/commit/9f860a14c308f7a9a27a6850d36790615717a710 Co-authored-by: Shougo Matsushita Co-authored-by: Milly --- runtime/lua/vim/_meta/options.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index e485009ca2..c635d9bd3b 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -4381,6 +4381,7 @@ vim.go.mouset = vim.go.mousetime --- Determines how many entries are remembered in the `:messages` history. --- The maximum value is 10000. +--- Setting it to zero clears the message history. --- --- @type integer vim.o.msghistory = 500 -- cgit From ff00d583af8f1ce21a508d701d09c54d2621b16e Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 24 Nov 2024 15:05:48 +0100 Subject: vim-patch:9.1.0886: filetype: debian control file not detected Problem: filetype: debian control file not detected Solution: detect 'debian/control' files as debcontrol filetype (author) closes: vim/vim#16067 https://github.com/vim/vim/commit/57b947e3c3b8d52b914158979263855d785445d5 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 2 ++ runtime/lua/vim/filetype/detect.lua | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 4383c0983e..aa566973b6 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1490,6 +1490,7 @@ local filename = { ['NEWS.dch'] = 'debchangelog', ['NEWS.Debian'] = 'debchangelog', ['/debian/control'] = 'debcontrol', + ['/DEBIAN/control'] = 'debcontrol', ['/debian/copyright'] = 'debcopyright', ['/etc/apt/sources.list'] = 'debsources', ['denyhosts.conf'] = 'denyhosts', @@ -2319,6 +2320,7 @@ local pattern = { ['%.cmake%.in$'] = 'cmake', ['^crontab%.'] = starsetf('crontab'), ['^cvs%d+$'] = 'cvs', + ['/DEBIAN/control$'] = 'debcontrol', ['^php%.ini%-'] = 'dosini', ['^php%-fpm%.conf'] = 'dosini', ['^www%.conf'] = 'dosini', diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 81b94c69db..4f2fef5b1f 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -237,7 +237,8 @@ end --- Debian Control --- @type vim.filetype.mapfn function M.control(_, bufnr) - if getline(bufnr, 1):find('^Source:') then + local line1 = getline(bufnr, 1) + if line1 and findany(line1, { '^Source:', '^Package:' }) then return 'debcontrol' end end -- cgit From 165b099fa38c5f4a9855cda3d13575bf63767647 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Tue, 26 Nov 2024 00:06:05 +0800 Subject: refactor(lsp): rename `offset_encoding` to `position_encoding` #31286 Problem: LSP spec uses the term "position encoding" where we say "offset encoding". Solution: - Rename it everywhere except `vim.lsp.Client.offset_encoding` (which would be breaking). - Mention "position encoding" in the documentation for `vim.lsp.Client.offset_encoding`. --- runtime/lua/vim/lsp/_changetracking.lua | 10 +-- runtime/lua/vim/lsp/_tagfunc.lua | 16 ++-- runtime/lua/vim/lsp/buf.lua | 8 +- runtime/lua/vim/lsp/client.lua | 11 ++- runtime/lua/vim/lsp/completion.lua | 6 +- runtime/lua/vim/lsp/diagnostic.lua | 6 +- runtime/lua/vim/lsp/sync.lua | 52 ++++++------ runtime/lua/vim/lsp/util.lua | 140 +++++++++++++++++--------------- 8 files changed, 130 insertions(+), 119 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/_changetracking.lua b/runtime/lua/vim/lsp/_changetracking.lua index 8588502697..c2ff66b90e 100644 --- a/runtime/lua/vim/lsp/_changetracking.lua +++ b/runtime/lua/vim/lsp/_changetracking.lua @@ -18,14 +18,14 @@ local M = {} --- --- None: One group for all clients --- Full: One group for all clients ---- Incremental: One group per `offset_encoding` +--- Incremental: One group per `position_encoding` --- --- Sending changes can be debounced per buffer. To simplify the implementation the --- smallest debounce interval is used and we don't group clients by different intervals. --- --- @class vim.lsp.CTGroup --- @field sync_kind integer TextDocumentSyncKind, considers config.flags.allow_incremental_sync ---- @field offset_encoding "utf-8"|"utf-16"|"utf-32" +--- @field position_encoding "utf-8"|"utf-16"|"utf-32" --- --- @class vim.lsp.CTBufferState --- @field name string name of the buffer @@ -46,7 +46,7 @@ local M = {} ---@return string local function group_key(group) if group.sync_kind == protocol.TextDocumentSyncKind.Incremental then - return tostring(group.sync_kind) .. '\0' .. group.offset_encoding + return tostring(group.sync_kind) .. '\0' .. group.position_encoding end return tostring(group.sync_kind) end @@ -72,7 +72,7 @@ local function get_group(client) end return { sync_kind = sync_kind, - offset_encoding = client.offset_encoding, + position_encoding = client.offset_encoding, } end @@ -310,7 +310,7 @@ local function send_changes_for_group(bufnr, firstline, lastline, new_lastline, -- The contents would further change and startline/endline may no longer fit local changes = incremental_changes( buf_state, - group.offset_encoding, + group.position_encoding, bufnr, firstline, lastline, diff --git a/runtime/lua/vim/lsp/_tagfunc.lua b/runtime/lua/vim/lsp/_tagfunc.lua index f6ffc63824..554f0cb991 100644 --- a/runtime/lua/vim/lsp/_tagfunc.lua +++ b/runtime/lua/vim/lsp/_tagfunc.lua @@ -6,12 +6,12 @@ local ms = lsp.protocol.Methods ---@param name string ---@param range lsp.Range ---@param uri string ----@param offset_encoding string +---@param position_encoding string ---@return {name: string, filename: string, cmd: string, kind?: string} -local function mk_tag_item(name, range, uri, offset_encoding) +local function mk_tag_item(name, range, uri, position_encoding) local bufnr = vim.uri_to_bufnr(uri) -- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position - local byte = util._get_line_byte_from_position(bufnr, range.start, offset_encoding) + 1 + local byte = util._get_line_byte_from_position(bufnr, range.start, position_encoding) + 1 return { name = name, filename = vim.uri_to_fname(uri), @@ -32,9 +32,9 @@ local function query_definition(pattern) --- @param range lsp.Range --- @param uri string - --- @param offset_encoding string - local add = function(range, uri, offset_encoding) - table.insert(results, mk_tag_item(pattern, range, uri, offset_encoding)) + --- @param position_encoding string + local add = function(range, uri, position_encoding) + table.insert(results, mk_tag_item(pattern, range, uri, position_encoding)) end local remaining = #clients @@ -78,11 +78,11 @@ local function query_workspace_symbols(pattern) local results = {} for client_id, responses in pairs(assert(results_by_client)) do local client = lsp.get_client_by_id(client_id) - local offset_encoding = client and client.offset_encoding or 'utf-16' + local position_encoding = client and client.offset_encoding or 'utf-16' local symbols = responses.result --[[@as lsp.SymbolInformation[]|nil]] for _, symbol in pairs(symbols or {}) do local loc = symbol.location - local item = mk_tag_item(symbol.name, loc.range, loc.uri, offset_encoding) + local item = mk_tag_item(symbol.name, loc.range, loc.uri, position_encoding) item.kind = lsp.protocol.SymbolKind[symbol.kind] or 'Unknown' table.insert(results, item) end diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 10c0dbefdc..aca6bf27f4 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -638,14 +638,14 @@ function M.rename(new_name, opts) local cword = vim.fn.expand('') --- @param range lsp.Range - --- @param offset_encoding string - local function get_text_at_range(range, offset_encoding) + --- @param position_encoding string + local function get_text_at_range(range, position_encoding) return api.nvim_buf_get_text( bufnr, range.start.line, - util._get_line_byte_from_position(bufnr, range.start, offset_encoding), + util._get_line_byte_from_position(bufnr, range.start, position_encoding), range['end'].line, - util._get_line_byte_from_position(bufnr, range['end'], offset_encoding), + util._get_line_byte_from_position(bufnr, range['end'], position_encoding), {} )[1] end diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 3b79da99ea..7c2b7192f5 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -94,7 +94,8 @@ local validate = vim.validate --- 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. +--- Called "position encoding" in LSP spec, the encoding that the LSP server expects. +--- Client does not verify this is correct. --- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32' --- --- Callback invoked when the client operation throws an error. `code` is a number describing the error. @@ -148,8 +149,10 @@ local validate = vim.validate --- See |vim.lsp.rpc.start()|. --- @field rpc vim.lsp.rpc.PublicClient --- ---- The encoding used for communicating with the server. You can modify this in ---- the `config`'s `on_init` method before text is sent to the server. +--- Called "position encoding" in LSP spec, +--- the encoding used for communicating with the server. +--- You can modify this in the `config`'s `on_init` method +--- before text is sent to the server. --- @field offset_encoding string --- --- The handlers used by the client as described in |lsp-handler|. @@ -278,7 +281,7 @@ local function validate_encoding(encoding) return valid_encodings[encoding:lower()] or error( string.format( - "Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", + "Invalid position encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding ) ) diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 0f388a88fd..1e950f625e 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -550,7 +550,7 @@ local function on_complete_done() return end - local offset_encoding = client.offset_encoding or 'utf-16' + local position_encoding = client.offset_encoding or 'utf-16' local resolve_provider = (client.server_capabilities.completionProvider or {}).resolveProvider local function clear_word() @@ -576,7 +576,7 @@ local function on_complete_done() if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then clear_word() - lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, offset_encoding) + lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, position_encoding) apply_snippet_and_command() elseif resolve_provider and type(completion_item) == 'table' then local changedtick = vim.b[bufnr].changedtick @@ -591,7 +591,7 @@ local function on_complete_done() if err then vim.notify_once(err.message, vim.log.levels.WARN) elseif result and result.additionalTextEdits then - lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, offset_encoding) + lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, position_encoding) if result.command then completion_item.command = result.command end diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 8fd30c7668..12984f8c26 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -77,7 +77,7 @@ end local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) local buf_lines = get_buf_lines(bufnr) local client = vim.lsp.get_client_by_id(client_id) - local offset_encoding = client and client.offset_encoding or 'utf-16' + local position_encoding = client and client.offset_encoding or 'utf-16' --- @param diagnostic lsp.Diagnostic --- @return vim.Diagnostic return vim.tbl_map(function(diagnostic) @@ -95,9 +95,9 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) --- @type vim.Diagnostic return { lnum = start.line, - col = vim.str_byteindex(line, offset_encoding, start.character, false), + col = vim.str_byteindex(line, position_encoding, start.character, false), end_lnum = _end.line, - end_col = vim.str_byteindex(line, offset_encoding, _end.character, false), + end_col = vim.str_byteindex(line, position_encoding, _end.character, false), severity = severity_lsp_to_vim(diagnostic.severity), message = message, source = diagnostic.source, diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 3df45ebff0..621f63b25f 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -48,21 +48,21 @@ local str_utfindex = vim.str_utfindex local str_utf_start = vim.str_utf_start local str_utf_end = vim.str_utf_end --- Given a line, byte idx, alignment, and offset_encoding convert to the aligned +-- Given a line, byte idx, alignment, and position_encoding convert to the aligned -- utf-8 index and either the utf-16, or utf-32 index. ---@param line string the line to index into ---@param byte integer the byte idx ----@param offset_encoding string utf-8|utf-16|utf-32|nil (default: utf-8) +---@param position_encoding string utf-8|utf-16|utf-32|nil (default: utf-8) ---@return integer byte_idx of first change position ---@return integer char_idx of first change position -local function align_end_position(line, byte, offset_encoding) +local function align_end_position(line, byte, position_encoding) local char --- @type integer -- If on the first byte, or an empty string: the trivial case if byte == 1 or #line == 0 then char = byte -- Called in the case of extending an empty line "" -> "a" elseif byte == #line + 1 then - char = str_utfindex(line, offset_encoding) + 1 + char = str_utfindex(line, position_encoding) + 1 else -- Modifying line, find the nearest utf codepoint local offset = str_utf_start(line, byte) @@ -73,9 +73,9 @@ local function align_end_position(line, byte, offset_encoding) end if byte <= #line then --- Convert to 0 based for input, and from 0 based for output - char = str_utfindex(line, offset_encoding, byte - 1) + 1 + char = str_utfindex(line, position_encoding, byte - 1) + 1 else - char = str_utfindex(line, offset_encoding) + 1 + char = str_utfindex(line, position_encoding) + 1 end -- Extending line, find the nearest utf codepoint for the last valid character end @@ -93,7 +93,7 @@ end ---@param firstline integer firstline from on_lines, adjusted to 1-index ---@param lastline integer lastline from on_lines, adjusted to 1-index ---@param new_lastline integer new_lastline from on_lines, adjusted to 1-index ----@param offset_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8) +---@param position_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8) ---@return vim.lsp.sync.Range result table include line_idx, byte_idx, and char_idx of first change position local function compute_start_range( prev_lines, @@ -101,7 +101,7 @@ local function compute_start_range( firstline, lastline, new_lastline, - offset_encoding + position_encoding ) local char_idx --- @type integer? local byte_idx --- @type integer? @@ -115,7 +115,7 @@ local function compute_start_range( if line then line_idx = firstline - 1 byte_idx = #line + 1 - char_idx = str_utfindex(line, offset_encoding) + 1 + char_idx = str_utfindex(line, position_encoding) + 1 else line_idx = firstline byte_idx = 1 @@ -152,11 +152,11 @@ local function compute_start_range( char_idx = 1 elseif start_byte_idx == #prev_line + 1 then byte_idx = start_byte_idx - char_idx = str_utfindex(prev_line, offset_encoding) + 1 + char_idx = str_utfindex(prev_line, position_encoding) + 1 else byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx) --- Convert to 0 based for input, and from 0 based for output - char_idx = vim.str_utfindex(prev_line, offset_encoding, byte_idx - 1) + 1 + char_idx = vim.str_utfindex(prev_line, position_encoding, byte_idx - 1) + 1 end -- Return the start difference (shared for new and prev lines) @@ -174,7 +174,7 @@ end ---@param firstline integer ---@param lastline integer ---@param new_lastline integer ----@param offset_encoding string +---@param position_encoding string ---@return vim.lsp.sync.Range prev_end_range ---@return vim.lsp.sync.Range curr_end_range local function compute_end_range( @@ -184,7 +184,7 @@ local function compute_end_range( firstline, lastline, new_lastline, - offset_encoding + position_encoding ) -- A special case for the following `firstline == new_lastline` case where lines are deleted. -- Even if the buffer has become empty, nvim behaves as if it has an empty line with eol. @@ -193,7 +193,7 @@ local function compute_end_range( return { line_idx = lastline - 1, byte_idx = #prev_line + 1, - char_idx = str_utfindex(prev_line, offset_encoding) + 1, + char_idx = str_utfindex(prev_line, position_encoding) + 1, }, { line_idx = 1, byte_idx = 1, char_idx = 1 } end -- If firstline == new_lastline, the first change occurred on a line that was deleted. @@ -259,7 +259,7 @@ local function compute_end_range( prev_end_byte_idx = 1 end local prev_byte_idx, prev_char_idx = - align_end_position(prev_line, prev_end_byte_idx, offset_encoding) + align_end_position(prev_line, prev_end_byte_idx, position_encoding) local prev_end_range = { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx } @@ -274,7 +274,7 @@ local function compute_end_range( curr_end_byte_idx = 1 end local curr_byte_idx, curr_char_idx = - align_end_position(curr_line, curr_end_byte_idx, offset_encoding) + align_end_position(curr_line, curr_end_byte_idx, position_encoding) curr_end_range = { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx } end @@ -317,7 +317,7 @@ local function extract_text(lines, start_range, end_range, line_ending) end end --- rangelength depends on the offset encoding +-- rangelength depends on the position encoding -- bytes for utf-8 (clangd with extension) -- codepoints for utf-16 -- codeunits for utf-32 @@ -326,10 +326,10 @@ end ---@param lines string[] ---@param start_range vim.lsp.sync.Range ---@param end_range vim.lsp.sync.Range ----@param offset_encoding string +---@param position_encoding string ---@param line_ending string ---@return integer -local function compute_range_length(lines, start_range, end_range, offset_encoding, line_ending) +local function compute_range_length(lines, start_range, end_range, position_encoding, line_ending) local line_ending_length = #line_ending -- Single line case if start_range.line_idx == end_range.line_idx then @@ -339,7 +339,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi local start_line = lines[start_range.line_idx] local range_length --- @type integer if start_line and #start_line > 0 then - range_length = str_utfindex(start_line, offset_encoding) + range_length = str_utfindex(start_line, position_encoding) - start_range.char_idx + 1 + line_ending_length @@ -352,7 +352,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi for idx = start_range.line_idx + 1, end_range.line_idx - 1 do -- Length full line plus newline character if #lines[idx] > 0 then - range_length = range_length + str_utfindex(lines[idx], offset_encoding) + #line_ending + range_length = range_length + str_utfindex(lines[idx], position_encoding) + #line_ending else range_length = range_length + line_ending_length end @@ -372,7 +372,7 @@ end ---@param firstline integer line to begin search for first difference ---@param lastline integer line to begin search in old_lines for last difference ---@param new_lastline integer line to begin search in new_lines for last difference ----@param offset_encoding string encoding requested by language server +---@param position_encoding string encoding requested by language server ---@param line_ending string ---@return lsp.TextDocumentContentChangeEvent : see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent function M.compute_diff( @@ -381,7 +381,7 @@ function M.compute_diff( firstline, lastline, new_lastline, - offset_encoding, + position_encoding, line_ending ) -- Find the start of changes between the previous and current buffer. Common between both. @@ -393,7 +393,7 @@ function M.compute_diff( firstline + 1, lastline + 1, new_lastline + 1, - offset_encoding + position_encoding ) -- Find the last position changed in the previous and current buffer. -- prev_end_range is sent to the server as as the end of the changed range. @@ -405,7 +405,7 @@ function M.compute_diff( firstline + 1, lastline + 1, new_lastline + 1, - offset_encoding + position_encoding ) -- Grab the changed text of from start_range to curr_end_range in the current buffer. @@ -414,7 +414,7 @@ function M.compute_diff( -- Compute the range of the replaced text. Deprecated but still required for certain language servers local range_length = - compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending) + compute_range_length(prev_lines, start_range, prev_end_range, position_encoding, line_ending) -- convert to 0 based indexing local result = { diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 0f608ffbd1..3de76f93a6 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -277,9 +277,9 @@ end --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position ---@param position lsp.Position ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@return integer -local function get_line_byte_from_position(bufnr, position, offset_encoding) +local function get_line_byte_from_position(bufnr, position, position_encoding) -- LSP's line and characters are 0-indexed -- Vim's line and columns are 1-indexed local col = position.character @@ -287,7 +287,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- character if col > 0 then local line = get_line(bufnr, position.line) or '' - return vim.str_byteindex(line, offset_encoding, col, false) + return vim.str_byteindex(line, position_encoding, col, false) end return col end @@ -295,12 +295,12 @@ end --- Applies a list of text edits to a buffer. ---@param text_edits lsp.TextEdit[] ---@param bufnr integer Buffer id ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit -function M.apply_text_edits(text_edits, bufnr, offset_encoding) +function M.apply_text_edits(text_edits, bufnr, position_encoding) validate('text_edits', text_edits, 'table', false) validate('bufnr', bufnr, 'number', false) - validate('offset_encoding', offset_encoding, 'string', false) + validate('position_encoding', position_encoding, 'string', false) if not next(text_edits) then return @@ -359,9 +359,9 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Convert from LSP style ranges to Neovim style ranges. local start_row = text_edit.range.start.line - local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding) + local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, position_encoding) local end_row = text_edit.range['end'].line - local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding) + local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], position_encoding) local text = vim.split(text_edit.newText, '\n', { plain = true }) local max = api.nvim_buf_line_count(bufnr) @@ -430,14 +430,14 @@ end --- ---@param text_document_edit lsp.TextDocumentEdit ---@param index? integer: Optional index of the edit, if from a list of edits (or nil, if not from a list) ----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding? 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit -function M.apply_text_document_edit(text_document_edit, index, offset_encoding) +function M.apply_text_document_edit(text_document_edit, index, position_encoding) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) - if offset_encoding == nil then + if position_encoding == nil then vim.notify_once( - 'apply_text_document_edit must be called with valid offset encoding', + 'apply_text_document_edit must be called with valid position encoding', vim.log.levels.WARN ) return @@ -459,7 +459,7 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) return end - M.apply_text_edits(text_document_edit.edits, bufnr, offset_encoding) + M.apply_text_edits(text_document_edit.edits, bufnr, position_encoding) end local function path_components(path) @@ -619,12 +619,12 @@ end --- Applies a `WorkspaceEdit`. --- ---@param workspace_edit lsp.WorkspaceEdit ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' (required) +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' (required) ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit -function M.apply_workspace_edit(workspace_edit, offset_encoding) - if offset_encoding == nil then +function M.apply_workspace_edit(workspace_edit, position_encoding) + if position_encoding == nil then vim.notify_once( - 'apply_workspace_edit must be called with valid offset encoding', + 'apply_workspace_edit must be called with valid position encoding', vim.log.levels.WARN ) return @@ -641,7 +641,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) elseif change.kind then --- @diagnostic disable-line:undefined-field error(string.format('Unsupported change: %q', vim.inspect(change))) else - M.apply_text_document_edit(change, idx, offset_encoding) + M.apply_text_document_edit(change, idx, position_encoding) end end return @@ -654,7 +654,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) for uri, changes in pairs(all_changes) do local bufnr = vim.uri_to_bufnr(uri) - M.apply_text_edits(changes, bufnr, offset_encoding) + M.apply_text_edits(changes, bufnr, position_encoding) end end @@ -904,17 +904,20 @@ end --- Shows document and optionally jumps to the location. --- ---@param location lsp.Location|lsp.LocationLink ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? +---@param position_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param opts? vim.lsp.util.show_document.Opts ---@return boolean `true` if succeeded -function M.show_document(location, offset_encoding, opts) +function M.show_document(location, position_encoding, opts) -- location may be Location or LocationLink local uri = location.uri or location.targetUri if uri == nil then return false end - if offset_encoding == nil then - vim.notify_once('show_document must be called with valid offset encoding', vim.log.levels.WARN) + if position_encoding == nil then + vim.notify_once( + 'show_document must be called with valid position encoding', + vim.log.levels.WARN + ) return false end local bufnr = vim.uri_to_bufnr(uri) @@ -946,7 +949,7 @@ function M.show_document(location, offset_encoding, opts) if range then -- Jump to new location (adjusting for encoding of characters) local row = range.start.line - local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) + local col = get_line_byte_from_position(bufnr, range.start, position_encoding) api.nvim_win_set_cursor(win, { row + 1, col }) vim._with({ win = win }, function() -- Open folds under the cursor @@ -961,12 +964,12 @@ end --- ---@deprecated use `vim.lsp.util.show_document` with `{focus=true}` instead ---@param location lsp.Location|lsp.LocationLink ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? +---@param position_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param reuse_win boolean? Jump to existing window if buffer is already open. ---@return boolean `true` if the jump succeeded -function M.jump_to_location(location, offset_encoding, reuse_win) +function M.jump_to_location(location, position_encoding, reuse_win) vim.deprecate('vim.lsp.util.jump_to_location', nil, '0.12') - return M.show_document(location, offset_encoding, { reuse_win = reuse_win, focus = true }) + return M.show_document(location, position_encoding, { reuse_win = reuse_win, focus = true }) end --- Previews a location in a floating window @@ -1661,18 +1664,18 @@ do --[[ References ]] --- ---@param bufnr integer Buffer id ---@param references lsp.DocumentHighlight[] objects to highlight - ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' + ---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent - function M.buf_highlight_references(bufnr, references, offset_encoding) + function M.buf_highlight_references(bufnr, references, position_encoding) validate('bufnr', bufnr, 'number', true) - validate('offset_encoding', offset_encoding, 'string', false) + validate('position_encoding', position_encoding, 'string', false) for _, reference in ipairs(references) do local range = reference.range local start_line = range.start.line local end_line = range['end'].line - local start_idx = get_line_byte_from_position(bufnr, range.start, offset_encoding) - local end_idx = get_line_byte_from_position(bufnr, range['end'], offset_encoding) + local start_idx = get_line_byte_from_position(bufnr, range.start, position_encoding) + local end_idx = get_line_byte_from_position(bufnr, range['end'], position_encoding) local document_highlight_kind = { [protocol.DocumentHighlightKind.Text] = 'LspReferenceText', @@ -1706,16 +1709,16 @@ end) --- |setloclist()|. --- ---@param locations lsp.Location[]|lsp.LocationLink[] ----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding? 'utf-8'|'utf-16'|'utf-32' --- default to first client of buffer ---@return vim.quickfix.entry[] # See |setqflist()| for the format -function M.locations_to_items(locations, offset_encoding) - if offset_encoding == nil then +function M.locations_to_items(locations, position_encoding) + if position_encoding == nil then vim.notify_once( - 'locations_to_items must be called with valid offset encoding', + 'locations_to_items must be called with valid position encoding', vim.log.levels.WARN ) - offset_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding + position_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding end local items = {} --- @type vim.quickfix.entry[] @@ -1752,8 +1755,8 @@ function M.locations_to_items(locations, offset_encoding) local end_row = end_pos.line local line = lines[row] or '' local end_line = lines[end_row] or '' - local col = vim.str_byteindex(line, offset_encoding, pos.character, false) - local end_col = vim.str_byteindex(end_line, offset_encoding, end_pos.character, false) + local col = vim.str_byteindex(line, position_encoding, pos.character, false) + local end_col = vim.str_byteindex(end_line, position_encoding, end_pos.character, false) items[#items + 1] = { filename = filename, @@ -1864,8 +1867,8 @@ 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' -local function make_position_param(window, offset_encoding) +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' +local function make_position_param(window, position_encoding) window = window or 0 local buf = api.nvim_win_get_buf(window) local row, col = unpack(api.nvim_win_get_cursor(window)) @@ -1875,7 +1878,7 @@ local function make_position_param(window, offset_encoding) return { line = 0, character = 0 } end - col = vim.str_utfindex(line, offset_encoding, col, false) + col = vim.str_utfindex(line, position_encoding, col, false) return { line = row, character = col } end @@ -1883,22 +1886,22 @@ 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' +---@param position_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) +function M.make_position_params(window, position_encoding) window = window or 0 local buf = api.nvim_win_get_buf(window) - if offset_encoding == nil then + if position_encoding == nil then vim.notify_once( - 'warning: offset_encoding is required, using the offset_encoding from the first client', + 'warning: position_encoding is required, using the offset_encoding from the first client', vim.log.levels.WARN ) - offset_encoding = M._get_offset_encoding(buf) + position_encoding = M._get_offset_encoding(buf) end return { textDocument = M.make_text_document_params(buf), - position = make_position_param(window, offset_encoding), + position = make_position_param(window, position_encoding), } end @@ -1941,18 +1944,18 @@ end --- `textDocument/rangeFormatting`. --- ---@param window integer? window handle or 0 for current, defaults to current ----@param offset_encoding "utf-8"|"utf-16"|"utf-32" +---@param position_encoding "utf-8"|"utf-16"|"utf-32" ---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range } -function M.make_range_params(window, offset_encoding) +function M.make_range_params(window, position_encoding) local buf = api.nvim_win_get_buf(window or 0) - if offset_encoding == nil then + if position_encoding == nil then vim.notify_once( - 'warning: offset_encoding is required, using the offset_encoding from the first client', + 'warning: position_encoding is required, using the offset_encoding from the first client', vim.log.levels.WARN ) - offset_encoding = M._get_offset_encoding(buf) + position_encoding = M._get_offset_encoding(buf) end - local position = make_position_param(window, offset_encoding) + local position = make_position_param(window, position_encoding) return { textDocument = M.make_text_document_params(buf), range = { start = position, ['end'] = position }, @@ -1967,19 +1970,19 @@ 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' +---@param position_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) +function M.make_given_range_params(start_pos, end_pos, bufnr, position_encoding) validate('start_pos', start_pos, 'table', true) validate('end_pos', end_pos, 'table', true) - validate('offset_encoding', offset_encoding, 'string', true) + validate('position_encoding', position_encoding, 'string', true) bufnr = bufnr or api.nvim_get_current_buf() - if offset_encoding == nil then + if position_encoding == nil then vim.notify_once( - 'warning: offset_encoding is required, using the offset_encoding from the first client', + 'warning: position_encoding is required, using the offset_encoding from the first client', vim.log.levels.WARN ) - offset_encoding = M._get_offset_encoding(bufnr) + position_encoding = M._get_offset_encoding(bufnr) end --- @type [integer, integer] local A = { unpack(start_pos or api.nvim_buf_get_mark(bufnr, '<')) } @@ -1988,12 +1991,12 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) -- convert to 0-index A[1] = A[1] - 1 B[1] = B[1] - 1 - -- account for offset_encoding. + -- account for position_encoding. if A[2] > 0 then - A[2] = M.character_offset(bufnr, A[1], A[2], offset_encoding) + A[2] = M.character_offset(bufnr, A[1], A[2], position_encoding) end if B[2] > 0 then - B[2] = M.character_offset(bufnr, B[1], B[2], offset_encoding) + B[2] = M.character_offset(bufnr, B[1], B[2], position_encoding) end -- we need to offset the end character position otherwise we loose the last -- character of the selection, as LSP end position is exclusive @@ -2100,9 +2103,9 @@ end ---@param bufnr integer ---@param start_line integer ---@param end_line integer ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@return lsp.Range -local function make_line_range_params(bufnr, start_line, end_line, offset_encoding) +local function make_line_range_params(bufnr, start_line, end_line, position_encoding) local last_line = api.nvim_buf_line_count(bufnr) - 1 ---@type lsp.Position @@ -2111,7 +2114,12 @@ local function make_line_range_params(bufnr, start_line, end_line, offset_encodi if end_line == last_line and not vim.bo[bufnr].endofline then end_pos = { line = end_line, - character = M.character_offset(bufnr, end_line, #get_line(bufnr, end_line), offset_encoding), + character = M.character_offset( + bufnr, + end_line, + #get_line(bufnr, end_line), + position_encoding + ), } else end_pos = { line = end_line + 1, character = 0 } -- cgit From 99e7323aa386865035ad79483a7da0c5b106464f Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 25 Nov 2024 17:11:22 +0100 Subject: feat(diagnostic): inherit parent 'path' option in open_float (#31273) Diagnostics sometimes contain stack traces, it's useful being able to use `CTRL-W F` on them to open related files from within the diagnostic window. --- runtime/lua/vim/diagnostic.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 4fb8c6a686..2de996feeb 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1962,6 +1962,7 @@ function M.open_float(opts, ...) opts.focus_id = scope end local float_bufnr, winnr = vim.lsp.util.open_floating_preview(lines, 'plaintext', opts) + vim.bo[float_bufnr].path = vim.bo[bufnr].path for i, hl in ipairs(highlights) do local line = lines[i] local prefix_len = hl.prefix and hl.prefix.length or 0 -- cgit From 8d55cc218cfed54136677398ca76c45987b15f29 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 24 Nov 2024 10:40:56 +0000 Subject: feat(keysets): teach Union and LuaRefOf --- runtime/lua/vim/_meta/api.lua | 2 +- runtime/lua/vim/_meta/api_keysets.lua | 100 ++++++++++++++-------------- runtime/lua/vim/_meta/api_keysets_extra.lua | 45 +++++++++++++ 3 files changed, 96 insertions(+), 51 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 3c9b9d4f44..acd12b353d 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1012,7 +1012,7 @@ function vim.api.nvim_create_namespace(name) end --- ``` --- --- @param name string Name of the new user command. Must begin with an uppercase letter. ---- @param command any Replacement command to execute when this user command is executed. When called +--- @param command string|fun(args: vim.api.keyset.create_user_command.command_args) Replacement command to execute when this user command is executed. When called --- from Lua, the command can also be a Lua function. The function is called with a --- single table argument that contains the following keys: --- - name: (string) Command name diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index bf184dee2d..e11dddb2d3 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -4,11 +4,11 @@ error('Cannot require a meta file') --- @class vim.api.keyset.buf_attach ---- @field on_lines? function ---- @field on_bytes? function ---- @field on_changedtick? function ---- @field on_detach? function ---- @field on_reload? function +--- @field on_lines? fun(_: "lines", bufnr: integer, changedtick: integer, first: integer, last_old: integer, last_new: integer, byte_count: integer, deleted_codepoints?: integer, deleted_codeunits?: integer): boolean? +--- @field on_bytes? fun(_: "bytes", bufnr: integer, changedtick: integer, start_row: integer, start_col: integer, start_byte: integer, old_end_row: integer, old_end_col: integer, old_end_byte: integer, new_end_row: integer, new_end_col: integer, new_end_byte: integer): boolean? +--- @field on_changedtick? fun(_: "changedtick", bufnr: integer, changedtick: integer) +--- @field on_detach? fun(_: "detach", bufnr: integer) +--- @field on_reload? fun(_: "reload", bufnr: integer) --- @field utf_sizes? boolean --- @field preview? boolean @@ -18,9 +18,9 @@ error('Cannot require a meta file') --- @class vim.api.keyset.clear_autocmds --- @field buffer? integer ---- @field event? any ---- @field group? any ---- @field pattern? any +--- @field event? string|string[] +--- @field group? integer|string +--- @field pattern? string|string[] --- @class vim.api.keyset.cmd --- @field cmd? string @@ -28,12 +28,12 @@ error('Cannot require a meta file') --- @field count? integer --- @field reg? string --- @field bang? boolean ---- @field args? any[] +--- @field args? string[] --- @field magic? table --- @field mods? table ---- @field nargs? any ---- @field addr? any ---- @field nextcmd? any +--- @field nargs? integer|string +--- @field addr? string +--- @field nextcmd? string --- @class vim.api.keyset.cmd_magic --- @field file? boolean @@ -72,20 +72,20 @@ error('Cannot require a meta file') --- @field info? string --- @class vim.api.keyset.context ---- @field types? any[] +--- @field types? string[] --- @class vim.api.keyset.create_augroup ---- @field clear? any +--- @field clear? boolean --- @class vim.api.keyset.create_autocmd --- @field buffer? integer ---- @field callback? any +--- @field callback? string|(fun(args: vim.api.keyset.create_autocmd.callback_args): boolean?) --- @field command? string --- @field desc? string ---- @field group? any +--- @field group? integer|string --- @field nested? boolean --- @field once? boolean ---- @field pattern? any +--- @field pattern? string|string[] --- @class vim.api.keyset.echo_opts --- @field verbose? boolean @@ -103,19 +103,19 @@ error('Cannot require a meta file') --- @class vim.api.keyset.exec_autocmds --- @field buffer? integer ---- @field group? any +--- @field group? integer|string --- @field modeline? boolean ---- @field pattern? any +--- @field pattern? string|string[] --- @field data? any --- @class vim.api.keyset.exec_opts --- @field output? boolean --- @class vim.api.keyset.get_autocmds ---- @field event? any ---- @field group? any ---- @field pattern? any ---- @field buffer? any +--- @field event? string|string[] +--- @field group? integer|string +--- @field pattern? string|string[] +--- @field buffer? integer|integer[] --- @class vim.api.keyset.get_commands --- @field builtin? boolean @@ -154,17 +154,17 @@ error('Cannot require a meta file') --- @field altfont? boolean --- @field nocombine? boolean --- @field default? boolean ---- @field cterm? any ---- @field foreground? any ---- @field fg? any ---- @field background? any ---- @field bg? any ---- @field ctermfg? any ---- @field ctermbg? any ---- @field special? any ---- @field sp? any ---- @field link? any ---- @field global_link? any +--- @field cterm? integer|string +--- @field foreground? integer|string +--- @field fg? integer|string +--- @field background? integer|string +--- @field bg? integer|string +--- @field ctermfg? integer|string +--- @field ctermbg? integer|string +--- @field special? integer|string +--- @field sp? integer|string +--- @field link? integer|string +--- @field global_link? integer|string --- @field fallback? boolean --- @field blend? integer --- @field fg_indexed? boolean @@ -201,7 +201,7 @@ error('Cannot require a meta file') --- @field wins? any[] --- @class vim.api.keyset.open_term ---- @field on_input? function +--- @field on_input? fun(_: "input", term: integer, bufnr: integer, data: any) --- @field force_crlf? boolean --- @class vim.api.keyset.option @@ -227,20 +227,20 @@ error('Cannot require a meta file') --- @field do_source? boolean --- @class vim.api.keyset.set_decoration_provider ---- @field on_start? function ---- @field on_buf? function ---- @field on_win? function ---- @field on_line? function ---- @field on_end? function ---- @field _on_hl_def? function ---- @field _on_spell_nav? function +--- @field on_start? fun(_: "start", tick: integer) +--- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer) +--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer) +--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer) +--- @field on_end? fun(_: "end", tick: integer) +--- @field _on_hl_def? fun(_: "hl_def") +--- @field _on_spell_nav? fun(_: "spell_nav") --- @class vim.api.keyset.set_extmark --- @field id? integer --- @field end_line? integer --- @field end_row? integer --- @field end_col? integer ---- @field hl_group? number|string +--- @field hl_group? integer|string --- @field virt_text? any[] --- @field virt_text_pos? string --- @field virt_text_win_col? integer @@ -258,10 +258,10 @@ error('Cannot require a meta file') --- @field virt_lines_leftcol? boolean --- @field strict? boolean --- @field sign_text? string ---- @field sign_hl_group? number|string ---- @field number_hl_group? number|string ---- @field line_hl_group? number|string ---- @field cursorline_hl_group? number|string +--- @field sign_hl_group? integer|string +--- @field number_hl_group? integer|string +--- @field line_hl_group? integer|string +--- @field cursorline_hl_group? integer|string --- @field conceal? string --- @field spell? boolean --- @field ui_watched? boolean @@ -292,7 +292,7 @@ error('Cannot require a meta file') --- @field relative? string --- @field split? string --- @field win? integer ---- @field bufpos? any[] +--- @field bufpos? integer[] --- @field external? boolean --- @field focusable? boolean --- @field mouse? boolean @@ -315,12 +315,12 @@ error('Cannot require a meta file') --- @field end_vcol? integer --- @class vim.api.keyset.xdl_diff ---- @field on_hunk? function +--- @field on_hunk? fun(start_a: integer, count_a: integer, start_b: integer, count_b: integer): integer? --- @field result_type? string --- @field algorithm? string --- @field ctxlen? integer --- @field interhunkctxlen? integer ---- @field linematch? any +--- @field linematch? boolean|integer --- @field ignore_whitespace? boolean --- @field ignore_whitespace_change? boolean --- @field ignore_whitespace_change_at_eol? boolean diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua index 81bce50746..e8e901acb2 100644 --- a/runtime/lua/vim/_meta/api_keysets_extra.lua +++ b/runtime/lua/vim/_meta/api_keysets_extra.lua @@ -73,6 +73,51 @@ error('Cannot require a meta file') --- @field buflocal? boolean --- @field buffer? integer +--- @class vim.api.keyset.create_autocmd.callback_args +--- @field id integer autocommand id +--- @field event string name of the triggered event |autocmd-events| +--- @field group? integer autocommand group id, if any +--- @field match string expanded value of +--- @field buf integer expanded value of +--- @field file string expanded value of +--- @field data? any arbitrary data passed from |nvim_exec_autocmds()| *event-data* + +--- @class vim.api.keyset.create_user_command.command_args +--- @field name string Command name +--- +--- The args passed to the command, if any +--- @field args string +--- +--- The args split by unescaped whitespace +--- (when more than one argument is allowed), if any +--- @field fargs string[] +--- +--- Number of arguments |:command-nargs| +--- @field nargs string +--- +--- "true" if the command was executed with a ! modifier +--- @field bang boolean +--- +--- The starting line of the command range +--- @field line1 integer +--- +--- The final line of the command range +--- @field line2 integer +--- +--- The number of items in the command range: 0, 1, or 2 +--- @field range integer +--- +--- Any count supplied +--- @field count integer +--- The optional register, if specified +--- @field reg string +--- Command modifiers, if any +--- @field mods string +--- +--- Command modifiers in a structured format. Has the same structure as the +--- "mods" key of |nvim_parse_cmd()|. +--- @field smods table + --- @class vim.api.keyset.command_info --- @field name string --- @field definition string -- cgit From 29c72cdf4a4913c152f037865cb28c78a8930340 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Mon, 25 Nov 2024 11:48:11 -0600 Subject: fix(lsp): retrigger diagnostics request on server cancellation (#31345) Co-authored-by: Jesse --- runtime/lua/vim/lsp/diagnostic.lua | 12 ++++++++++-- runtime/lua/vim/lsp/handlers.lua | 3 ++- runtime/lua/vim/lsp/protocol.lua | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 12984f8c26..3f135d84f3 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -246,10 +246,18 @@ end --- --- See |vim.diagnostic.config()| for configuration options. --- ----@param _ lsp.ResponseError? +---@param error lsp.ResponseError? ---@param result lsp.DocumentDiagnosticReport ---@param ctx lsp.HandlerContext -function M.on_diagnostic(_, result, ctx) +function M.on_diagnostic(error, result, ctx) + if error ~= nil and error.code == protocol.ErrorCodes.ServerCancelled then + if error.data == nil or error.data.retriggerRequest ~= false then + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + client:request(ctx.method, ctx.params) + end + return + end + if result == nil or result.kind == 'unchanged' then return end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 5c28d88b38..1945040bda 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -659,7 +659,8 @@ for k, fn in pairs(M) do }) end - if err then + -- ServerCancelled errors should be propagated to the request handler + if err and err.code ~= protocol.ErrorCodes.ServerCancelled then -- LSP spec: -- interface ResponseError: -- code: integer; diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 7db48b0c06..3d29dad90a 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -174,6 +174,7 @@ local constants = { -- Defined by the protocol. RequestCancelled = -32800, ContentModified = -32801, + ServerCancelled = -32802, }, -- Describes the content type that a client supports in various -- cgit From c644228e1dfe9f70aae53292b328be98dc95b8f7 Mon Sep 17 00:00:00 2001 From: Marcus Caisey Date: Mon, 25 Nov 2024 22:33:11 +0000 Subject: fix(defaults): omit empty line from unimpaired mapping messages (#31347) Problem: The default unimpaired mappings display an empty line after the command's output. This results (with default configuration) in the `Press ENTER or type command to continue` prompt to be displayed, like so: ``` (2 of 16): item2 Press ENTER or type command to continue ``` Solution: The cause is that we're checking the second return value from `pcall(vim.api.nvim_cmd, opts, {})` to determine whether the call was successful. `nvim_cmd` returns an empty string on success, so this value is an empty string in the successful path which we then display. The fix is simple: check the first return value instead which is the "status code" of the call. --- runtime/lua/vim/_defaults.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 06f6ed6829..2687f34302 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -222,8 +222,8 @@ do --- Execute a command and print errors without a stacktrace. --- @param opts table Arguments to |nvim_cmd()| local function cmd(opts) - local _, err = pcall(vim.api.nvim_cmd, opts, {}) - if err then + local ok, err = pcall(vim.api.nvim_cmd, opts, {}) + if not ok then vim.api.nvim_err_writeln(err:sub(#'Vim:' + 1)) end end -- cgit From 66bb1e577c96d8eb63c04dcc737394b4ce2b0f5d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 26 Nov 2024 07:53:07 +0800 Subject: vim-patch:9.1.0888: leftcol property not available in getwininfo() (#31349) Problem: leftcol property not available in getwininfo() Solution: add leftcol property property (glepnir) closes: vim/vim#16119 https://github.com/vim/vim/commit/0a850673e3d4193d55f47bcbbc0b0da5f155307d Co-authored-by: glepnir --- runtime/lua/vim/_meta/vimfn.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 5eb15e1eee..f9b5d93a4b 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -3885,6 +3885,8 @@ function vim.fn.gettext(text) end --- botline last complete displayed buffer line --- bufnr number of buffer in the window --- height window height (excluding winbar) +--- leftcol first column displayed; only used when +--- 'wrap' is off --- loclist 1 if showing a location list --- quickfix 1 if quickfix or location list window --- terminal 1 if a terminal window -- cgit From 3d707e6f14b7db64b3510f58bf9321c71163f552 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 26 Nov 2024 06:15:50 -0800 Subject: fix(lua): remove vim.loader.disable() #31344 Problem: `vim.loader.disable` does not conform to `:help dev-name-common` and `:help dev-patterns`. Solution: - Add `enable` parameter to `vim.loader.enable` - Remove `vim.loader.disable` - Note the change in `:help news-breaking-dev` (HEAD changes). - This is not a breaking change (except to "HEAD") because `vim.loader` is marked "experimental". previous: 26765e8461c1ba1e9a351632212cf89900221781 --- runtime/lua/vim/loader.lua | 65 +++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 32 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 0cce0ab21d..71d0188128 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -399,50 +399,51 @@ function M.reset(path) end end ---- Enables the experimental Lua module loader: ---- * overrides loadfile +--- Enables or disables the experimental Lua module loader: +--- +--- Enable (`enable=true`): +--- * overrides |loadfile()| --- * adds the Lua loader using the byte-compilation cache --- * adds the libs loader --- * removes the default Nvim loader --- ---- @since 0 -function M.enable() - if M.enabled then - return - end - M.enabled = true - vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p') - _G.loadfile = loadfile_cached - -- add Lua loader - table.insert(loaders, 2, loader_cached) - -- add libs loader - table.insert(loaders, 3, loader_lib_cached) - -- remove Nvim loader - for l, loader in ipairs(loaders) do - if loader == vim._load_package then - table.remove(loaders, l) - break - end - end -end - ---- Disables the experimental Lua module loader: +--- Disable (`enable=false`): --- * removes the loaders --- * adds the default Nvim loader --- --- @since 0 -function M.disable() - if not M.enabled then +--- +--- @param enable? (boolean) true/nil to enable, false to disable +function M.enable(enable) + enable = enable == nil and true or enable + if enable == M.enabled then return end - M.enabled = false - _G.loadfile = _loadfile - for l, loader in ipairs(loaders) do - if loader == loader_cached or loader == loader_lib_cached then - table.remove(loaders, l) + M.enabled = enable + + if enable then + vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p') + _G.loadfile = loadfile_cached + -- add Lua loader + table.insert(loaders, 2, loader_cached) + -- add libs loader + table.insert(loaders, 3, loader_lib_cached) + -- remove Nvim loader + for l, loader in ipairs(loaders) do + if loader == vim._load_package then + table.remove(loaders, l) + break + end + end + else + _G.loadfile = _loadfile + for l, loader in ipairs(loaders) do + if loader == loader_cached or loader == loader_lib_cached then + table.remove(loaders, l) + end end + table.insert(loaders, 2, vim._load_package) end - table.insert(loaders, 2, vim._load_package) end --- Tracks the time spent in a function -- cgit From 99b5ffd688247f25295f3dd06e57c0d9ad85b072 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 26 Nov 2024 13:56:01 -0600 Subject: perf(vim.text): use lookup table implementation for hex encoding (#30080) Co-authored-by: glepnir --- runtime/lua/vim/text.lua | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/text.lua b/runtime/lua/vim/text.lua index d45c8021c6..f910ab3a1d 100644 --- a/runtime/lua/vim/text.lua +++ b/runtime/lua/vim/text.lua @@ -2,6 +2,18 @@ local M = {} +local alphabet = '0123456789ABCDEF' +local atoi = {} ---@type table +local itoa = {} ---@type table +do + for i = 1, #alphabet do + local char = alphabet:sub(i, i) + itoa[i - 1] = char + atoi[char] = i - 1 + atoi[char:lower()] = i - 1 + end +end + --- Hex encode a string. --- --- @param str string String to encode @@ -9,7 +21,9 @@ local M = {} function M.hexencode(str) local enc = {} ---@type string[] for i = 1, #str do - enc[i] = string.format('%02X', str:byte(i, i + 1)) + local byte = str:byte(i) + enc[2 * i - 1] = itoa[math.floor(byte / 16)] + enc[2 * i] = itoa[byte % 16] end return table.concat(enc) end @@ -26,8 +40,12 @@ function M.hexdecode(enc) local str = {} ---@type string[] for i = 1, #enc, 2 do - local n = assert(tonumber(enc:sub(i, i + 1), 16)) - str[#str + 1] = string.char(n) + local u = atoi[enc:sub(i, i)] + local l = atoi[enc:sub(i + 1, i + 1)] + if not u or not l then + return nil, 'string must contain only hex characters' + end + str[(i + 1) / 2] = string.char(u * 16 + l) end return table.concat(str), nil end -- cgit From d460928263d0ff53283f301dfcb85f5b6e17d2ac Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 26 Nov 2024 14:22:01 -0600 Subject: feat(tui): update 'background' on theme change events (#31350) Enabling private DEC mode 2031 tells the terminal to notify Nvim whenever the OS theme changes (i.e. light mode to dark mode or vice versa) or the terminal emulator's palette changes. When we receive one of these notifications we query the terminal color's background color again to see if it has changed and update the value of 'background' if it has. We only do this though if the user has not explicitly set the value of 'bg' themselves. The help text is updated slightly to hint to users that they probably shouldn't set this value: on modern terminal emulators Nvim is able to completely determine this automatically. --- runtime/lua/vim/_defaults.lua | 49 ++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 2687f34302..6583cf48b3 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -546,8 +546,9 @@ do --- --- @param option string Option name --- @param value any Option value - local function setoption(option, value) - if vim.api.nvim_get_option_info2(option, {}).was_set then + --- @param force boolean? Always set the value, even if already set + local function setoption(option, value, force) + if not force and vim.api.nvim_get_option_info2(option, {}).was_set then -- Don't do anything if option is already set return end @@ -563,7 +564,7 @@ do once = true, nested = true, callback = function() - setoption(option, value) + setoption(option, value, force) end, }) end @@ -645,11 +646,15 @@ do return nil, nil, nil end - local timer = assert(vim.uv.new_timer()) - + -- This autocommand updates the value of 'background' anytime we receive + -- an OSC 11 response from the terminal emulator. If the user has set + -- 'background' explictly then we will delete this autocommand, + -- effectively disabling automatic background setting. + local force = false local id = vim.api.nvim_create_autocmd('TermResponse', { group = group, nested = true, + desc = "Update the value of 'background' automatically based on the terminal emulator's background color", callback = function(args) local resp = args.data ---@type string local r, g, b = parseosc11(resp) @@ -661,27 +666,33 @@ do if rr and gg and bb then local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb) local bg = luminance < 0.5 and 'dark' or 'light' - setoption('background', bg) + setoption('background', bg, force) + + -- On the first query response, don't force setting the option in + -- case the user has already set it manually. If they have, then + -- this autocommand will be deleted. If they haven't, then we do + -- want to force setting the option to override the value set by + -- this autocommand. + if not force then + force = true + end end + end + end, + }) - return true + vim.api.nvim_create_autocmd('VimEnter', { + group = group, + nested = true, + once = true, + callback = function() + if vim.api.nvim_get_option_info2('background', {}).was_set then + vim.api.nvim_del_autocmd(id) end end, }) io.stdout:write('\027]11;?\007') - - timer:start(1000, 0, function() - -- Delete the autocommand if no response was received - vim.schedule(function() - -- Suppress error if autocommand has already been deleted - pcall(vim.api.nvim_del_autocmd, id) - end) - - if not timer:is_closing() then - timer:close() - end - end) end --- If the TUI (term_has_truecolor) was able to determine that the host -- cgit From 203e7a43d1f209168a3cdae1650a9ef01db9cd72 Mon Sep 17 00:00:00 2001 From: glepnir Date: Wed, 27 Nov 2024 21:44:29 +0800 Subject: fix(lsp): mention function name in warning #31301 Problem: The warning message is the same in different functions. It is not convenient to locate the specific function name Solution: add function name in warning message --- runtime/lua/vim/lsp/util.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 3de76f93a6..ced8aa5745 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1894,7 +1894,7 @@ function M.make_position_params(window, position_encoding) local buf = api.nvim_win_get_buf(window) if position_encoding == nil then vim.notify_once( - 'warning: position_encoding is required, using the offset_encoding from the first client', + 'position_encoding param is required in vim.lsp.util.make_position_params. Defaulting to position encoding of the first client.', vim.log.levels.WARN ) position_encoding = M._get_offset_encoding(buf) @@ -1950,7 +1950,7 @@ function M.make_range_params(window, position_encoding) local buf = api.nvim_win_get_buf(window or 0) if position_encoding == nil then vim.notify_once( - 'warning: position_encoding is required, using the offset_encoding from the first client', + 'position_encoding param is required in vim.lsp.util.make_range_params. Defaulting to position encoding of the first client.', vim.log.levels.WARN ) position_encoding = M._get_offset_encoding(buf) @@ -1979,7 +1979,7 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, position_encoding) bufnr = bufnr or api.nvim_get_current_buf() if position_encoding == nil then vim.notify_once( - 'warning: position_encoding is required, using the offset_encoding from the first client', + 'position_encoding param is required in vim.lsp.util.make_given_range_params. Defaulting to position encoding of the first client.', vim.log.levels.WARN ) position_encoding = M._get_offset_encoding(bufnr) -- cgit From 76dcc7029b200e1d85024d7ba4a34c602e730dbe Mon Sep 17 00:00:00 2001 From: dundargoc Date: Wed, 27 Nov 2024 15:36:38 +0100 Subject: docs: add tag `vim.fs.exists()` and document suggested replacement --- runtime/lua/vim/fs.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index d91eeaf02f..2f007d97c3 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -1,3 +1,15 @@ +--- @brief
help
+--- *vim.fs.exists()*
+--- Use |uv.fs_stat()| to check a file's type, and whether it exists.
+---
+--- Example:
+---
+--- >lua
+---   if vim.uv.fs_stat(file) then
+---     vim.print("file exists")
+---   end
+--- <
+
 local uv = vim.uv
 
 local M = {}
-- 
cgit 


From 5897994cb75b5b64a34017873119c87a75c42375 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 28 Nov 2024 09:02:10 +0800
Subject: vim-patch:9.1.0890: %! item not allowed for 'rulerformat' (#31369)

Problem:  %! item not allowed for 'rulerformat'
          (yatinlala)
Solution: also allow to use %! for rulerformat option
          (Yegappan Lakshmanan)

fixes: vim/vim#16091
closes: vim/vim#16118

https://github.com/vim/vim/commit/ac023e8baae65584537aa3c11494dad6f71770af

Co-authored-by: Yegappan Lakshmanan 
---
 runtime/lua/vim/_meta/options.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index c635d9bd3b..247b464a70 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -6311,6 +6311,7 @@ vim.wo.stc = vim.wo.statuscolumn
 --- All fields except the {item} are optional.  A single percent sign can
 --- be given as "%%".
 ---
+--- 						*stl-%!*
 --- When the option starts with "%!" then it is used as an expression,
 --- evaluated and the result is used as the option value.  Example:
 ---
-- 
cgit 


From 864f25d6b08ccfe17e0cf3fbc30639005c0145e0 Mon Sep 17 00:00:00 2001
From: Riley Bruins 
Date: Mon, 25 Nov 2024 20:56:18 -0800
Subject: docs: more accurate typing for LSP references context

**Problem:** The `context` parameter for `references()` is just typed as
a table, which is unhelpful.

**Solution:** Properly type it as an `lsp.ReferenceContext`!
---
 runtime/lua/vim/lsp/buf.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index aca6bf27f4..e4b6b9ac1e 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -736,7 +736,7 @@ end
 
 --- Lists all the references to the symbol under the cursor in the quickfix window.
 ---
----@param context (table|nil) Context for the request
+---@param context lsp.ReferenceContext? Context for the request
 ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
 ---@param opts? vim.lsp.ListOpts
 function M.references(context, opts)
-- 
cgit 


From 146b8300a145efa64e579527da8606546a36162b Mon Sep 17 00:00:00 2001
From: Yi Ming 
Date: Fri, 29 Nov 2024 00:08:27 +0800
Subject: docs(lsp): update example, optional parameters #31299

---
 runtime/lua/vim/lsp/buf.lua    | 2 +-
 runtime/lua/vim/lsp/client.lua | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index e4b6b9ac1e..10479807a2 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -487,7 +487,7 @@ end
 --- ```lua
 --- -- Never request typescript-language-server for formatting
 --- vim.lsp.buf.format {
----   filter = function(client) return client.name ~= "tsserver" end
+---   filter = function(client) return client.name ~= "ts_ls" end
 --- }
 --- ```
 --- @field filter? fun(client: vim.lsp.Client): boolean?
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 7c2b7192f5..a14b6ccda6 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -639,7 +639,7 @@ 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 (default).
+--- @param bufnr? integer (default: 0) Buffer handle, or 0 for current.
 --- @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()|.
@@ -718,7 +718,7 @@ end
 --- @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).
+--- @param bufnr? integer (default: 0) Buffer handle, or 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
-- 
cgit 


From a1e313ded6e4c46c58012639e5c0c6d0b009d52a Mon Sep 17 00:00:00 2001
From: Yi Ming 
Date: Fri, 29 Nov 2024 20:40:32 +0800
Subject: feat(lsp): support `textDocument/foldingRange` (#31311)

* refactor(shared): extract `vim._list_insert` and `vim._list_remove`

* feat(lsp): add `vim.lsp.foldexpr()`

* docs(lsp): add a todo for state management

* feat(lsp): add `vim.lsp.folding_range.foldclose()`

* feat(lsp): schedule `foldclose()` if the buffer is not up-to-date

* feat(lsp): add `vim.lsp.foldtext()`

* feat(lsp): support multiple folding range providers

* refactor(lsp): expose all folding related functions under `vim.lsp.*`

* perf(lsp): add `lsp.MultiHandler` for do `foldupdate()` only once
---
 runtime/lua/vim/lsp.lua                |  34 +++
 runtime/lua/vim/lsp/_folding_range.lua | 371 +++++++++++++++++++++++++++++++++
 runtime/lua/vim/lsp/protocol.lua       |   7 +
 runtime/lua/vim/shared.lua             |  45 ++++
 runtime/lua/vim/treesitter/_fold.lua   |  53 +----
 5 files changed, 461 insertions(+), 49 deletions(-)
 create mode 100644 runtime/lua/vim/lsp/_folding_range.lua

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 6d29c9e4df..a3791e15c3 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -3,6 +3,7 @@ local validate = vim.validate
 
 local lsp = vim._defer_require('vim.lsp', {
   _changetracking = ..., --- @module 'vim.lsp._changetracking'
+  _folding_range = ..., --- @module 'vim.lsp._folding_range'
   _snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar'
   _tagfunc = ..., --- @module 'vim.lsp._tagfunc'
   _watchfiles = ..., --- @module 'vim.lsp._watchfiles'
@@ -57,6 +58,7 @@ lsp._request_name_to_capability = {
   [ms.textDocument_documentHighlight] = { 'documentHighlightProvider' },
   [ms.textDocument_documentLink] = { 'documentLinkProvider' },
   [ms.textDocument_documentSymbol] = { 'documentSymbolProvider' },
+  [ms.textDocument_foldingRange] = { 'foldingRangeProvider' },
   [ms.textDocument_formatting] = { 'documentFormattingProvider' },
   [ms.textDocument_hover] = { 'hoverProvider' },
   [ms.textDocument_implementation] = { 'implementationProvider' },
@@ -1094,6 +1096,38 @@ function lsp.tagfunc(pattern, flags)
   return vim.lsp._tagfunc(pattern, flags)
 end
 
+--- Provides an interface between the built-in client and a `foldexpr` function.
+---@param lnum integer line number
+function lsp.foldexpr(lnum)
+  return vim.lsp._folding_range.foldexpr(lnum)
+end
+
+--- Close all {kind} of folds in the the window with {winid}.
+---
+--- To automatically fold imports when opening a file, you can use an autocmd:
+---
+--- ```lua
+--- vim.api.nvim_create_autocmd('LspNotify', {
+---   callback = function(args)
+---     if args.data.method == 'textDocument/didOpen' then
+---       vim.lsp.foldclose('imports', vim.fn.bufwinid(args.buf))
+---     end
+---   end,
+--- })
+--- ```
+---
+---@param kind lsp.FoldingRangeKind Kind to close, one of "comment", "imports" or "region".
+---@param winid? integer Defaults to the current window.
+function lsp.foldclose(kind, winid)
+  return vim.lsp._folding_range.foldclose(kind, winid)
+end
+
+--- Provides a `foldtext` function that shows the `collapsedText` retrieved,
+--- defaults to the first folded line if `collapsedText` is not provided.
+function lsp.foldtext()
+  return vim.lsp._folding_range.foldtext()
+end
+
 ---Checks whether a client is stopped.
 ---
 ---@param client_id (integer)
diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua
new file mode 100644
index 0000000000..6a445017a3
--- /dev/null
+++ b/runtime/lua/vim/lsp/_folding_range.lua
@@ -0,0 +1,371 @@
+local util = require('vim.lsp.util')
+local log = require('vim.lsp.log')
+local ms = require('vim.lsp.protocol').Methods
+local api = vim.api
+
+local M = {}
+
+---@class (private) vim.lsp.folding_range.BufState
+---
+---@field version? integer
+---
+--- Never use this directly, `renew()` the cached foldinfo
+--- then use on demand via `row_*` fields.
+---
+--- Index In the form of client_id -> ranges
+---@field client_ranges table
+---
+--- Index in the form of row -> [foldlevel, mark]
+---@field row_level table" | "<"?]?>
+---
+--- Index in the form of start_row -> kinds
+---@field row_kinds table?>>
+---
+--- Index in the form of start_row -> collapsed_text
+---@field row_text table
+
+---@type table
+local bufstates = {}
+
+--- Renew the cached foldinfo in the buffer.
+---@param bufnr integer
+local function renew(bufnr)
+  local bufstate = assert(bufstates[bufnr])
+
+  ---@type table" | "<"?]?>
+  local row_level = {}
+  ---@type table?>>
+  local row_kinds = {}
+  ---@type table
+  local row_text = {}
+
+  for _, ranges in pairs(bufstate.client_ranges) do
+    for _, range in ipairs(ranges) do
+      local start_row = range.startLine
+      local end_row = range.endLine
+      -- Adding folds within a single line is not supported by Nvim.
+      if start_row ~= end_row then
+        row_text[start_row] = range.collapsedText
+
+        local kind = range.kind
+        if kind then
+          local kinds = row_kinds[start_row] or {}
+          kinds[kind] = true
+          row_kinds[start_row] = kinds
+        end
+
+        for row = start_row, end_row do
+          local level = row_level[row] or { 0 }
+          level[1] = level[1] + 1
+          row_level[row] = level
+        end
+        row_level[start_row][2] = '>'
+        row_level[end_row][2] = '<'
+      end
+    end
+  end
+
+  bufstate.row_level = row_level
+  bufstate.row_kinds = row_kinds
+  bufstate.row_text = row_text
+end
+
+--- Renew the cached foldinfo then force `foldexpr()` to be re-evaluated,
+--- without opening folds.
+---@param bufnr integer
+local function foldupdate(bufnr)
+  renew(bufnr)
+  for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
+    local wininfo = vim.fn.getwininfo(winid)[1]
+    if wininfo and wininfo.tabnr == vim.fn.tabpagenr() then
+      if vim.wo[winid].foldmethod == 'expr' then
+        vim._foldupdate(winid, 0, api.nvim_buf_line_count(bufnr))
+      end
+    end
+  end
+end
+
+--- Whether `foldupdate()` is scheduled for the buffer with `bufnr`.
+---
+--- Index in the form of bufnr -> true?
+---@type table
+local scheduled_foldupdate = {}
+
+--- Schedule `foldupdate()` after leaving insert mode.
+---@param bufnr integer
+local function schedule_foldupdate(bufnr)
+  if not scheduled_foldupdate[bufnr] then
+    scheduled_foldupdate[bufnr] = true
+    api.nvim_create_autocmd('InsertLeave', {
+      buffer = bufnr,
+      once = true,
+      callback = function()
+        foldupdate(bufnr)
+        scheduled_foldupdate[bufnr] = nil
+      end,
+    })
+  end
+end
+
+---@param results table
+---@type lsp.MultiHandler
+local function multi_handler(results, ctx)
+  local bufnr = assert(ctx.bufnr)
+  -- Handling responses from outdated buffer only causes performance overhead.
+  if util.buf_versions[bufnr] ~= ctx.version then
+    return
+  end
+
+  local bufstate = assert(bufstates[bufnr])
+  for client_id, result in pairs(results) do
+    if result.err then
+      log.error(result.err)
+    else
+      bufstate.client_ranges[client_id] = result.result
+    end
+  end
+  bufstate.version = ctx.version
+
+  if api.nvim_get_mode().mode:match('^i') then
+    -- `foldUpdate()` is guarded in insert mode.
+    schedule_foldupdate(bufnr)
+  else
+    foldupdate(bufnr)
+  end
+end
+
+---@param result lsp.FoldingRange[]?
+---@type lsp.Handler
+local function handler(err, result, ctx)
+  multi_handler({ [ctx.client_id] = { err = err, result = result } }, ctx)
+end
+
+--- Request `textDocument/foldingRange` from the server.
+--- `foldupdate()` is scheduled once after the request is completed.
+---@param bufnr integer
+---@param client? vim.lsp.Client The client whose server supports `foldingRange`.
+local function request(bufnr, client)
+  ---@type lsp.FoldingRangeParams
+  local params = { textDocument = util.make_text_document_params(bufnr) }
+
+  if client then
+    client:request(ms.textDocument_foldingRange, params, handler, bufnr)
+    return
+  end
+
+  if not next(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) then
+    return
+  end
+
+  vim.lsp.buf_request_all(bufnr, ms.textDocument_foldingRange, params, multi_handler)
+end
+
+-- NOTE:
+-- `bufstate` and event hooks are interdependent:
+-- * `bufstate` needs event hooks for correctness.
+-- * event hooks require the previous `bufstate` for updates.
+-- Since they are manually created and destroyed,
+-- we ensure their lifecycles are always synchronized.
+--
+-- TODO(ofseed):
+-- 1. Implement clearing `bufstate` and event hooks
+--    when no clients in the buffer support the corresponding method.
+-- 2. Then generalize this state management to other LSP modules.
+local augroup_setup = api.nvim_create_augroup('vim_lsp_folding_range/setup', {})
+
+--- Initialize `bufstate` and event hooks, then request folding ranges.
+--- Manage their lifecycle within this function.
+---@param bufnr integer
+---@return vim.lsp.folding_range.BufState?
+local function setup(bufnr)
+  if not api.nvim_buf_is_loaded(bufnr) then
+    return
+  end
+
+  -- Register the new `bufstate`.
+  bufstates[bufnr] = {
+    client_ranges = {},
+    row_level = {},
+    row_kinds = {},
+    row_text = {},
+  }
+
+  -- Event hooks from `buf_attach` can't be removed externally.
+  -- Hooks and `bufstate` share the same lifecycle;
+  -- they should self-destroy if `bufstate == nil`.
+  api.nvim_buf_attach(bufnr, false, {
+    -- `on_detach` also runs on buffer reload (`:e`).
+    -- Ensure `bufstate` and hooks are cleared to avoid duplication or leftover states.
+    on_detach = function()
+      bufstates[bufnr] = nil
+      api.nvim_clear_autocmds({ buffer = bufnr, group = augroup_setup })
+    end,
+    -- Reset `bufstate` and request folding ranges.
+    on_reload = function()
+      bufstates[bufnr] = {
+        client_ranges = {},
+        row_level = {},
+        row_kinds = {},
+        row_text = {},
+      }
+      request(bufnr)
+    end,
+    --- Sync changed rows with their previous foldlevels before applying new ones.
+    on_bytes = function(_, _, _, start_row, _, _, old_row, _, _, new_row, _, _)
+      if bufstates[bufnr] == nil then
+        return true
+      end
+      local row_level = bufstates[bufnr].row_level
+      if next(row_level) == nil then
+        return
+      end
+      local row = new_row - old_row
+      if row > 0 then
+        vim._list_insert(row_level, start_row, start_row + math.abs(row) - 1, { -1 })
+        -- If the previous row ends a fold,
+        -- Nvim treats the first row after consecutive `-1`s as a new fold start,
+        -- which is not the desired behavior.
+        local prev_level = row_level[start_row - 1]
+        if prev_level and prev_level[2] == '<' then
+          row_level[start_row] = { prev_level[1] - 1 }
+        end
+      elseif row < 0 then
+        vim._list_remove(row_level, start_row, start_row + math.abs(row) - 1)
+      end
+    end,
+  })
+  api.nvim_create_autocmd('LspDetach', {
+    group = augroup_setup,
+    buffer = bufnr,
+    callback = function(args)
+      if not api.nvim_buf_is_loaded(bufnr) then
+        return
+      end
+
+      ---@type integer
+      local client_id = args.data.client_id
+      bufstates[bufnr].client_ranges[client_id] = nil
+
+      ---@type vim.lsp.Client[]
+      local clients = vim
+        .iter(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange }))
+        ---@param client vim.lsp.Client
+        :filter(function(client)
+          return client.id ~= client_id
+        end)
+        :totable()
+      if #clients == 0 then
+        bufstates[bufnr] = {
+          client_ranges = {},
+          row_level = {},
+          row_kinds = {},
+          row_text = {},
+        }
+      end
+
+      foldupdate(bufnr)
+    end,
+  })
+  api.nvim_create_autocmd('LspAttach', {
+    group = augroup_setup,
+    buffer = bufnr,
+    callback = function(args)
+      local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
+      request(bufnr, client)
+    end,
+  })
+  api.nvim_create_autocmd('LspNotify', {
+    group = augroup_setup,
+    buffer = bufnr,
+    callback = function(args)
+      local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
+      if
+        client:supports_method(ms.textDocument_foldingRange, bufnr)
+        and (
+          args.data.method == ms.textDocument_didChange
+          or args.data.method == ms.textDocument_didOpen
+        )
+      then
+        request(bufnr, client)
+      end
+    end,
+  })
+
+  request(bufnr)
+
+  return bufstates[bufnr]
+end
+
+---@param kind lsp.FoldingRangeKind
+---@param winid integer
+local function foldclose(kind, winid)
+  vim._with({ win = winid }, function()
+    local bufnr = api.nvim_win_get_buf(winid)
+    local row_kinds = bufstates[bufnr].row_kinds
+    -- Reverse traverse to ensure that the smallest ranges are closed first.
+    for row = api.nvim_buf_line_count(bufnr) - 1, 0, -1 do
+      local kinds = row_kinds[row]
+      if kinds and kinds[kind] then
+        vim.cmd(row + 1 .. 'foldclose')
+      end
+    end
+  end)
+end
+
+---@param kind lsp.FoldingRangeKind
+---@param winid? integer
+function M.foldclose(kind, winid)
+  vim.validate('kind', kind, 'string')
+  vim.validate('winid', winid, 'number', true)
+
+  winid = winid or api.nvim_get_current_win()
+  local bufnr = api.nvim_win_get_buf(winid)
+  local bufstate = bufstates[bufnr]
+  if not bufstate then
+    return
+  end
+
+  if bufstate.version == util.buf_versions[bufnr] then
+    foldclose(kind, winid)
+    return
+  end
+  -- Schedule `foldclose()` if the buffer is not up-to-date.
+
+  if not next(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) then
+    return
+  end
+  ---@type lsp.FoldingRangeParams
+  local params = { textDocument = util.make_text_document_params(bufnr) }
+  vim.lsp.buf_request_all(bufnr, ms.textDocument_foldingRange, params, function(...)
+    multi_handler(...)
+    foldclose(kind, winid)
+  end)
+end
+
+---@return string
+function M.foldtext()
+  local bufnr = api.nvim_get_current_buf()
+  local lnum = vim.v.foldstart
+  local row = lnum - 1
+  local bufstate = bufstates[bufnr]
+  if bufstate and bufstate.row_text[row] then
+    return bufstate.row_text[row]
+  end
+  return vim.fn.getline(lnum)
+end
+
+---@param lnum? integer
+---@return string level
+function M.foldexpr(lnum)
+  local bufnr = api.nvim_get_current_buf()
+  local bufstate = bufstates[bufnr] or setup(bufnr)
+  if not bufstate then
+    return '0'
+  end
+
+  local row = (lnum or vim.v.lnum) - 1
+  local level = bufstate.row_level[row]
+  return level and (level[2] or '') .. (level[1] or '0') or '0'
+end
+
+return M
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 3d29dad90a..cfd47d8f7c 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -440,6 +440,13 @@ function protocol.make_client_capabilities()
           properties = { 'command' },
         },
       },
+      foldingRange = {
+        dynamicRegistration = false,
+        lineFoldingOnly = true,
+        foldingRange = {
+          collapsedText = true,
+        },
+      },
       formatting = {
         dynamicRegistration = true,
       },
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 4f2373b182..2e8edea22a 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -737,6 +737,51 @@ function vim.list_slice(list, start, finish)
   return new_list
 end
 
+--- Efficiently insert items into the middle of a list.
+---
+--- Calling table.insert() in a loop will re-index the tail of the table on
+--- every iteration, instead this function will re-index  the table exactly
+--- once.
+---
+--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
+---
+---@param t any[]
+---@param first integer
+---@param last integer
+---@param v any
+function vim._list_insert(t, first, last, v)
+  local n = #t
+
+  -- Shift table forward
+  for i = n - first, 0, -1 do
+    t[last + 1 + i] = t[first + i]
+  end
+
+  -- Fill in new values
+  for i = first, last do
+    t[i] = v
+  end
+end
+
+--- Efficiently remove items from middle of a list.
+---
+--- Calling table.remove() in a loop will re-index the tail of the table on
+--- every iteration, instead this function will re-index  the table exactly
+--- once.
+---
+--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
+---
+---@param t any[]
+---@param first integer
+---@param last integer
+function vim._list_remove(t, first, last)
+  local n = #t
+  for i = 0, n - first do
+    t[first + i] = t[last + 1 + i]
+    t[last + 1 + i] = nil
+  end
+end
+
 --- Trim whitespace (Lua pattern "%s") from both sides of a string.
 ---
 ---@see |lua-patterns|
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index 7237d2e7d4..0cb5b497c7 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -30,65 +30,20 @@ function FoldInfo.new()
   }, FoldInfo)
 end
 
---- Efficiently remove items from middle of a list a list.
----
---- Calling table.remove() in a loop will re-index the tail of the table on
---- every iteration, instead this function will re-index  the table exactly
---- once.
----
---- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
----
----@param t any[]
----@param first integer
----@param last integer
-local function list_remove(t, first, last)
-  local n = #t
-  for i = 0, n - first do
-    t[first + i] = t[last + 1 + i]
-    t[last + 1 + i] = nil
-  end
-end
-
 ---@package
 ---@param srow integer
 ---@param erow integer 0-indexed, exclusive
 function FoldInfo:remove_range(srow, erow)
-  list_remove(self.levels, srow + 1, erow)
-  list_remove(self.levels0, srow + 1, erow)
-end
-
---- Efficiently insert items into the middle of a list.
----
---- Calling table.insert() in a loop will re-index the tail of the table on
---- every iteration, instead this function will re-index  the table exactly
---- once.
----
---- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
----
----@param t any[]
----@param first integer
----@param last integer
----@param v any
-local function list_insert(t, first, last, v)
-  local n = #t
-
-  -- Shift table forward
-  for i = n - first, 0, -1 do
-    t[last + 1 + i] = t[first + i]
-  end
-
-  -- Fill in new values
-  for i = first, last do
-    t[i] = v
-  end
+  vim._list_remove(self.levels, srow + 1, erow)
+  vim._list_remove(self.levels0, srow + 1, erow)
 end
 
 ---@package
 ---@param srow integer
 ---@param erow integer 0-indexed, exclusive
 function FoldInfo:add_range(srow, erow)
-  list_insert(self.levels, srow + 1, erow, -1)
-  list_insert(self.levels0, srow + 1, erow, -1)
+  vim._list_insert(self.levels, srow + 1, erow, -1)
+  vim._list_insert(self.levels0, srow + 1, erow, -1)
 end
 
 ---@param range Range2
-- 
cgit 


From 4426a326e2441326e280a0478f83128e09305806 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sat, 30 Nov 2024 11:17:59 +0100
Subject: vim-patch:9.1.0897: filetype: pyrex files are not detected

Problem:  filetype: pyrex files are not detected
Solution: detect '*.pxi' and '*.pyx+' as pyrex filetype
          (user202729)

References:
https://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#cython-file-types
https://www.csse.canterbury.ac.nz/greg.ewing/python/Pyrex/version/Doc/Manual/using_with_c++.html

closes: vim/vim#16136

https://github.com/vim/vim/commit/aa16b30552f0c6a00dcc761e3954cd5266bf106a

Co-authored-by: user202729 <25191436+user202729@users.noreply.github.com>
---
 runtime/lua/vim/filetype.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index aa566973b6..5d771c30e9 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -962,7 +962,9 @@ local extension = {
   purs = 'purescript',
   arr = 'pyret',
   pxd = 'pyrex',
+  pxi = 'pyrex',
   pyx = 'pyrex',
+  ['pyx+'] = 'pyrex',
   pyw = 'python',
   py = 'python',
   pyi = 'python',
-- 
cgit 


From feb62d5429680278c1353c565db6bb3ecb3b7c24 Mon Sep 17 00:00:00 2001
From: "C.D. MacEachern" 
Date: Sun, 1 Dec 2024 16:58:28 -0500
Subject: docs: example keybind for :Inspect #31391

---
 runtime/lua/vim/_inspector.lua | 7 +++++++
 1 file changed, 7 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index fccf4b8dbe..f4e41a31e5 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -146,6 +146,13 @@ end
 ---
 ---Can also be shown with `:Inspect`. [:Inspect]()
 ---
+---Example: To bind this function to the vim-scriptease
+---inspired `zS` in Normal mode:
+---
+---```lua
+---vim.keymap.set('n', 'zS', vim.show_pos)
+---```
+---
 ---@since 11
 ---@param bufnr? integer defaults to the current buffer
 ---@param row? integer row to inspect, 0-based. Defaults to the row of the current cursor
-- 
cgit 


From c1378413c142e1762b80132c41b10c16c4aa7c13 Mon Sep 17 00:00:00 2001
From: dundargoc 
Date: Sun, 1 Dec 2024 20:30:39 +0100
Subject: vim-patch:768728b: runtime(doc): Update documentation for "noselect"
 in 'completeopt'

In particular, make the distinction and interaction between "noinsert"
and "noselect" clearer as it was very confusing before.

closes: vim/vim#16148

https://github.com/vim/vim/commit/768728b48751c5e937409d12d98bfa1fb4c37266

Co-authored-by: dundargoc 
---
 runtime/lua/vim/_meta/options.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 247b464a70..01d5235d94 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -1086,9 +1086,9 @@ vim.go.cia = vim.go.completeitemalign
 --- 	    a match from the menu. Only works in combination with
 --- 	    "menu" or "menuone". No effect if "longest" is present.
 ---
----    noselect Do not select a match in the menu, force the user to
---- 	    select one from the menu. Only works in combination with
---- 	    "menu" or "menuone".
+---    noselect Same as "noinsert", except that no menu item is
+--- 	    pre-selected. If both "noinsert" and "noselect" are present,
+--- 	    "noselect" has precedence.
 ---
 ---    fuzzy    Enable `fuzzy-matching` for completion candidates. This
 --- 	    allows for more flexible and intuitive matching, where
-- 
cgit 


From 49d6cd1da86cab49c7a5a8c79e59d48d016975fa Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Mon, 2 Dec 2024 12:13:09 -0600
Subject: docs: provide example for configuring LSP foldexpr (#31411)

Using the "supports_method" function with a client capability inside of
an LspAttach autocommand is the preferred method to do this, so we
should be showing users how to do it.
---
 runtime/lua/vim/lsp.lua | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index a3791e15c3..b1a3316e3e 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1097,6 +1097,22 @@ function lsp.tagfunc(pattern, flags)
 end
 
 --- Provides an interface between the built-in client and a `foldexpr` function.
+---
+--- To use, check for the "textDocument/foldingRange" capability in an
+--- |LspAttach| autocommand. Example:
+---
+--- ```lua
+--- vim.api.nvim_create_autocommand('LspAttach', {
+---   callback = function(args)
+---     local client = vim.lsp.get_client_by_id(args.data.client_id)
+---     if client:supports_method('textDocument/foldingRange') then
+---       vim.wo.foldmethod = 'expr'
+---       vim.wo.foldexpr = 'v:lua.vim.lsp.foldexpr()'
+---     end
+---   end,
+--- })
+--- ```
+---
 ---@param lnum integer line number
 function lsp.foldexpr(lnum)
   return vim.lsp._folding_range.foldexpr(lnum)
-- 
cgit 


From ae93c7f369a174f3d738ab55030de2c9dfc10c57 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Tue, 3 Dec 2024 09:44:28 -0800
Subject: docs: misc, help tags for neovim.io searches #31428

Problem:
Various keywords are commonly searched-for on https://neovim.io, but
don't have help tags.

Solution:
Add help tags.

fix #31327
---
 runtime/lua/vim/_meta/api.lua   | 14 +++++++++++++-
 runtime/lua/vim/_meta/vimfn.lua | 13 +++++++------
 2 files changed, 20 insertions(+), 7 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index acd12b353d..d74ee11b46 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -1654,7 +1654,7 @@ function vim.api.nvim_notify(msg, log_level, opts) end
 --- Open a terminal instance in a buffer
 ---
 --- By default (and currently the only option) the terminal will not be
---- connected to an external process. Instead, input send on the channel
+--- connected to an external process. Instead, input sent on the channel
 --- will be echoed directly by the terminal. This is useful to display
 --- ANSI terminal sequences returned as part of a rpc message, or similar.
 ---
@@ -1665,6 +1665,18 @@ function vim.api.nvim_notify(msg, log_level, opts) end
 --- Then `nvim_chan_send()` can be called immediately to process sequences
 --- in a virtual terminal having the intended size.
 ---
+--- Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you
+--- can use Nvim as a "scrollback pager" (for terminals like kitty): [terminal-scrollback-pager]()
+---
+--- ```lua
+--- vim.api.nvim_create_user_command('TermHl', function()
+---   local b = vim.api.nvim_create_buf(false, true)
+---   local chan = vim.api.nvim_open_term(b, {})
+---   vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n'))
+---   vim.api.nvim_win_set_buf(0, b)
+--- end, { desc = 'Highlights ANSI termcodes in curbuf' })
+--- ```
+---
 --- @param buffer integer the buffer to use (expected to be empty)
 --- @param opts vim.api.keyset.open_term Optional parameters.
 --- - on_input: Lua callback for input sent, i e keypresses in terminal
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index f9b5d93a4b..e207f641de 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -4928,7 +4928,7 @@ function vim.fn.jobstop(id) end
 ---
 --- @param jobs integer[]
 --- @param timeout? integer
---- @return any
+--- @return integer[]
 function vim.fn.jobwait(jobs, timeout) end
 
 --- Join the items in {list} together into one String.
@@ -7244,9 +7244,9 @@ function vim.fn.round(expr) end
 ---
 --- @param channel integer
 --- @param event string
---- @param args? any
+--- @param ... any
 --- @return any
-function vim.fn.rpcnotify(channel, event, args) end
+function vim.fn.rpcnotify(channel, event, ...) end
 
 --- Sends a request to {channel} to invoke {method} via
 --- |RPC| and blocks until a response is received.
@@ -7256,9 +7256,9 @@ function vim.fn.rpcnotify(channel, event, args) end
 ---
 --- @param channel integer
 --- @param method string
---- @param args? any
+--- @param ... any
 --- @return any
-function vim.fn.rpcrequest(channel, method, args) end
+function vim.fn.rpcrequest(channel, method, ...) end
 
 --- @deprecated
 --- Deprecated. Replace  >vim
@@ -9328,6 +9328,7 @@ function vim.fn.str2float(string, quoted) end
 --- and exists only for backwards-compatibility.
 --- With UTF-8 composing characters are handled properly: >vim
 ---   echo str2list("á")    " returns [97, 769]
+--- <
 ---
 --- @param string string
 --- @param utf8? boolean
@@ -10870,7 +10871,7 @@ function vim.fn.winheight(nr) end
 --- <
 ---
 --- @param tabnr? integer
---- @return any
+--- @return any[]
 function vim.fn.winlayout(tabnr) end
 
 --- The result is a Number, which is the screen line of the cursor
-- 
cgit 


From b079a9d2e76062ee7275e35a4623108550e836a5 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Wed, 4 Dec 2024 17:49:12 +0800
Subject: vim-patch:9.1.0902: filetype: Conda configuration files are not
 recognized (#31445)

Problem:  filetype: Conda configuration files are not recognized
Solution: detect '.condarc' and 'condarc' files as yaml filetype.
          (zeertzjq)

closes: vim/vim#16162

https://github.com/vim/vim/commit/876de275cb3affa5910664cc52a5177c214313e8
---
 runtime/lua/vim/filetype.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 5d771c30e9..b4c37dd160 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1873,6 +1873,8 @@ local filename = {
   ['.clang-tidy'] = 'yaml',
   ['yarn.lock'] = 'yaml',
   matplotlibrc = 'yaml',
+  ['.condarc'] = 'yaml',
+  condarc = 'yaml',
   zathurarc = 'zathurarc',
   ['/etc/zprofile'] = 'zsh',
   ['.zlogin'] = 'zsh',
-- 
cgit 


From e56437cd48f7df87ccdfb79812ee56241c0da0cb Mon Sep 17 00:00:00 2001
From: Maria José Solano 
Date: Wed, 4 Dec 2024 05:14:47 -0800
Subject: feat(lsp): deprecate vim.lsp.start_client #31341

Problem:
LSP module has multiple "start" interfaces.

Solution:
- Enhance vim.lsp.start
- Deprecate vim.lsp.start_client
---
 runtime/lua/vim/lsp.lua        | 208 +++++++++++++++++++++++------------------
 runtime/lua/vim/lsp/client.lua |   8 +-
 runtime/lua/vim/lsp/rpc.lua    |   2 +-
 3 files changed, 121 insertions(+), 97 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index b1a3316e3e..4717d7995a 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -211,17 +211,117 @@ local function reuse_client_default(client, config)
   return false
 end
 
+--- Reset defaults set by `set_defaults`.
+--- Must only be called if the last client attached to a buffer exits.
+local function reset_defaults(bufnr)
+  if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
+    vim.bo[bufnr].tagfunc = nil
+  end
+  if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
+    vim.bo[bufnr].omnifunc = nil
+  end
+  if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
+    vim.bo[bufnr].formatexpr = nil
+  end
+  vim._with({ buf = bufnr }, function()
+    local keymap = vim.fn.maparg('K', 'n', false, true)
+    if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
+      vim.keymap.del('n', 'K', { buffer = bufnr })
+    end
+  end)
+end
+
+--- @param code integer
+--- @param signal integer
+--- @param client_id integer
+local function on_client_exit(code, signal, client_id)
+  local client = all_clients[client_id]
+
+  vim.schedule(function()
+    for bufnr in pairs(client.attached_buffers) do
+      if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then
+        api.nvim_exec_autocmds('LspDetach', {
+          buffer = bufnr,
+          modeline = false,
+          data = { client_id = client_id },
+        })
+      end
+
+      client.attached_buffers[bufnr] = nil
+
+      if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
+        reset_defaults(bufnr)
+      end
+    end
+
+    local namespace = vim.lsp.diagnostic.get_namespace(client_id)
+    vim.diagnostic.reset(namespace)
+  end)
+
+  local name = client.name or 'unknown'
+
+  -- Schedule the deletion of the client object so that it exists in the execution of LspDetach
+  -- autocommands
+  vim.schedule(function()
+    all_clients[client_id] = nil
+
+    -- Client can be absent if executable starts, but initialize fails
+    -- init/attach won't have happened
+    if client then
+      changetracking.reset(client)
+    end
+    if code ~= 0 or (signal ~= 0 and signal ~= 15) then
+      local msg = string.format(
+        'Client %s quit with exit code %s and signal %s. Check log for errors: %s',
+        name,
+        code,
+        signal,
+        lsp.get_log_path()
+      )
+      vim.notify(msg, vim.log.levels.WARN)
+    end
+  end)
+end
+
+--- Creates and initializes a client with the given configuration.
+--- @param config vim.lsp.ClientConfig Configuration for the server.
+--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
+---         fully initialized. Use `on_init` to do any actions once
+---         the client has been initialized.
+--- @return string? # Error message, if any
+local function create_and_initialize_client(config)
+  local ok, res = pcall(require('vim.lsp.client').create, config)
+  if not ok then
+    return nil, res --[[@as string]]
+  end
+
+  local client = assert(res)
+
+  --- @diagnostic disable-next-line: invisible
+  table.insert(client._on_exit_cbs, on_client_exit)
+
+  all_clients[client.id] = client
+
+  client:initialize()
+
+  return client.id, nil
+end
+
 --- @class vim.lsp.start.Opts
 --- @inlinedoc
 ---
 --- Predicate used to decide if a client should be re-used. Used on all
 --- running clients. The default implementation re-uses a client if name and
 --- root_dir matches.
---- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
+--- @field reuse_client? (fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean)
 ---
 --- Buffer handle to attach to if starting or re-using a client (0 for current).
 --- @field bufnr? integer
 ---
+--- Whether to attach the client to a buffer (default true).
+--- If set to `false`, `reuse_client` and `bufnr` will be ignored.
+--- @field attach? boolean
+---
 --- Suppress error reporting if the LSP server fails to start (default false).
 --- @field silent? boolean
 
@@ -239,10 +339,10 @@ end
 --- })
 --- ```
 ---
---- See |vim.lsp.start_client()| for all available options. The most important are:
+--- See |vim.lsp.ClientConfig| for all available options. The most important are:
 ---
 --- - `name` arbitrary name for the LSP client. Should be unique per language server.
---- - `cmd` command string[] or function, described at |vim.lsp.start_client()|.
+--- - `cmd` command string[] or function.
 --- - `root_dir` path to the project root. By default this is used to decide if an existing client
 ---   should be re-used. The example above uses |vim.fs.root()| to detect the root by traversing
 ---   the file system upwards starting from the current directory until either a `pyproject.toml`
@@ -262,7 +362,7 @@ end
 --- `ftplugin/.lua` (See |ftplugin-name|)
 ---
 --- @param config vim.lsp.ClientConfig Configuration for the server.
---- @param opts vim.lsp.start.Opts? Optional keyword arguments
+--- @param opts vim.lsp.start.Opts? Optional keyword arguments.
 --- @return integer? client_id
 function lsp.start(config, opts)
   opts = opts or {}
@@ -271,6 +371,10 @@ function lsp.start(config, opts)
 
   for _, client in pairs(all_clients) do
     if reuse_client(client, config) then
+      if opts.attach == false then
+        return client.id
+      end
+
       if lsp.buf_attach_client(bufnr, client.id) then
         return client.id
       else
@@ -279,7 +383,7 @@ function lsp.start(config, opts)
     end
   end
 
-  local client_id, err = lsp.start_client(config)
+  local client_id, err = create_and_initialize_client(config)
   if err then
     if not opts.silent then
       vim.notify(err, vim.log.levels.WARN)
@@ -287,6 +391,10 @@ function lsp.start(config, opts)
     return nil
   end
 
+  if opts.attach == false then
+    return client_id
+  end
+
   if client_id and lsp.buf_attach_client(bufnr, client_id) then
     return client_id
   end
@@ -383,78 +491,7 @@ function lsp._set_defaults(client, bufnr)
   end
 end
 
---- Reset defaults set by `set_defaults`.
---- Must only be called if the last client attached to a buffer exits.
-local function reset_defaults(bufnr)
-  if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
-    vim.bo[bufnr].tagfunc = nil
-  end
-  if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
-    vim.bo[bufnr].omnifunc = nil
-  end
-  if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
-    vim.bo[bufnr].formatexpr = nil
-  end
-  vim._with({ buf = bufnr }, function()
-    local keymap = vim.fn.maparg('K', 'n', false, true)
-    if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
-      vim.keymap.del('n', 'K', { buffer = bufnr })
-    end
-  end)
-end
-
---- @param code integer
---- @param signal integer
---- @param client_id integer
-local function on_client_exit(code, signal, client_id)
-  local client = all_clients[client_id]
-
-  vim.schedule(function()
-    for bufnr in pairs(client.attached_buffers) do
-      if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then
-        api.nvim_exec_autocmds('LspDetach', {
-          buffer = bufnr,
-          modeline = false,
-          data = { client_id = client_id },
-        })
-      end
-
-      client.attached_buffers[bufnr] = nil
-
-      if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
-        reset_defaults(bufnr)
-      end
-    end
-
-    local namespace = vim.lsp.diagnostic.get_namespace(client_id)
-    vim.diagnostic.reset(namespace)
-  end)
-
-  local name = client.name or 'unknown'
-
-  -- Schedule the deletion of the client object so that it exists in the execution of LspDetach
-  -- autocommands
-  vim.schedule(function()
-    all_clients[client_id] = nil
-
-    -- Client can be absent if executable starts, but initialize fails
-    -- init/attach won't have happened
-    if client then
-      changetracking.reset(client)
-    end
-    if code ~= 0 or (signal ~= 0 and signal ~= 15) then
-      local msg = string.format(
-        'Client %s quit with exit code %s and signal %s. Check log for errors: %s',
-        name,
-        code,
-        signal,
-        lsp.get_log_path()
-      )
-      vim.notify(msg, vim.log.levels.WARN)
-    end
-  end)
-end
-
+--- @deprecated
 --- Starts and initializes a client with the given configuration.
 --- @param config vim.lsp.ClientConfig Configuration for the server.
 --- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
@@ -462,21 +499,8 @@ end
 ---         the client has been initialized.
 --- @return string? # Error message, if any
 function lsp.start_client(config)
-  local ok, res = pcall(require('vim.lsp.client').create, config)
-  if not ok then
-    return nil, res --[[@as string]]
-  end
-
-  local client = assert(res)
-
-  --- @diagnostic disable-next-line: invisible
-  table.insert(client._on_exit_cbs, on_client_exit)
-
-  all_clients[client.id] = client
-
-  client:initialize()
-
-  return client.id, nil
+  vim.deprecate('vim.lsp.start_client()', 'vim.lsp.start()', '0.13')
+  return create_and_initialize_client(config)
 end
 
 ---Buffer lifecycle handler for textDocument/didSave
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index a14b6ccda6..a83d75bf75 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -78,7 +78,7 @@ local validate = vim.validate
 --- @field settings? table
 ---
 --- Table that maps string of clientside commands to user-defined functions.
---- Commands passed to start_client take precedence over the global command registry. Each key
+--- Commands passed to `start()` take precedence over the global command registry. Each key
 --- must be a unique command name, and the value is a function which is called if any LSP action
 --- (code action, code lenses, ...) triggers the command.
 --- @field commands? table
@@ -104,7 +104,7 @@ local validate = vim.validate
 --- @field on_error? fun(code: integer, err: string)
 ---
 --- Callback invoked before the LSP "initialize" phase, where `params` contains the parameters
---- being sent to the server and `config` is the config that was passed to |vim.lsp.start_client()|.
+--- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|.
 --- You can use this to modify parameters before they are sent.
 --- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)
 ---
@@ -167,7 +167,7 @@ local validate = vim.validate
 --- @field requests table
 ---
 --- copy of the table that was passed by the user
---- to |vim.lsp.start_client()|.
+--- to |vim.lsp.start()|.
 --- @field config vim.lsp.ClientConfig
 ---
 --- Response from the server sent on `initialize` describing the server's
@@ -307,7 +307,7 @@ local function default_get_language_id(_bufnr, filetype)
   return filetype
 end
 
---- Validates a client configuration as given to |vim.lsp.start_client()|.
+--- Validates a client configuration as given to |vim.lsp.start()|.
 --- @param config vim.lsp.ClientConfig
 local function validate_config(config)
   validate('config', config, 'table')
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 6c8564845f..2327a37ab1 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -617,7 +617,7 @@ end
 ---  - a host and port via TCP
 ---
 --- Return a function that can be passed to the `cmd` field for
---- |vim.lsp.start_client()| or |vim.lsp.start()|.
+--- |vim.lsp.start()|.
 ---
 ---@param host_or_path string host to connect to or path to a pipe/domain socket
 ---@param port integer? TCP port to connect to. If absent the first argument must be a pipe
-- 
cgit 


From 56d11b494b54862a9b55ca1efcaa35f26c56b3ff Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Wed, 4 Dec 2024 08:40:01 -0600
Subject: defaults: disable 'number', 'relativenumber', and 'signcolumn' in
 terminal buffers (#31443)

---
 runtime/lua/vim/_defaults.lua | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 6583cf48b3..ef83a3ccc3 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -492,6 +492,9 @@ do
       vim.bo.textwidth = 0
       vim.wo[0][0].wrap = false
       vim.wo[0][0].list = false
+      vim.wo[0][0].number = false
+      vim.wo[0][0].relativenumber = false
+      vim.wo[0][0].signcolumn = 'no'
 
       -- This is gross. Proper list options support when?
       local winhl = vim.o.winhighlight
-- 
cgit 


From 734dba04d13bc7a6714134af322d49f333bfdc4c Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Tue, 3 Dec 2024 18:06:58 +0000
Subject: fix(vim.system): close pipe handles after process handle

Fixes #30846
---
 runtime/lua/vim/_system.lua | 126 +++++++++++++++++++++++---------------------
 1 file changed, 67 insertions(+), 59 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua
index ce5dbffeaa..c0a0570e13 100644
--- a/runtime/lua/vim/_system.lua
+++ b/runtime/lua/vim/_system.lua
@@ -47,15 +47,6 @@ local function close_handle(handle)
   end
 end
 
----@param state vim.SystemState
-local function close_handles(state)
-  close_handle(state.handle)
-  close_handle(state.stdin)
-  close_handle(state.stdout)
-  close_handle(state.stderr)
-  close_handle(state.timer)
-end
-
 --- @class vim.SystemObj
 --- @field cmd string[]
 --- @field pid integer
@@ -132,9 +123,7 @@ function SystemObj:write(data)
     -- (https://github.com/neovim/neovim/pull/17620#discussion_r820775616)
     stdin:write('', function()
       stdin:shutdown(function()
-        if stdin then
-          stdin:close()
-        end
+        close_handle(stdin)
       end)
     end)
   end
@@ -146,25 +135,52 @@ function SystemObj:is_closing()
   return handle == nil or handle:is_closing() or false
 end
 
----@param output fun(err:string?, data: string?)|false
----@return uv.uv_stream_t?
----@return fun(err:string?, data: string?)? Handler
-local function setup_output(output)
-  if output == nil then
-    return assert(uv.new_pipe(false)), nil
+--- @param output? uv.read_start.callback|false
+--- @param text? boolean
+--- @return uv.uv_stream_t? pipe
+--- @return uv.read_start.callback? handler
+--- @return string[]? data
+local function setup_output(output, text)
+  if output == false then
+    return
   end
 
+  local bucket --- @type string[]?
+  local handler --- @type uv.read_start.callback
+
   if type(output) == 'function' then
-    return assert(uv.new_pipe(false)), output
+    handler = output
+  else
+    bucket = {}
+    handler = function(err, data)
+      if err then
+        error(err)
+      end
+      if text and data then
+        bucket[#bucket + 1] = data:gsub('\r\n', '\n')
+      else
+        bucket[#bucket + 1] = data
+      end
+    end
   end
 
-  assert(output == false)
-  return nil, nil
+  local pipe = assert(uv.new_pipe(false))
+
+  --- @type uv.read_start.callback
+  local function handler_with_close(err, data)
+    handler(err, data)
+    if data == nil then
+      pipe:read_stop()
+      pipe:close()
+    end
+  end
+
+  return pipe, handler_with_close, bucket
 end
 
----@param input string|string[]|true|nil
----@return uv.uv_stream_t?
----@return string|string[]?
+--- @param input? string|string[]|boolean
+--- @return uv.uv_stream_t?
+--- @return string|string[]?
 local function setup_input(input)
   if not input then
     return
@@ -208,28 +224,6 @@ local function setup_env(env, clear_env)
   return renv
 end
 
---- @param stream uv.uv_stream_t
---- @param text? boolean
---- @param bucket string[]
---- @return fun(err: string?, data: string?)
-local function default_handler(stream, text, bucket)
-  return function(err, data)
-    if err then
-      error(err)
-    end
-    if data ~= nil then
-      if text then
-        bucket[#bucket + 1] = data:gsub('\r\n', '\n')
-      else
-        bucket[#bucket + 1] = data
-      end
-    else
-      stream:read_stop()
-      stream:close()
-    end
-  end
-end
-
 local is_win = vim.fn.has('win32') == 1
 
 local M = {}
@@ -255,9 +249,9 @@ local function spawn(cmd, opts, on_exit, on_error)
   return handle, pid_or_err --[[@as integer]]
 end
 
----@param timeout integer
----@param cb fun()
----@return uv.uv_timer_t
+--- @param timeout integer
+--- @param cb fun()
+--- @return uv.uv_timer_t
 local function timer_oneshot(timeout, cb)
   local timer = assert(uv.new_timer())
   timer:start(timeout, 0, function()
@@ -273,7 +267,12 @@ end
 --- @param signal integer
 --- @param on_exit fun(result: vim.SystemCompleted)?
 local function _on_exit(state, code, signal, on_exit)
-  close_handles(state)
+  close_handle(state.handle)
+  close_handle(state.stdin)
+  close_handle(state.timer)
+
+  -- #30846: Do not close stdout/stderr here, as they may still have data to
+  -- read. They will be closed in uv.read_start on EOF.
 
   local check = assert(uv.new_check())
   check:start(function()
@@ -311,6 +310,15 @@ local function _on_exit(state, code, signal, on_exit)
   end)
 end
 
+--- @param state vim.SystemState
+local function _on_error(state)
+  close_handle(state.handle)
+  close_handle(state.stdin)
+  close_handle(state.stdout)
+  close_handle(state.stderr)
+  close_handle(state.timer)
+end
+
 --- Run a system command
 ---
 --- @param cmd string[]
@@ -324,8 +332,8 @@ function M.run(cmd, opts, on_exit)
 
   opts = opts or {}
 
-  local stdout, stdout_handler = setup_output(opts.stdout)
-  local stderr, stderr_handler = setup_output(opts.stderr)
+  local stdout, stdout_handler, stdout_data = setup_output(opts.stdout, opts.text)
+  local stderr, stderr_handler, stderr_data = setup_output(opts.stderr, opts.text)
   local stdin, towrite = setup_input(opts.stdin)
 
   --- @type vim.SystemState
@@ -335,7 +343,9 @@ function M.run(cmd, opts, on_exit)
     timeout = opts.timeout,
     stdin = stdin,
     stdout = stdout,
+    stdout_data = stdout_data,
     stderr = stderr,
+    stderr_data = stderr_data,
   }
 
   --- @diagnostic disable-next-line:missing-fields
@@ -350,17 +360,15 @@ function M.run(cmd, opts, on_exit)
   }, function(code, signal)
     _on_exit(state, code, signal, on_exit)
   end, function()
-    close_handles(state)
+    _on_error(state)
   end)
 
-  if stdout then
-    state.stdout_data = {}
-    stdout:read_start(stdout_handler or default_handler(stdout, opts.text, state.stdout_data))
+  if stdout and stdout_handler then
+    stdout:read_start(stdout_handler)
   end
 
-  if stderr then
-    state.stderr_data = {}
-    stderr:read_start(stderr_handler or default_handler(stderr, opts.text, state.stderr_data))
+  if stderr and stderr_handler then
+    stderr:read_start(stderr_handler)
   end
 
   local obj = new_systemobj(state)
-- 
cgit 


From 7579af3c514c44581fe33b5c03660cdfda7d658e Mon Sep 17 00:00:00 2001
From: Jeremy Fleischman 
Date: Wed, 4 Dec 2024 07:49:57 -0800
Subject: feat(diagnostic): vim.diagnostic.setqflist improvements #30868

1. Use the new "u" action to update the quickfix list so we don't lose
   our position in the quickfix list when updating it.
2. Rather than creating a new quickfix list each time, update the
   exiting one if we've already created one.
---
 runtime/lua/vim/diagnostic.lua | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 2de996feeb..dbf4f56032 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -2,6 +2,8 @@ local api, if_nil = vim.api, vim.F.if_nil
 
 local M = {}
 
+local _qf_id = nil
+
 --- [diagnostic-structure]()
 ---
 --- Diagnostics use the same indexing as the rest of the Nvim API (i.e. 0-based
@@ -848,9 +850,24 @@ local function set_list(loclist, opts)
   local diagnostics = get_diagnostics(bufnr, opts --[[@as vim.diagnostic.GetOpts]], false)
   local items = M.toqflist(diagnostics)
   if loclist then
-    vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items })
+    vim.fn.setloclist(winnr, {}, 'u', { title = title, items = items })
   else
-    vim.fn.setqflist({}, ' ', { title = title, items = items })
+    -- Check if the diagnostics quickfix list no longer exists.
+    if _qf_id and vim.fn.getqflist({ id = _qf_id }).id == 0 then
+      _qf_id = nil
+    end
+
+    -- If we already have a diagnostics quickfix, update it rather than creating a new one.
+    -- This avoids polluting the finite set of quickfix lists, and preserves the currently selected
+    -- entry.
+    vim.fn.setqflist({}, _qf_id and 'u' or ' ', {
+      title = title,
+      items = items,
+      id = _qf_id,
+    })
+
+    -- Get the id of the newly created quickfix list.
+    _qf_id = vim.fn.getqflist({ id = 0 }).id
   end
   if open then
     api.nvim_command(loclist and 'lwindow' or 'botright cwindow')
-- 
cgit 


From 0a15cd250035b770e771baaabfb071827778347d Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Wed, 4 Dec 2024 10:17:46 -0600
Subject: misc: keep deprecated vim.loader.disable stub (#31450)

Transitional stub to minimize breaking change pain, to be removed after
0.11 release.
---
 runtime/lua/vim/loader.lua | 6 ++++++
 1 file changed, 6 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua
index 71d0188128..c7158673fe 100644
--- a/runtime/lua/vim/loader.lua
+++ b/runtime/lua/vim/loader.lua
@@ -446,6 +446,12 @@ function M.enable(enable)
   end
 end
 
+--- @deprecated
+function M.disable()
+  vim.deprecate('vim.loader.disable', 'vim.loader.enable(false)', '0.12')
+  vim.loader.enable(false)
+end
+
 --- Tracks the time spent in a function
 --- @generic F: function
 --- @param f F
-- 
cgit 


From 2f5e7cbac49ed2fde571c5bf85619afec624f8e8 Mon Sep 17 00:00:00 2001
From: glepnir 
Date: Thu, 5 Dec 2024 17:49:39 +0800
Subject: vim-patch:9.1.0905: Missing information in CompleteDone event
 (#31455)

Problem:  Missing information in CompleteDone event
Solution: add complete_word and complete_type to v:event dict
          (glepnir)

closes: vim/vim#16153

https://github.com/vim/vim/commit/1c5a120a701fcf558617c4e70b5a447778f0e51d
---
 runtime/lua/vim/_meta/vvars.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua
index 8784fdbac9..264907109f 100644
--- a/runtime/lua/vim/_meta/vvars.lua
+++ b/runtime/lua/vim/_meta/vvars.lua
@@ -197,6 +197,8 @@ vim.v.errors = ...
 ---                    changing window  (or tab) on `DirChanged`.
 ---   status           Job status or exit code, -1 means "unknown". `TermClose`
 ---   reason           Reason for completion being done. `CompleteDone`
+---   complete_word    The word that was selected, empty if abandoned complete.
+---   complete_type    See `complete_info_mode`
 --- @type any
 vim.v.event = ...
 
-- 
cgit 


From 8323398bc6081af3f79b220b886e3c0373129c7e Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 5 Dec 2024 19:03:58 +0800
Subject: fix(defaults): don't replace keycodes in Visual search mappings
 (#31460)

Also remove "silent" to be more consistent with Normal mode search.
---
 runtime/lua/vim/_defaults.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index ef83a3ccc3..f891c3baa4 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -49,10 +49,10 @@ do
 
     vim.keymap.set('x', '*', function()
       return _visual_search('/')
-    end, { desc = ':help v_star-default', expr = true, silent = true })
+    end, { desc = ':help v_star-default', expr = true, replace_keycodes = false })
     vim.keymap.set('x', '#', function()
       return _visual_search('?')
-    end, { desc = ':help v_#-default', expr = true, silent = true })
+    end, { desc = ':help v_#-default', expr = true, replace_keycodes = false })
   end
 
   --- Map Y to y$. This mimics the behavior of D and C. See |Y-default|
-- 
cgit 


From fd902b1cb2463e220e5bd5fe37a0cfdd259ff35a Mon Sep 17 00:00:00 2001
From: Jeremy Fleischman 
Date: Thu, 5 Dec 2024 13:59:33 -0800
Subject: fix(diagnostic): only store quickfix id when creating a new one
 #31466

The old code would always update `_qf_id` with the current quickfix,
even if you're currently looking at a completely different,
non-diagnostics quickfix list. This completely defeats the intent of
, whoops!
---
 runtime/lua/vim/diagnostic.lua | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index dbf4f56032..b8ffb26377 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -867,7 +867,9 @@ local function set_list(loclist, opts)
     })
 
     -- Get the id of the newly created quickfix list.
-    _qf_id = vim.fn.getqflist({ id = 0 }).id
+    if _qf_id == nil then
+      _qf_id = vim.fn.getqflist({ id = 0 }).id
+    end
   end
   if open then
     api.nvim_command(loclist and 'lwindow' or 'botright cwindow')
-- 
cgit 


From 812d02970206d5a65819e076bcddedd92e083a19 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 6 Dec 2024 09:20:32 +0100
Subject: vim-patch:9.1.0906: filetype: Nvidia PTX files are not recognized

Problem:  filetype: Nvidia PTX files are not recognized
Solution: detect '*.ptx' files as ptx filetype (Yinzuo Jiang)

Reference: https://docs.nvidia.com/cuda/parallel-thread-execution/

closes: vim/vim#16171

https://github.com/vim/vim/commit/bdb5f85a5189534653f36e92b1bc780ca8d25218

Co-authored-by: Yinzuo Jiang 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index b4c37dd160..b983e7d1c6 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -958,6 +958,7 @@ local extension = {
   ps1xml = 'ps1xml',
   psf = 'psf',
   psl = 'psl',
+  ptx = 'ptx',
   pug = 'pug',
   purs = 'purescript',
   arr = 'pyret',
-- 
cgit 


From e9f4ceeb7467364554ecef770fd3380e89457abb Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 6 Dec 2024 20:01:28 +0800
Subject: fix(events): don't expand `args.file` for Lua callback (#31473)

Problem:  In an autocommand Lua callback whether `args.file` is expanded
          depends on whether `expand('')` has been called.
Solution: Always use the unexpanded file name for `args.file`.

Related to #31306 and vim/vim#16106. This doesn't provide `sfname`, but
at least makes `args.file` have a consistent value.
---
 runtime/lua/vim/_meta/api.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index d74ee11b46..b2385197bd 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -963,9 +963,9 @@ function vim.api.nvim_create_augroup(name, opts) end
 ---     - id: (number) autocommand id
 ---     - event: (string) name of the triggered event `autocmd-events`
 ---     - group: (number|nil) autocommand group id, if any
----     - match: (string) expanded value of []
----     - buf: (number) expanded value of []
----     - file: (string) expanded value of []
+---     - file: (string) [] (not expanded to a full path)
+---     - match: (string) [] (expanded to a full path)
+---     - buf: (number) []
 ---     - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]()
 --- - command (string) optional: Vim command to execute on event. Cannot be used with
 --- {callback}
-- 
cgit 


From e8e3b443f8040329bd833fcc945d5dbf3adb832c Mon Sep 17 00:00:00 2001
From: Micah Halter 
Date: Fri, 6 Dec 2024 11:19:24 -0500
Subject: feat(defaults): disable 'foldcolumn' in terminal buffers (#31480)

---
 runtime/lua/vim/_defaults.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index f891c3baa4..0b8a54e957 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -495,6 +495,7 @@ do
       vim.wo[0][0].number = false
       vim.wo[0][0].relativenumber = false
       vim.wo[0][0].signcolumn = 'no'
+      vim.wo[0][0].foldcolumn = '0'
 
       -- This is gross. Proper list options support when?
       local winhl = vim.o.winhighlight
-- 
cgit 


From b8c75a31e6f4716f542cd2000e4a7c19c1ae9d70 Mon Sep 17 00:00:00 2001
From: Riley Bruins 
Date: Sat, 17 Aug 2024 21:05:09 -0700
Subject: feat(treesitter): #trim! can trim all whitespace

This commit also implements more generic trimming, acting on all
whitespace (charwise) rather than just empty lines.

It will unblock
https://github.com/nvim-treesitter/nvim-treesitter/pull/3442 and allow
for properly concealing markdown bullet markers regardless of indent
width, e.g.
---
 runtime/lua/vim/treesitter/query.lua | 48 +++++++++++++++++++++++++-----------
 1 file changed, 34 insertions(+), 14 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 1677e8d364..3c7bc2eb89 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -572,13 +572,17 @@ local directive_handlers = {
 
     metadata[id].text = text:gsub(pattern, replacement)
   end,
-  -- Trim blank lines from end of the node
-  -- Example: (#trim! @fold)
-  -- TODO(clason): generalize to arbitrary whitespace removal
+  -- Trim whitespace from both sides of the node
+  -- Example: (#trim! @fold 1 1 1 1)
   ['trim!'] = function(match, _, bufnr, pred, metadata)
     local capture_id = pred[2]
     assert(type(capture_id) == 'number')
 
+    local trim_start_lines = pred[3] == '1'
+    local trim_start_cols = pred[4] == '1'
+    local trim_end_lines = pred[5] == '1' or not pred[3] -- default true for backwards compatibility
+    local trim_end_cols = pred[6] == '1'
+
     local nodes = match[capture_id]
     if not nodes or #nodes == 0 then
       return
@@ -588,20 +592,36 @@ local directive_handlers = {
 
     local start_row, start_col, end_row, end_col = node:range()
 
-    -- Don't trim if region ends in middle of a line
-    if end_col ~= 0 then
-      return
-    end
-
-    while end_row >= start_row do
-      -- As we only care when end_col == 0, always inspect one line above end_row.
-      local end_line = api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1]
+    local node_text = vim.split(vim.treesitter.get_node_text(node, bufnr), '\n')
+    local end_idx = #node_text
+    local start_idx = 1
 
-      if end_line ~= '' then
-        break
+    if trim_end_lines then
+      while end_idx > 0 and node_text[end_idx]:find('^%s*$') do
+        end_idx = end_idx - 1
+        end_row = end_row - 1
       end
+    end
+    if trim_end_cols then
+      if end_idx == 0 then
+        end_row = start_row
+        end_col = start_col
+      else
+        local whitespace_start = node_text[end_idx]:find('(%s*)$')
+        end_col = (whitespace_start - 1) + (end_idx == 1 and start_col or 0)
+      end
+    end
 
-      end_row = end_row - 1
+    if trim_start_lines then
+      while start_idx <= end_idx and node_text[start_idx]:find('^%s*$') do
+        start_idx = start_idx + 1
+        start_row = start_row + 1
+      end
+    end
+    if trim_start_cols and node_text[start_idx] then
+      local _, whitespace_end = node_text[start_idx]:find('^(%s*)')
+      whitespace_end = whitespace_end or 0
+      start_col = (start_idx == 1 and start_col or 0) + whitespace_end
     end
 
     -- If this produces an invalid range, we just skip it.
-- 
cgit 


From fac96b72a59e15d483e03bdc43b9ba08ce6c828e Mon Sep 17 00:00:00 2001
From: Tristan Knight 
Date: Fri, 6 Dec 2024 18:09:07 +0000
Subject: fix(lsp): add foldingrange method support check #31463

Problem: The folding_range request method assumes that the client
supports the method

Solution: Add a capability guard to the call
---
 runtime/lua/vim/lsp/_folding_range.lua | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua
index 6a445017a3..2f1767aaf5 100644
--- a/runtime/lua/vim/lsp/_folding_range.lua
+++ b/runtime/lua/vim/lsp/_folding_range.lua
@@ -271,7 +271,9 @@ local function setup(bufnr)
     buffer = bufnr,
     callback = function(args)
       local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
-      request(bufnr, client)
+      if client:supports_method(vim.lsp.protocol.Methods.textDocument_foldingRange, bufnr) then
+        request(bufnr, client)
+      end
     end,
   })
   api.nvim_create_autocmd('LspNotify', {
-- 
cgit 


From 517ecb85f58ed6ac8b4d5443931612e75e7c7dc2 Mon Sep 17 00:00:00 2001
From: Bartłomiej Maryńczak 
Date: Fri, 6 Dec 2024 21:43:41 +0100
Subject: feat(stdlib): vim.json.encode(...,{escape_slash:boolean}) #30561

Problem:
vim.json.encode escapes every slash in string values (for example in
file paths), and is not optional. Use-case is for preventing HTML
injections (eg. injecting `` closing tag); in the context of
Nvim this is rarely useful.

Solution:
- Add a `escape_slash` flag to `vim.json.encode`.
- Defaults to `false`. (This is a "breaking" change, but more like
  a bug fix.)
---
 runtime/lua/vim/_meta/json.lua | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/json.lua b/runtime/lua/vim/_meta/json.lua
index 07d89aafc8..1a7e87db9c 100644
--- a/runtime/lua/vim/_meta/json.lua
+++ b/runtime/lua/vim/_meta/json.lua
@@ -35,5 +35,8 @@ function vim.json.decode(str, opts) end
 
 --- Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
 ---@param obj any
+---@param opts? table Options table with keys:
+---                                 - escape_slash: (boolean) (default false) When true, escapes `/`
+---                                                           character in JSON strings
 ---@return string
-function vim.json.encode(obj) end
+function vim.json.encode(obj, opts) end
-- 
cgit 


From ec94c2704f5059794923777ed51412d80bd26b5b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 7 Dec 2024 10:17:36 +0800
Subject: vim-patch:9.1.0908: not possible to configure :messages (#31492)

Problem:  not possible to configure :messages
Solution: add the 'messagesopt' option (Shougo Matsushita)

closes: vim/vim#16068

https://github.com/vim/vim/commit/51d4d84d6a7159c6ce9e04b36f8edc105ca3794b

Co-authored-by: Shougo Matsushita 
Co-authored-by: h_east 
---
 runtime/lua/vim/_meta/options.lua | 34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 01d5235d94..59d270e640 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -3016,7 +3016,7 @@ vim.go.hid = vim.go.hidden
 
 --- A history of ":" commands, and a history of previous search patterns
 --- is remembered.  This option decides how many entries may be stored in
---- each of these histories (see `cmdline-editing` and 'msghistory' for
+--- each of these histories (see `cmdline-editing` and 'messagesopt' for
 --- the number of messages to remember).
 --- The maximum value is 10000.
 ---
@@ -4084,6 +4084,28 @@ vim.o.mis = vim.o.menuitems
 vim.go.menuitems = vim.o.menuitems
 vim.go.mis = vim.go.menuitems
 
+--- Option settings when outputting messages.  It can consist of the
+--- following items.  Items must be separated by a comma.
+---
+--- hit-enter	Use `hit-enter` prompt when the message is longer than
+--- 		'cmdheight' size.
+---
+--- wait:{n}	Ignored when "hit-enter" is present.  Instead of using
+--- 		`hit-enter` prompt, will simply wait for {n}
+--- 		milliseconds so the user has a chance to read the
+--- 		message, use 0 to disable sleep (but then the user may
+--- 		miss an important message).
+---
+--- history:{n}	Determines how many entries are remembered in the
+--- 		`:messages` history.  The maximum value is 10000.
+--- 		Setting it to zero clears the message history.
+---
+--- @type string
+vim.o.messagesopt = "hit-enter,history:500"
+vim.o.mopt = vim.o.messagesopt
+vim.go.messagesopt = vim.o.messagesopt
+vim.go.mopt = vim.go.messagesopt
+
 --- Parameters for `:mkspell`.  This tunes when to start compressing the
 --- word tree.  Compression can be slow when there are many words, but
 --- it's needed to avoid running out of memory.  The amount of memory used
@@ -4379,16 +4401,6 @@ vim.o.mouset = vim.o.mousetime
 vim.go.mousetime = vim.o.mousetime
 vim.go.mouset = vim.go.mousetime
 
---- Determines how many entries are remembered in the `:messages` history.
---- The maximum value is 10000.
---- Setting it to zero clears the message history.
----
---- @type integer
-vim.o.msghistory = 500
-vim.o.mhi = vim.o.msghistory
-vim.go.msghistory = vim.o.msghistory
-vim.go.mhi = vim.go.msghistory
-
 --- This defines what bases Vim will consider for numbers when using the
 --- CTRL-A and CTRL-X commands for adding to and subtracting from a number
 --- respectively; see `CTRL-A` for more info on these commands.
-- 
cgit 


From 9c278af7cc96e5b7f07cd9fdec27951651063a76 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 6 Dec 2024 19:18:03 +0100
Subject: fix(inspect): show priority for treesitter highlights

Problem: `:Inspect` does not show priority for treesitter highlights,
leading to confusion why sometimes earlier highlights override later
highlights.

Solution: Also print priority metadata if set.
---
 runtime/lua/vim/_inspector.lua | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index f4e41a31e5..520c437906 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -191,7 +191,12 @@ function vim.show_pos(bufnr, row, col, filter)
     append('Treesitter', 'Title')
     nl()
     for _, capture in ipairs(items.treesitter) do
-      item(capture, capture.lang)
+      item(
+        capture,
+        capture.metadata.priority
+            and string.format('%s priority: %d', capture.lang, capture.metadata.priority)
+          or capture.lang
+      )
     end
     nl()
   end
-- 
cgit 


From c2bf09ddff4994f901350dd02412087a8abfde0a Mon Sep 17 00:00:00 2001
From: Maria José Solano 
Date: Sun, 24 Nov 2024 13:43:27 -0800
Subject: fix(lsp): check for configuration workspace folders when reusing
 clients

---
 runtime/lua/vim/lsp.lua        | 46 +++++++++++++++++++++++++++++++-----------
 runtime/lua/vim/lsp/client.lua | 21 +++----------------
 2 files changed, 37 insertions(+), 30 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 4717d7995a..b67b2d6988 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -114,6 +114,22 @@ function lsp._unsupported_method(method)
   return msg
 end
 
+---@private
+---@param workspace_folders string|lsp.WorkspaceFolder[]?
+---@return lsp.WorkspaceFolder[]?
+function lsp._get_workspace_folders(workspace_folders)
+  if type(workspace_folders) == 'table' then
+    return workspace_folders
+  elseif type(workspace_folders) == 'string' then
+    return {
+      {
+        uri = vim.uri_from_fname(workspace_folders),
+        name = workspace_folders,
+      },
+    }
+  end
+end
+
 local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'error' }
 
 local format_line_ending = {
@@ -196,19 +212,24 @@ local function reuse_client_default(client, config)
     return false
   end
 
-  if config.root_dir then
-    local root = vim.uri_from_fname(config.root_dir)
-    for _, dir in ipairs(client.workspace_folders or {}) do
-      -- note: do not need to check client.root_dir since that should be client.workspace_folders[1]
-      if root == dir.uri then
-        return true
+  local config_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir)
+    or {}
+  local config_folders_included = 0
+
+  if not next(config_folders) then
+    return false
+  end
+
+  for _, config_folder in ipairs(config_folders) do
+    for _, client_folder in ipairs(client.workspace_folders) do
+      if config_folder.uri == client_folder.uri then
+        config_folders_included = config_folders_included + 1
+        break
       end
     end
   end
 
-  -- TODO(lewis6991): also check config.workspace_folders
-
-  return false
+  return config_folders_included == #config_folders
 end
 
 --- Reset defaults set by `set_defaults`.
@@ -311,9 +332,10 @@ end
 --- @inlinedoc
 ---
 --- Predicate used to decide if a client should be re-used. Used on all
---- running clients. The default implementation re-uses a client if name and
---- root_dir matches.
---- @field reuse_client? (fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean)
+--- running clients. The default implementation re-uses a client if it has the
+--- same name and if the given workspace folders (or root_dir) are all included
+--- in the client's workspace folders.
+--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
 ---
 --- Buffer handle to attach to if starting or re-using a client (0 for current).
 --- @field bufnr? integer
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index a83d75bf75..7eb023da39 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -365,21 +365,6 @@ local function get_name(id, config)
   return tostring(id)
 end
 
---- @param workspace_folders string|lsp.WorkspaceFolder[]?
---- @return lsp.WorkspaceFolder[]?
-local function get_workspace_folders(workspace_folders)
-  if type(workspace_folders) == 'table' then
-    return workspace_folders
-  elseif type(workspace_folders) == 'string' then
-    return {
-      {
-        uri = vim.uri_from_fname(workspace_folders),
-        name = workspace_folders,
-      },
-    }
-  end
-end
-
 --- @generic T
 --- @param x elem_or_list?
 --- @return T[]
@@ -417,7 +402,7 @@ function Client.create(config)
     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 or config.root_dir),
+    workspace_folders = lsp._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),
@@ -1174,7 +1159,7 @@ function Client:_add_workspace_folder(dir)
     end
   end
 
-  local wf = assert(get_workspace_folders(dir))
+  local wf = assert(lsp._get_workspace_folders(dir))
 
   self:notify(ms.workspace_didChangeWorkspaceFolders, {
     event = { added = wf, removed = {} },
@@ -1189,7 +1174,7 @@ end
 --- Remove a directory to the workspace folders.
 --- @param dir string?
 function Client:_remove_workspace_folder(dir)
-  local wf = assert(get_workspace_folders(dir))
+  local wf = assert(lsp._get_workspace_folders(dir))
 
   self:notify(ms.workspace_didChangeWorkspaceFolders, {
     event = { added = {}, removed = wf },
-- 
cgit 


From bdfba8598b41b891e1fcc8b96163f442baf509b4 Mon Sep 17 00:00:00 2001
From: tris203 
Date: Fri, 6 Dec 2024 16:25:13 +0000
Subject: fix(lsp): cancel pending requests before refreshing

Problem:
Diagnostics and inlay hints can be expensive to calculate, and we
shouldn't stack them as this can cause noticeable lag.

Solution:
Check for duplicate inflight requests and cancel them before issuing a new one.
This ensures that only the latest request is processed, improving
performance and preventing potential conflicts.
---
 runtime/lua/vim/lsp/util.lua | 5 +++++
 1 file changed, 5 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index ced8aa5745..5bf436f2ca 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -2162,6 +2162,11 @@ 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
+          for rid, req in pairs(client.requests) do
+            if req.method == method and req.type == 'pending' and req.bufnr == bufnr then
+              client:cancel_request(rid)
+            end
+          end
           client:request(method, {
             textDocument = textDocument,
             range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding),
-- 
cgit 


From 5c245ec3e95570e515c1665a2ec694828706ac52 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 6 Dec 2024 17:09:49 +0000
Subject: fix: remove vim.lsp._with_extend

Not used anywhere.
---
 runtime/lua/vim/lsp.lua | 38 --------------------------------------
 1 file changed, 38 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index b67b2d6988..e1946816da 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1279,44 +1279,6 @@ function lsp.with(handler, override_config)
   end
 end
 
---- Helper function to use when implementing a handler.
---- This will check that all of the keys in the user configuration
---- are valid keys and make sense to include for this handler.
----
---- Will error on invalid keys (i.e. keys that do not exist in the options)
---- @param name string
---- @param options table
---- @param user_config table
-function lsp._with_extend(name, options, user_config)
-  user_config = user_config or {}
-
-  local resulting_config = {} --- @type table
-  for k, v in pairs(user_config) do
-    if options[k] == nil then
-      error(
-        debug.traceback(
-          string.format(
-            'Invalid option for `%s`: %s. Valid options are:\n%s',
-            name,
-            k,
-            vim.inspect(vim.tbl_keys(options))
-          )
-        )
-      )
-    end
-
-    resulting_config[k] = v
-  end
-
-  for k, v in pairs(options) do
-    if resulting_config[k] == nil then
-      resulting_config[k] = v
-    end
-  end
-
-  return resulting_config
-end
-
 --- Registry for client side commands.
 --- This is an extension point for plugins to handle custom commands which are
 --- not part of the core language server protocol specification.
-- 
cgit 


From c63e49cce2d20cee129a6312319dde8dcea6e3f6 Mon Sep 17 00:00:00 2001
From: Riley Bruins 
Date: Sat, 7 Dec 2024 03:01:59 -0800
Subject: fix(treesitter): #trim! range for nodes ending at col 0 #31488

Problem:
char-wise folding for `#trim!` ranges are improperly calculated for nodes that
end at column 0, due to the way `get_node_text` works.

Solution:
Add the blank line that `get_node_text` removes for for nodes ending at column
0. Also properly set column positions when performing linewise trims.
---
 runtime/lua/vim/treesitter/query.lua | 9 +++++++++
 1 file changed, 9 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 3c7bc2eb89..dbe3d54c2f 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -593,6 +593,11 @@ local directive_handlers = {
     local start_row, start_col, end_row, end_col = node:range()
 
     local node_text = vim.split(vim.treesitter.get_node_text(node, bufnr), '\n')
+    if end_col == 0 then
+      -- get_node_text() will ignore the last line if the node ends at column 0
+      node_text[#node_text + 1] = ''
+    end
+
     local end_idx = #node_text
     local start_idx = 1
 
@@ -600,6 +605,9 @@ local directive_handlers = {
       while end_idx > 0 and node_text[end_idx]:find('^%s*$') do
         end_idx = end_idx - 1
         end_row = end_row - 1
+        -- set the end position to the last column of the next line, or 0 if we just trimmed the
+        -- last line
+        end_col = end_idx > 0 and #node_text[end_idx] or 0
       end
     end
     if trim_end_cols then
@@ -616,6 +624,7 @@ local directive_handlers = {
       while start_idx <= end_idx and node_text[start_idx]:find('^%s*$') do
         start_idx = start_idx + 1
         start_row = start_row + 1
+        start_col = 0
       end
     end
     if trim_start_cols and node_text[start_idx] then
-- 
cgit 


From b52ffd0a59df3b79f2f3f2338485f235da94478d Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sat, 7 Dec 2024 10:57:09 +0100
Subject: fix(inspect): always show priority

Problem: It is not obvious if a treesitter highlight priority shown in
`:Inspect` is higher or lower than the default.

Solution: Also print default priority (`vim.hl.priorities.treesitter`).
Add padding for better readability.
---
 runtime/lua/vim/_inspector.lua | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index 520c437906..a09dd55a47 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -178,7 +178,7 @@ function vim.show_pos(bufnr, row, col, filter)
     if data.hl_group ~= data.hl_group_link then
       append('links to ', 'MoreMsg')
       append(data.hl_group_link, data.hl_group_link)
-      append(' ')
+      append('   ')
     end
     if comment then
       append(comment, 'Comment')
@@ -193,9 +193,11 @@ function vim.show_pos(bufnr, row, col, filter)
     for _, capture in ipairs(items.treesitter) do
       item(
         capture,
-        capture.metadata.priority
-            and string.format('%s priority: %d', capture.lang, capture.metadata.priority)
-          or capture.lang
+        string.format(
+          'priority: %d   language: %s',
+          capture.metadata.priority or vim.hl.priorities.treesitter,
+          capture.lang
+        )
       )
     end
     nl()
-- 
cgit 


From 668d2569b4109e7e83c45578c506c1c64dbd5e87 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Sat, 7 Dec 2024 13:05:05 +0000
Subject: refactor: add vim._resolve_bufnr

---
 runtime/lua/vim/_inspector.lua          |  2 +-
 runtime/lua/vim/diagnostic.lua          | 33 +++++++++++++--------------------
 runtime/lua/vim/lsp.lua                 | 28 ++++++++--------------------
 runtime/lua/vim/lsp/buf.lua             |  4 ++--
 runtime/lua/vim/lsp/client.lua          | 18 +++---------------
 runtime/lua/vim/lsp/codelens.lua        |  8 ++------
 runtime/lua/vim/lsp/completion.lua      |  2 +-
 runtime/lua/vim/lsp/diagnostic.lua      |  4 +---
 runtime/lua/vim/lsp/inlay_hint.lua      | 21 ++++++---------------
 runtime/lua/vim/lsp/semantic_tokens.lua | 19 +++++--------------
 runtime/lua/vim/lsp/util.lua            | 11 +++--------
 runtime/lua/vim/shared.lua              | 10 ++++++++++
 runtime/lua/vim/treesitter.lua          | 22 ++++++----------------
 13 files changed, 61 insertions(+), 121 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index a09dd55a47..15b0733671 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -53,7 +53,7 @@ function vim.inspect_pos(bufnr, row, col, filter)
     local cursor = vim.api.nvim_win_get_cursor(win)
     row, col = cursor[1] - 1, cursor[2]
   end
-  bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr
+  bufnr = vim._resolve_bufnr(bufnr)
 
   local results = {
     treesitter = {}, --- @type table[]
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index b8ffb26377..340bca4f6b 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -573,13 +573,6 @@ local underline_highlight_map = make_highlight_map('Underline')
 local floating_highlight_map = make_highlight_map('Floating')
 local sign_highlight_map = make_highlight_map('Sign')
 
-local function get_bufnr(bufnr)
-  if not bufnr or bufnr == 0 then
-    return api.nvim_get_current_buf()
-  end
-  return bufnr
-end
-
 --- @param diagnostics vim.Diagnostic[]
 --- @return table
 local function diagnostic_lines(diagnostics)
@@ -642,7 +635,7 @@ end
 --- @param namespace integer
 --- @param bufnr? integer
 local function save_extmarks(namespace, bufnr)
-  bufnr = get_bufnr(bufnr)
+  bufnr = vim._resolve_bufnr(bufnr)
   if not diagnostic_attached_buffers[bufnr] then
     api.nvim_buf_attach(bufnr, false, {
       on_lines = function(_, _, _, _, _, last)
@@ -814,7 +807,7 @@ local function get_diagnostics(bufnr, opts, clamp)
       end
     end
   elseif namespace == nil then
-    bufnr = get_bufnr(bufnr)
+    bufnr = vim._resolve_bufnr(bufnr)
     for iter_namespace in pairs(diagnostic_cache[bufnr]) do
       add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace])
     end
@@ -825,7 +818,7 @@ local function get_diagnostics(bufnr, opts, clamp)
       end
     end
   else
-    bufnr = get_bufnr(bufnr)
+    bufnr = vim._resolve_bufnr(bufnr)
     for _, iter_namespace in ipairs(namespace) do
       add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace] or {})
     end
@@ -1100,7 +1093,7 @@ function M.set(namespace, bufnr, diagnostics, opts)
   vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
   vim.validate('opts', opts, 'table', true)
 
-  bufnr = get_bufnr(bufnr)
+  bufnr = vim._resolve_bufnr(bufnr)
 
   if vim.tbl_isempty(diagnostics) then
     diagnostic_cache[bufnr][namespace] = nil
@@ -1380,7 +1373,7 @@ M.handlers.signs = {
     vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
     vim.validate('opts', opts, 'table', true)
 
-    bufnr = get_bufnr(bufnr)
+    bufnr = vim._resolve_bufnr(bufnr)
     opts = opts or {}
 
     if not api.nvim_buf_is_loaded(bufnr) then
@@ -1486,7 +1479,7 @@ M.handlers.underline = {
     vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
     vim.validate('opts', opts, 'table', true)
 
-    bufnr = get_bufnr(bufnr)
+    bufnr = vim._resolve_bufnr(bufnr)
     opts = opts or {}
 
     if not vim.api.nvim_buf_is_loaded(bufnr) then
@@ -1550,7 +1543,7 @@ M.handlers.virtual_text = {
     vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
     vim.validate('opts', opts, 'table', true)
 
-    bufnr = get_bufnr(bufnr)
+    bufnr = vim._resolve_bufnr(bufnr)
     opts = opts or {}
 
     if not vim.api.nvim_buf_is_loaded(bufnr) then
@@ -1675,7 +1668,7 @@ function M.hide(namespace, bufnr)
   vim.validate('namespace', namespace, 'number', true)
   vim.validate('bufnr', bufnr, 'number', true)
 
-  local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
+  local buffers = bufnr and { vim._resolve_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
   for _, iter_bufnr in ipairs(buffers) do
     local namespaces = namespace and { namespace } or vim.tbl_keys(diagnostic_cache[iter_bufnr])
     for _, iter_namespace in ipairs(namespaces) do
@@ -1702,7 +1695,7 @@ function M.is_enabled(filter)
     return vim.tbl_isempty(diagnostic_disabled) and not diagnostic_disabled[1]
   end
 
-  local bufnr = get_bufnr(filter.bufnr)
+  local bufnr = vim._resolve_bufnr(filter.bufnr)
   if type(diagnostic_disabled[bufnr]) == 'table' then
     return not diagnostic_disabled[bufnr][filter.ns_id]
   end
@@ -1743,7 +1736,7 @@ function M.show(namespace, bufnr, diagnostics, opts)
       end
     else
       -- namespace is nil
-      bufnr = get_bufnr(bufnr)
+      bufnr = vim._resolve_bufnr(bufnr)
       for iter_namespace in pairs(diagnostic_cache[bufnr]) do
         M.show(iter_namespace, bufnr, nil, opts)
       end
@@ -1810,7 +1803,7 @@ function M.open_float(opts, ...)
   end
 
   opts = opts or {}
-  bufnr = get_bufnr(bufnr or opts.bufnr)
+  bufnr = vim._resolve_bufnr(bufnr or opts.bufnr)
 
   do
     -- Resolve options with user settings from vim.diagnostic.config
@@ -2013,7 +2006,7 @@ function M.reset(namespace, bufnr)
   vim.validate('namespace', namespace, 'number', true)
   vim.validate('bufnr', bufnr, 'number', true)
 
-  local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
+  local buffers = bufnr and { vim._resolve_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
   for _, iter_bufnr in ipairs(buffers) do
     local namespaces = namespace and { namespace } or vim.tbl_keys(diagnostic_cache[iter_bufnr])
     for _, iter_namespace in ipairs(namespaces) do
@@ -2151,7 +2144,7 @@ function M.enable(enable, filter)
       ns.disabled = not enable
     end
   else
-    bufnr = get_bufnr(bufnr)
+    bufnr = vim._resolve_bufnr(bufnr)
     if not ns_id then
       diagnostic_disabled[bufnr] = (not enable) and true or nil
     else
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index e1946816da..ebdc050405 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -89,18 +89,6 @@ lsp._request_name_to_capability = {
 
 -- TODO improve handling of scratch buffers with LSP attached.
 
---- Returns the buffer number for the given {bufnr}.
----
----@param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer
----@return integer bufnr
-local function resolve_bufnr(bufnr)
-  validate('bufnr', bufnr, 'number', true)
-  if bufnr == nil or bufnr == 0 then
-    return api.nvim_get_current_buf()
-  end
-  return bufnr
-end
-
 ---@private
 --- Called by the client when trying to call a method that's not
 --- supported in any of the servers registered for the current buffer.
@@ -389,7 +377,7 @@ end
 function lsp.start(config, opts)
   opts = opts or {}
   local reuse_client = opts.reuse_client or reuse_client_default
-  local bufnr = resolve_bufnr(opts.bufnr)
+  local bufnr = vim._resolve_bufnr(opts.bufnr)
 
   for _, client in pairs(all_clients) do
     if reuse_client(client, config) then
@@ -528,7 +516,7 @@ end
 ---Buffer lifecycle handler for textDocument/didSave
 --- @param bufnr integer
 local function text_document_did_save_handler(bufnr)
-  bufnr = resolve_bufnr(bufnr)
+  bufnr = vim._resolve_bufnr(bufnr)
   local uri = vim.uri_from_bufnr(bufnr)
   local text = once(lsp._buf_get_full_text)
   for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do
@@ -689,7 +677,7 @@ end
 function lsp.buf_attach_client(bufnr, client_id)
   validate('bufnr', bufnr, 'number', true)
   validate('client_id', client_id, 'number')
-  bufnr = resolve_bufnr(bufnr)
+  bufnr = vim._resolve_bufnr(bufnr)
   if not api.nvim_buf_is_loaded(bufnr) then
     log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr))
     return false
@@ -726,7 +714,7 @@ end
 function lsp.buf_detach_client(bufnr, client_id)
   validate('bufnr', bufnr, 'number', true)
   validate('client_id', client_id, 'number')
-  bufnr = resolve_bufnr(bufnr)
+  bufnr = vim._resolve_bufnr(bufnr)
 
   local client = all_clients[client_id]
   if not client or not client.attached_buffers[bufnr] then
@@ -832,7 +820,7 @@ function lsp.get_clients(filter)
 
   local clients = {} --- @type vim.lsp.Client[]
 
-  local bufnr = filter.bufnr and resolve_bufnr(filter.bufnr)
+  local bufnr = filter.bufnr and vim._resolve_bufnr(filter.bufnr)
 
   for _, client in pairs(all_clients) do
     if
@@ -928,7 +916,7 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
   validate('handler', handler, 'function', true)
   validate('on_unsupported', on_unsupported, 'function', true)
 
-  bufnr = resolve_bufnr(bufnr)
+  bufnr = vim._resolve_bufnr(bufnr)
   local method_supported = false
   local clients = lsp.get_clients({ bufnr = bufnr })
   local client_request_ids = {} --- @type table
@@ -1208,7 +1196,7 @@ end
 function lsp.buf_get_clients(bufnr)
   vim.deprecate('vim.lsp.buf_get_clients()', 'vim.lsp.get_clients()', '0.12')
   local result = {} --- @type table
-  for _, client in ipairs(lsp.get_clients({ bufnr = resolve_bufnr(bufnr) })) do
+  for _, client in ipairs(lsp.get_clients({ bufnr = vim._resolve_bufnr(bufnr) })) do
     result[client.id] = client
   end
   return result
@@ -1262,7 +1250,7 @@ function lsp.for_each_buffer_client(bufnr, fn)
     'lsp.get_clients({ bufnr = bufnr }) with regular loop',
     '0.12'
   )
-  bufnr = resolve_bufnr(bufnr)
+  bufnr = vim._resolve_bufnr(bufnr)
 
   for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do
     fn(client, client.id, bufnr)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 10479807a2..1926a0228d 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -519,7 +519,7 @@ end
 --- @param opts? vim.lsp.buf.format.Opts
 function M.format(opts)
   opts = opts or {}
-  local bufnr = opts.bufnr or api.nvim_get_current_buf()
+  local bufnr = vim._resolve_bufnr(opts.bufnr)
   local mode = api.nvim_get_mode().mode
   local range = opts.range
   -- Try to use visual selection if no range is given
@@ -617,7 +617,7 @@ end
 ---@param opts? vim.lsp.buf.rename.Opts Additional options:
 function M.rename(new_name, opts)
   opts = opts or {}
-  local bufnr = opts.bufnr or api.nvim_get_current_buf()
+  local bufnr = vim._resolve_bufnr(opts.bufnr)
   local clients = lsp.get_clients({
     bufnr = bufnr,
     name = opts.name,
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 7eb023da39..e03fb2415d 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -604,18 +604,6 @@ 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? Buffer number to resolve. Defaults to current buffer
---- @return integer bufnr
-local function resolve_bufnr(bufnr)
-  validate('bufnr', bufnr, 'number', true)
-  if bufnr == nil or bufnr == 0 then
-    return api.nvim_get_current_buf()
-  end
-  return bufnr
-end
-
 --- Sends a request to the server.
 ---
 --- This is a thin wrapper around {client.rpc.request} with some additional
@@ -640,7 +628,7 @@ 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)
-  bufnr = resolve_bufnr(bufnr)
+  bufnr = vim._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)
@@ -891,7 +879,7 @@ end
 --- @param bufnr? integer
 --- @return lsp.Registration?
 function Client:_get_registration(method, bufnr)
-  bufnr = bufnr or vim.api.nvim_get_current_buf()
+  bufnr = vim._resolve_bufnr(bufnr)
   for _, reg in ipairs(self.registrations[method] or {}) do
     if not reg.registerOptions or not reg.registerOptions.documentSelector then
       return reg
@@ -928,7 +916,7 @@ end
 --- @param handler? lsp.Handler only called if a server command
 function Client:exec_cmd(command, context, handler)
   context = vim.deepcopy(context or {}, true) --[[@as lsp.HandlerContext]]
-  context.bufnr = context.bufnr or api.nvim_get_current_buf()
+  context.bufnr = vim._resolve_bufnr(context.bufnr)
   context.client_id = self.id
   local cmdname = command.command
   local fn = self.commands[cmdname] or lsp.commands[cmdname]
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index a11f84d6c6..3ccd165d0b 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -104,16 +104,12 @@ function M.run()
   end
 end
 
-local function resolve_bufnr(bufnr)
-  return bufnr == 0 and api.nvim_get_current_buf() or bufnr
-end
-
 --- Clear the lenses
 ---
 ---@param client_id integer|nil filter by client_id. All clients if nil
 ---@param bufnr integer|nil filter by buffer. All buffers if nil, 0 for current buffer
 function M.clear(client_id, bufnr)
-  bufnr = bufnr and resolve_bufnr(bufnr)
+  bufnr = bufnr and vim._resolve_bufnr(bufnr)
   local buffers = bufnr and { bufnr }
     or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs())
   for _, iter_bufnr in pairs(buffers) do
@@ -296,7 +292,7 @@ end
 --- @param opts? vim.lsp.codelens.refresh.Opts Optional fields
 function M.refresh(opts)
   opts = opts or {}
-  local bufnr = opts.bufnr and resolve_bufnr(opts.bufnr)
+  local bufnr = opts.bufnr and vim._resolve_bufnr(opts.bufnr)
   local buffers = bufnr and { bufnr }
     or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs())
 
diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua
index 1e950f625e..dbf0a62eeb 100644
--- a/runtime/lua/vim/lsp/completion.lua
+++ b/runtime/lua/vim/lsp/completion.lua
@@ -716,7 +716,7 @@ end
 --- @param bufnr integer Buffer handle, or 0 for the current buffer
 --- @param opts? vim.lsp.completion.BufferOpts
 function M.enable(enable, client_id, bufnr, opts)
-  bufnr = (bufnr == 0 and api.nvim_get_current_buf()) or bufnr
+  bufnr = vim._resolve_bufnr(bufnr)
 
   if enable then
     enable_completions(client_id, bufnr, opts or {})
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index 3f135d84f3..9a879d9f38 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -356,9 +356,7 @@ end
 ---@param bufnr (integer) Buffer handle, or 0 for current
 ---@private
 function M._enable(bufnr)
-  if bufnr == nil or bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
 
   if not bufstates[bufnr] then
     bufstates[bufnr] = { enabled = true }
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua
index f1a58de621..50cf9f5f29 100644
--- a/runtime/lua/vim/lsp/inlay_hint.lua
+++ b/runtime/lua/vim/lsp/inlay_hint.lua
@@ -149,8 +149,8 @@ function M.get(filter)
       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()
+  else
+    bufnr = vim._resolve_bufnr(bufnr)
   end
 
   local bufstate = bufstates[bufnr]
@@ -203,9 +203,7 @@ end
 --- Clear inlay hints
 ---@param bufnr (integer) Buffer handle, or 0 for current
 local function clear(bufnr)
-  if bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
   local bufstate = bufstates[bufnr]
   local client_lens = (bufstate or {}).client_hints or {}
   local client_ids = vim.tbl_keys(client_lens) --- @type integer[]
@@ -221,9 +219,7 @@ end
 --- Disable inlay hints for a buffer
 ---@param bufnr (integer) Buffer handle, or 0 for current
 local function _disable(bufnr)
-  if bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
   clear(bufnr)
   bufstates[bufnr] = nil
   bufstates[bufnr].enabled = false
@@ -242,9 +238,7 @@ end
 --- Enable inlay hints for a buffer
 ---@param bufnr (integer) Buffer handle, or 0 for current
 local function _enable(bufnr)
-  if bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
   bufstates[bufnr] = nil
   bufstates[bufnr].enabled = true
   _refresh(bufnr)
@@ -371,13 +365,10 @@ function M.is_enabled(filter)
   filter = filter or {}
   local bufnr = filter.bufnr
 
-  vim.validate('bufnr', bufnr, 'number', true)
   if bufnr == nil then
     return globalstate.enabled
-  elseif bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
   end
-  return bufstates[bufnr].enabled
+  return bufstates[vim._resolve_bufnr(bufnr)].enabled
 end
 
 --- Optional filters |kwargs|, or `nil` for all.
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
index 01421fea29..7cc3f95aed 100644
--- a/runtime/lua/vim/lsp/semantic_tokens.lua
+++ b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -600,9 +600,7 @@ function M.start(bufnr, client_id, opts)
   vim.validate('bufnr', bufnr, 'number')
   vim.validate('client_id', client_id, 'number')
 
-  if bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
 
   opts = opts or {}
   assert(
@@ -655,9 +653,7 @@ function M.stop(bufnr, client_id)
   vim.validate('bufnr', bufnr, 'number')
   vim.validate('client_id', client_id, 'number')
 
-  if bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
 
   local highlighter = STHighlighter.active[bufnr]
   if not highlighter then
@@ -691,9 +687,7 @@ end
 ---        - modifiers (table) token modifiers as a set. E.g., { static = true, readonly = true }
 ---        - client_id (integer)
 function M.get_at_pos(bufnr, row, col)
-  if bufnr == nil or bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
 
   local highlighter = STHighlighter.active[bufnr]
   if not highlighter then
@@ -739,8 +733,7 @@ function M.force_refresh(bufnr)
   vim.validate('bufnr', bufnr, 'number', true)
 
   local buffers = bufnr == nil and vim.tbl_keys(STHighlighter.active)
-    or bufnr == 0 and { api.nvim_get_current_buf() }
-    or { bufnr }
+    or { vim._resolve_bufnr(bufnr) }
 
   for _, buffer in ipairs(buffers) do
     local highlighter = STHighlighter.active[buffer]
@@ -770,9 +763,7 @@ end
 ---@param hl_group (string) Highlight group name
 ---@param opts? vim.lsp.semantic_tokens.highlight_token.Opts  Optional parameters:
 function M.highlight_token(token, bufnr, client_id, hl_group, opts)
-  if bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
   local highlighter = STHighlighter.active[bufnr]
   if not highlighter then
     return
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 5bf436f2ca..6bee5bc31f 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -192,9 +192,7 @@ local function get_lines(bufnr, rows)
   rows = type(rows) == 'table' and rows or { rows }
 
   -- This is needed for bufload and bufloaded
-  if bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
 
   local function buf_lines()
     local lines = {} --- @type table
@@ -1976,7 +1974,7 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, position_encoding)
   validate('start_pos', start_pos, 'table', true)
   validate('end_pos', end_pos, 'table', true)
   validate('position_encoding', position_encoding, 'string', true)
-  bufnr = bufnr or api.nvim_get_current_buf()
+  bufnr = vim._resolve_bufnr(bufnr)
   if position_encoding == nil then
     vim.notify_once(
       'position_encoding param is required in vim.lsp.util.make_given_range_params. Defaulting to position encoding of the first client.',
@@ -2143,10 +2141,7 @@ end
 ---@param opts? vim.lsp.util._refresh.Opts Options table
 function M._refresh(method, opts)
   opts = opts or {}
-  local bufnr = opts.bufnr
-  if bufnr == nil or bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  local bufnr = vim._resolve_bufnr(opts.bufnr)
 
   local clients = vim.lsp.get_clients({ bufnr = bufnr, method = method, id = opts.client_id })
 
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 2e8edea22a..9314d34314 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -1399,4 +1399,14 @@ function vim._with(context, f)
   return vim._with_c(context, callback)
 end
 
+--- @param bufnr? integer
+--- @return integer
+function vim._resolve_bufnr(bufnr)
+  if bufnr == nil or bufnr == 0 then
+    return vim.api.nvim_get_current_buf()
+  end
+  vim.validate('bufnr', bufnr, 'number')
+  return bufnr
+end
+
 return vim
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index dca89f413c..5f4e796413 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -32,9 +32,7 @@ M.minimum_language_version = vim._ts_get_minimum_language_version()
 ---
 ---@return vim.treesitter.LanguageTree object to use for parsing
 function M._create_parser(bufnr, lang, opts)
-  if bufnr == 0 then
-    bufnr = vim.api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
 
   vim.fn.bufload(bufnr)
 
@@ -90,9 +88,7 @@ function M.get_parser(bufnr, lang, opts)
   opts = opts or {}
   local should_error = opts.error == nil or opts.error
 
-  if bufnr == nil or bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
 
   if not valid_lang(lang) then
     lang = M.language.get_lang(vim.bo[bufnr].filetype)
@@ -258,9 +254,7 @@ end
 ---
 ---@return {capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata}[]
 function M.get_captures_at_pos(bufnr, row, col)
-  if bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  bufnr = vim._resolve_bufnr(bufnr)
   local buf_highlighter = M.highlighter.active[bufnr]
 
   if not buf_highlighter then
@@ -361,11 +355,7 @@ end
 function M.get_node(opts)
   opts = opts or {}
 
-  local bufnr = opts.bufnr
-
-  if not bufnr or bufnr == 0 then
-    bufnr = api.nvim_get_current_buf()
-  end
+  local bufnr = vim._resolve_bufnr(opts.bufnr)
 
   local row, col --- @type integer, integer
   if opts.pos then
@@ -417,7 +407,7 @@ end
 ---@param bufnr (integer|nil) Buffer to be highlighted (default: current buffer)
 ---@param lang (string|nil) Language of the parser (default: from buffer filetype)
 function M.start(bufnr, lang)
-  bufnr = bufnr or api.nvim_get_current_buf()
+  bufnr = vim._resolve_bufnr(bufnr)
   local parser = assert(M.get_parser(bufnr, lang, { error = false }))
   M.highlighter.new(parser)
 end
@@ -426,7 +416,7 @@ end
 ---
 ---@param bufnr (integer|nil) Buffer to stop highlighting (default: current buffer)
 function M.stop(bufnr)
-  bufnr = (bufnr and bufnr ~= 0) and bufnr or api.nvim_get_current_buf()
+  bufnr = vim._resolve_bufnr(bufnr)
 
   if M.highlighter.active[bufnr] then
     M.highlighter.active[bufnr]:destroy()
-- 
cgit 


From f3fa6507f2473d66a4c6172c82dec43bf55f8df6 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 8 Dec 2024 07:00:17 +0800
Subject: vim-patch:9.1.0910: 'messagesopt' does not check max wait time

Problem:  'messagesopt' does not check max wait time
          (after v9.1.0908)
Solution: Check for max wait value
          (Shougo Matsushita)

closes: vim/vim#16183

https://github.com/vim/vim/commit/d9e9f89e0ffd6e7ce5e2a7f8f1ace5471e37c210

Co-authored-by: Shougo Matsushita 
---
 runtime/lua/vim/_meta/options.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 59d270e640..e38fa4a89f 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -4095,6 +4095,7 @@ vim.go.mis = vim.go.menuitems
 --- 		milliseconds so the user has a chance to read the
 --- 		message, use 0 to disable sleep (but then the user may
 --- 		miss an important message).
+--- 		The maximum value is 10000.
 ---
 --- history:{n}	Determines how many entries are remembered in the
 --- 		`:messages` history.  The maximum value is 10000.
-- 
cgit 


From 96329910b011414758380e3c27e32c0ae7f43e1e Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 8 Dec 2024 07:01:59 +0800
Subject: vim-patch:9.1.0911: Variable name for 'messagesopt' doesn't match
 short name

Problem:  Variable name for 'messagesopt' doesn't match short name
          (after v9.1.0908)
Solution: Change p_meo to p_mopt.  Add more details to docs.

closes: vim/vim#16182

https://github.com/vim/vim/commit/8cc43daee1f485c9abf1de3c638cce7835b9f861
---
 runtime/lua/vim/_meta/options.lua | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index e38fa4a89f..7a8c3a6c29 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -4084,22 +4084,24 @@ vim.o.mis = vim.o.menuitems
 vim.go.menuitems = vim.o.menuitems
 vim.go.mis = vim.go.menuitems
 
---- Option settings when outputting messages.  It can consist of the
+--- Option settings for outputting messages.  It can consist of the
 --- following items.  Items must be separated by a comma.
 ---
---- hit-enter	Use `hit-enter` prompt when the message is longer than
+--- hit-enter	Use a `hit-enter` prompt when the message is longer than
 --- 		'cmdheight' size.
 ---
---- wait:{n}	Ignored when "hit-enter" is present.  Instead of using
---- 		`hit-enter` prompt, will simply wait for {n}
---- 		milliseconds so the user has a chance to read the
---- 		message, use 0 to disable sleep (but then the user may
---- 		miss an important message).
---- 		The maximum value is 10000.
+--- wait:{n}	Instead of using a `hit-enter` prompt, simply wait for
+--- 		{n} milliseconds so that the user has a chance to read
+--- 		the message.  The maximum value of {n} is 10000.  Use
+--- 		0 to disable the wait (but then the user may miss an
+--- 		important message).
+--- 		This item is ignored when "hit-enter" is present, but
+--- 		required when "hit-enter" is not present.
 ---
 --- history:{n}	Determines how many entries are remembered in the
 --- 		`:messages` history.  The maximum value is 10000.
 --- 		Setting it to zero clears the message history.
+--- 		This item must always be present.
 ---
 --- @type string
 vim.o.messagesopt = "hit-enter,history:500"
-- 
cgit 


From 4bfdd1ee9dee15c4b066412335f8cafbc3208f60 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Sat, 7 Dec 2024 10:39:56 +0000
Subject: refactor(lsp): better tracking of requests

Not essential, but adds robustness and hardening for future
changes.
---
 runtime/lua/vim/lsp/client.lua | 101 +++++++++++++++++++++++++----------------
 1 file changed, 62 insertions(+), 39 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index e03fb2415d..5eefe4600e 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -75,7 +75,7 @@ local validate = vim.validate
 ---
 --- Map with language server specific settings.
 --- See the {settings} in |vim.lsp.Client|.
---- @field settings? table
+--- @field settings? lsp.LSPObject
 ---
 --- Table that maps string of clientside commands to user-defined functions.
 --- Commands passed to `start()` take precedence over the global command registry. Each key
@@ -85,7 +85,7 @@ local validate = vim.validate
 ---
 --- Values to pass in the initialization request as `initializationOptions`. See `initialize` in
 --- the LSP spec.
---- @field init_options? table
+--- @field init_options? lsp.LSPObject
 ---
 --- Name in log messages.
 --- (default: client-id)
@@ -164,7 +164,7 @@ local validate = vim.validate
 --- for an active request, or "cancel" for a cancel request. It will be
 --- "complete" ephemerally while executing |LspRequest| autocmds when replies
 --- are received from the server.
---- @field requests table
+--- @field requests table
 ---
 --- copy of the table that was passed by the user
 --- to |vim.lsp.start()|.
@@ -189,9 +189,6 @@ local validate = vim.validate
 ---
 --- @field attached_buffers table
 ---
---- Buffers that should be attached to upon initialize()
---- @field package _buffers_to_attach table
----
 --- @field private _log_prefix string
 ---
 --- Track this so that we can escalate automatically if we've already tried a
@@ -210,7 +207,7 @@ local validate = vim.validate
 --- Map with language server specific settings. These are returned to the
 --- language server if requested via `workspace/configuration`. Keys are
 --- case-sensitive.
---- @field settings table
+--- @field settings lsp.LSPObject
 ---
 --- A table with flags for the client. The current (experimental) flags are:
 --- @field flags vim.lsp.Client.Flags
@@ -265,9 +262,6 @@ local valid_encodings = {
   ['utf8'] = 'utf-8',
   ['utf16'] = 'utf-16',
   ['utf32'] = 'utf-32',
-  UTF8 = 'utf-8',
-  UTF16 = 'utf-16',
-  UTF32 = 'utf-32',
 }
 
 --- Normalizes {encoding} to valid LSP encoding names.
@@ -276,7 +270,7 @@ local valid_encodings = {
 local function validate_encoding(encoding)
   validate('encoding', encoding, 'string', true)
   if not encoding then
-    return valid_encodings.UTF16
+    return valid_encodings.utf16
   end
   return valid_encodings[encoding:lower()]
     or error(
@@ -604,6 +598,57 @@ function Client:_resolve_handler(method)
   return self.handlers[method] or lsp.handlers[method]
 end
 
+--- @private
+--- @param id integer
+--- @param req_type 'pending'|'complete'|'cancel'|
+--- @param bufnr? integer (only required for req_type='pending')
+--- @param method? string (only required for req_type='pending')
+function Client:_process_request(id, req_type, bufnr, method)
+  local pending = req_type == 'pending'
+
+  validate('id', id, 'number')
+  if pending then
+    validate('bufnr', bufnr, 'number')
+    validate('method', method, 'string')
+  end
+
+  local cur_request = self.requests[id]
+
+  if pending and cur_request then
+    log.error(
+      self._log_prefix,
+      ('Cannot create request with id %d as one already exists'):format(id)
+    )
+    return
+  elseif not pending and not cur_request then
+    log.error(
+      self._log_prefix,
+      ('Cannot find request with id %d whilst attempting to %s'):format(id, req_type)
+    )
+    return
+  end
+
+  if cur_request then
+    bufnr = cur_request.bufnr
+    method = cur_request.method
+  end
+
+  assert(bufnr and method)
+
+  local request = { type = req_type, bufnr = bufnr, method = method }
+
+  -- Clear 'complete' requests
+  -- Note 'pending' and 'cancelled' requests are cleared when the server sends a response
+  -- which is processed via the notify_reply_callback argument to rpc.request.
+  self.requests[id] = req_type ~= 'complete' and request or nil
+
+  api.nvim_exec_autocmds('LspRequest', {
+    buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
+    modeline = false,
+    data = { client_id = self.id, request_id = id, request = request },
+  })
+end
+
 --- Sends a request to the server.
 ---
 --- This is a thin wrapper around {client.rpc.request} with some additional
@@ -632,33 +677,20 @@ function Client:request(method, params, handler, 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 = {
+    handler(err, result, {
       method = method,
       client_id = self.id,
       bufnr = bufnr,
       params = params,
       version = version,
-    }
-    handler(err, result, context)
-  end, function(request_id)
-    local request = self.requests[request_id]
-    request.type = 'complete'
-    api.nvim_exec_autocmds('LspRequest', {
-      buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
-      modeline = false,
-      data = { client_id = self.id, request_id = request_id, request = request },
     })
-    self.requests[request_id] = nil
+  end, function(request_id)
+    -- Called when the server sends a response to the request (including cancelled acknowledgment).
+    self:_process_request(request_id, 'complete')
   end)
 
   if success and request_id then
-    local request = { type = 'pending', bufnr = bufnr, method = method }
-    self.requests[request_id] = request
-    api.nvim_exec_autocmds('LspRequest', {
-      buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
-      modeline = false,
-      data = { client_id = self.id, request_id = request_id, request = request },
-    })
+    self:_process_request(request_id, 'pending', bufnr, method)
   end
 
   return success, request_id
@@ -756,16 +788,7 @@ end
 --- @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
-    request.type = 'cancel'
-    api.nvim_exec_autocmds('LspRequest', {
-      buffer = api.nvim_buf_is_valid(request.bufnr) and request.bufnr or nil,
-      modeline = false,
-      data = { client_id = self.id, request_id = id, request = request },
-    })
-  end
+  self:_process_request(id, 'cancel')
   return self.rpc.notify(ms.dollar_cancelRequest, { id = id })
 end
 
-- 
cgit 


From 3bb2d027597107a3d7f84ef61507104fd4dc050a Mon Sep 17 00:00:00 2001
From: Maria José Solano 
Date: Sun, 8 Dec 2024 19:18:40 -0800
Subject: docs: fix type of vim.validate value

---
 runtime/lua/vim/shared.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 9314d34314..0fe8e99350 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -1013,7 +1013,7 @@ do
   --- best performance.
   ---
   --- @param name string Argument name
-  --- @param value string Argument value
+  --- @param value any Argument value
   --- @param validator vim.validate.Validator
   ---   - (`string|string[]`): Any value that can be returned from |lua-type()| in addition to
   ---     `'callable'`: `'boolean'`, `'callable'`, `'function'`, `'nil'`, `'number'`, `'string'`, `'table'`,
-- 
cgit 


From 4889935a7a0e188d6fb247cbc3b3bb0c5b0ed731 Mon Sep 17 00:00:00 2001
From: luukvbaal 
Date: Tue, 10 Dec 2024 01:27:39 +0100
Subject: docs(vvars): adjust lua types for vim.v variables #31510

- classes for v:event and v:completed_item
- add remaining unknown types
---
 runtime/lua/vim/_meta/vvars.lua       | 20 ++++-----
 runtime/lua/vim/_meta/vvars_extra.lua | 77 +++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+), 10 deletions(-)
 create mode 100644 runtime/lua/vim/_meta/vvars_extra.lua

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua
index 264907109f..445da4e02f 100644
--- a/runtime/lua/vim/_meta/vvars.lua
+++ b/runtime/lua/vim/_meta/vvars.lua
@@ -15,7 +15,7 @@ vim.v.argv = ...
 --- Argument for evaluating 'formatexpr' and used for the typed
 --- character when using  in an abbreviation `:map-`.
 --- It is also used by the `InsertCharPre` and `InsertEnter` events.
---- @type any
+--- @type string
 vim.v.char = ...
 
 --- The name of the character encoding of a file to be converted.
@@ -60,7 +60,7 @@ vim.v.collate = ...
 --- mode.
 --- Note: Plugins can modify the value to emulate the builtin
 --- `CompleteDone` event behavior.
---- @type any
+--- @type vim.v.completed_item
 vim.v.completed_item = ...
 
 --- The count given for the last Normal mode command.  Can be used
@@ -90,7 +90,7 @@ vim.v.count1 = ...
 --- This variable can not be set directly, use the `:language`
 --- command.
 --- See `multi-lang`.
---- @type any
+--- @type string
 vim.v.ctype = ...
 
 --- Normally zero.  When a deadly signal is caught it's set to
@@ -199,7 +199,7 @@ vim.v.errors = ...
 ---   reason           Reason for completion being done. `CompleteDone`
 ---   complete_word    The word that was selected, empty if abandoned complete.
 ---   complete_type    See `complete_info_mode`
---- @type any
+--- @type vim.v.event
 vim.v.event = ...
 
 --- The value of the exception most recently caught and not
@@ -225,7 +225,7 @@ vim.v.exception = ...
 --- ```vim
 ---   :au VimLeave * echo "Exit value is " .. v:exiting
 --- ```
---- @type any
+--- @type integer?
 vim.v.exiting = ...
 
 --- Special value used to put "false" in JSON and msgpack.  See
@@ -421,7 +421,7 @@ vim.v.mouse_winid = ...
 --- and `msgpackdump()`. All types inside dictionary are fixed
 --- (not editable) empty lists. To check whether some list is one
 --- of msgpack types, use `is` operator.
---- @type any
+--- @type table
 vim.v.msgpack_types = ...
 
 --- Special value used to put "null" in JSON and NIL in msgpack.
@@ -565,7 +565,7 @@ vim.v.relnum = ...
 --- typed command.
 --- This can be used to find out why your script causes the
 --- hit-enter prompt.
---- @type any
+--- @type string
 vim.v.scrollstart = ...
 
 --- Search direction:  1 after a forward search, 0 after a
@@ -707,13 +707,13 @@ vim.v.termrequest = ...
 vim.v.termresponse = ...
 
 --- Must be set before using `test_garbagecollect_now()`.
---- @type any
+--- @type integer
 vim.v.testing = ...
 
 --- Full filename of the last loaded or saved session file.
 --- Empty when no session file has been saved.  See `:mksession`.
 --- Modifiable (can be set).
---- @type any
+--- @type string
 vim.v.this_session = ...
 
 --- The point where the exception most recently caught and not
@@ -730,7 +730,7 @@ vim.v.this_session = ...
 --- ```
 ---
 --- Output: "Exception from test.vim, line 2"
---- @type any
+--- @type string
 vim.v.throwpoint = ...
 
 --- Special value used to put "true" in JSON and msgpack.  See
diff --git a/runtime/lua/vim/_meta/vvars_extra.lua b/runtime/lua/vim/_meta/vvars_extra.lua
new file mode 100644
index 0000000000..7ef3021e89
--- /dev/null
+++ b/runtime/lua/vim/_meta/vvars_extra.lua
@@ -0,0 +1,77 @@
+--- @meta _
+error('Cannot require a meta file')
+
+--- Extra types for vim.v dictionary fields
+
+--- @class vim.v.completed_item
+--- @field word? string the text that will be inserted, mandatory
+--- abbreviation of "word"; when not empty it is used in the menu instead of "word"
+--- @field abbr? string
+--- extra text for the popup menu, displayed after "word" or "abbr"
+--- @field menu? string
+--- more information about the item, can be displayed in a preview window
+--- @field info? string
+--- @field kind? string single letter indicating the type of completion
+--- when non-zero case is to be ignored when comparing items to be equal; when
+--- omitted zero is used, thus items that only differ in case are added
+--- @field icase? integer
+--- when non-zero, always treat this item to be equal when comparing. Which
+--- means, "equal=1" disables filtering of this item.
+--- @field equal? integer
+--- when non-zero this match will be added even when an item with the same word
+--- is already present.
+--- @field dup? integer
+--- when non-zero this match will be added even when it is an empty string
+--- @field empty? integer
+--- custom data which is associated with the item and available
+--- in |v:completed_item|; it can be any type; defaults to an empty string
+--- @field user_data? any
+--- an additional highlight group whose attributes are combined
+--- with |hl-PmenuSel| and |hl-Pmenu| or |hl-PmenuMatchSel| and |hl-PmenuMatch|
+--- highlight attributes in the popup menu to apply cterm and gui properties
+--- (with higher priority) like strikethrough to the completion items abbreviation
+--- @field abbr_hlgroup? string
+--- an additional highlight group specifically for setting the highlight
+--- attributes of the completion kind. When this field is present, it will
+--- override the |hl-PmenuKind| highlight group, allowing for the customization
+--- of ctermfg and guifg properties for the completion kind
+--- @field kind_hlgroup? string
+
+--- @class vim.v.event
+--- Whether the event triggered during an aborting condition (e.g. |c_Esc| or
+--- |c_CTRL-C| for |CmdlineLeave|).
+--- @field abort? boolean
+--- @field chan? integer See |channel-id|
+--- @field info? table Dict of arbitrary event data.
+--- @field cmdlevel? integer Level of cmdline.
+--- @field cmdtype? string Type of cmdline, |cmdline-char|.
+--- @field cwd? string Current working directory.
+--- @field inclusive? boolean Motion is |inclusive|, else exclusive.
+--- @field scope? string Event-specific scope name.
+--- Current |operator|. Also set for Ex commands (unlike |v:operator|). For
+--- example if |TextYankPost| is triggered by the |:yank| Ex command then
+--- `v:event.operator` is "y".
+--- @field operator? string
+--- Text stored in the register as a |readfile()|-style list of lines.
+--- @field regcontents? string
+--- Requested register (e.g "x" for "xyy) or the empty string for an unnamed operation.
+--- @field regname? string
+--- @field regtype? string Type of register as returned by |getregtype()|.
+--- @field visual? boolean Selection is visual (as opposed to, e.g., via motion).
+--- @field completed_item? vim.v.completed_item
+--- Current selected complete item on |CompleteChanged|, Is `{}` when no
+--- complete item selected.
+--- @field height? integer
+--- @field width? integer Height of popup menu on |CompleteChanged|
+--- @field row? integer Width of popup menu on |CompleteChanged|
+--- Col count of popup menu on |CompleteChanged|, relative to screen.
+--- @field col? integer
+--- @field size? integer Total number of completion items on |CompleteChanged|.
+--- Is |v:true| if popup menu have scrollbar, or |v:false| if not.
+--- @field scrollbar? boolean
+--- Is |v:true| if the event fired while changing window  (or tab) on |DirChanged|.
+--- @field changed_window? boolean
+--- @field status? boolean Job status or exit code, -1 means "unknown". |TermClose|
+--- @field reason? string Reason for completion being done. |CompleteDone|
+--- The word that was selected, empty if abandoned complete. @field complete_word? string
+--- @field complete_type? string See |complete_info_mode|
-- 
cgit 


From 3f1d09bc94d02266d6fa588a2ccd1be1ca084cf7 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 1 Nov 2024 16:31:51 +0000
Subject: feat(lsp): add vim.lsp.config and vim.lsp.enable

Design goals/requirements:
- Default configuration of a server can be distributed across multiple sources.
  - And via RTP discovery.
- Default configuration can be specified for all servers.
- Configuration _can_ be project specific.

Solution:

- Two new API's:
  - `vim.lsp.config(name, cfg)`:
    - Used to define default configurations for servers of name.
    - Can be used like a table or called as a function.
    - Use `vim.lsp.confg('*', cfg)` to specify default config for all
      servers.
  - `vim.lsp.enable(name)`
    - Used to enable servers of name. Uses configuration defined
    via `vim.lsp.config()`.
---
 runtime/lua/vim/_meta/options.lua |   1 +
 runtime/lua/vim/lsp.lua           | 250 +++++++++++++++++++++++++++++++++++++-
 runtime/lua/vim/lsp/client.lua    |  21 ++--
 runtime/lua/vim/lsp/health.lua    |  74 ++++++++---
 runtime/lua/vim/shared.lua        |  10 ++
 5 files changed, 318 insertions(+), 38 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 7a8c3a6c29..fecbece655 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -5010,6 +5010,7 @@ vim.go.ruf = vim.go.rulerformat
 ---   indent/	indent scripts `indent-expression`
 ---   keymap/	key mapping files `mbyte-keymap`
 ---   lang/		menu translations `:menutrans`
+---   lsp/		LSP client configurations `lsp-config`
 ---   lua/		`Lua` plugins
 ---   menu.vim	GUI menus `menu.vim`
 ---   pack/		packages `:packadd`
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index ebdc050405..596e1b609b 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -316,6 +316,240 @@ local function create_and_initialize_client(config)
   return client.id, nil
 end
 
+--- @class vim.lsp.Config : vim.lsp.ClientConfig
+---
+--- See `cmd` in [vim.lsp.ClientConfig].
+--- @field cmd? string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
+---
+--- Filetypes the client will attach to, if activated by `vim.lsp.enable()`.
+--- If not provided, then the client will attach to all filetypes.
+--- @field filetypes? string[]
+---
+--- Directory markers (.e.g. '.git/') where the LSP server will base its workspaceFolders,
+--- rootUri, and rootPath on initialization. Unused if `root_dir` is provided.
+--- @field root_markers? string[]
+---
+--- Predicate used to decide if a client should be re-used. Used on all
+--- running clients. The default implementation re-uses a client if name and
+--- root_dir matches.
+--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
+
+--- Update the configuration for an LSP client.
+---
+--- Use name '*' to set default configuration for all clients.
+---
+--- Can also be table-assigned to redefine the configuration for a client.
+---
+--- Examples:
+---
+--- - Add a root marker for all clients:
+---   ```lua
+---      vim.lsp.config('*', {
+---          root_markers = { '.git' },
+---        })
+---        ```
+--- - Add additional capabilities to all clients:
+---   ```lua
+---      vim.lsp.config('*', {
+---          capabilities = {
+---            textDocument = {
+---              semanticTokens = {
+---                multilineTokenSupport = true,
+---              }
+---            }
+---          }
+---        })
+---        ```
+--- - (Re-)define the configuration for clangd:
+---   ```lua
+---      vim.lsp.config.clangd = {
+---          cmd = {
+---            'clangd',
+---            '--clang-tidy',
+---            '--background-index',
+---            '--offset-encoding=utf-8',
+---          },
+---          root_markers = { '.clangd', 'compile_commands.json' },
+---          filetypes = { 'c', 'cpp' },
+---        }
+---        ```
+--- - Get configuration for luals:
+---   ```lua
+---      local cfg = vim.lsp.config.luals
+---        ```
+---
+--- @param name string
+--- @param cfg vim.lsp.Config
+--- @diagnostic disable-next-line:assign-type-mismatch
+function lsp.config(name, cfg)
+  local _, _ = name, cfg -- ignore unused
+  -- dummy proto for docs
+end
+
+lsp._enabled_configs = {} --- @type table
+
+--- If a config in vim.lsp.config() is accessed then the resolved config becomes invalid.
+--- @param name string
+local function invalidate_enabled_config(name)
+  if name == '*' then
+    for _, v in pairs(lsp._enabled_configs) do
+      v.resolved_config = nil
+    end
+  elseif lsp._enabled_configs[name] then
+    lsp._enabled_configs[name].resolved_config = nil
+  end
+end
+
+--- @nodoc
+--- @class vim.lsp.config
+--- @field [string] vim.lsp.Config
+--- @field package _configs table
+lsp.config = setmetatable({ _configs = {} }, {
+  --- @param self vim.lsp.config
+  --- @param name string
+  --- @return vim.lsp.Config
+  __index = function(self, name)
+    validate('name', name, 'string')
+    invalidate_enabled_config(name)
+    self._configs[name] = self._configs[name] or {}
+    return self._configs[name]
+  end,
+
+  --- @param self vim.lsp.config
+  --- @param name string
+  --- @param cfg vim.lsp.Config
+  __newindex = function(self, name, cfg)
+    validate('name', name, 'string')
+    validate('cfg', cfg, 'table')
+    invalidate_enabled_config(name)
+    self._configs[name] = cfg
+  end,
+
+  --- @param self vim.lsp.config
+  --- @param name string
+  --- @param cfg vim.lsp.Config
+  __call = function(self, name, cfg)
+    validate('name', name, 'string')
+    validate('cfg', cfg, 'table')
+    invalidate_enabled_config(name)
+    self[name] = vim.tbl_deep_extend('force', self._configs[name] or {}, cfg)
+  end,
+})
+
+--- @private
+--- @param name string
+--- @return vim.lsp.Config
+function lsp._resolve_config(name)
+  local econfig = lsp._enabled_configs[name] or {}
+
+  if not econfig.resolved_config then
+    -- Resolve configs from lsp/*.lua
+    -- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
+    local orig_configs = lsp.config._configs
+    lsp.config._configs = {}
+    pcall(vim.cmd.runtime, { ('lsp/%s.lua'):format(name), bang = true })
+    local rtp_configs = lsp.config._configs
+    lsp.config._configs = orig_configs
+
+    local config = vim.tbl_deep_extend(
+      'force',
+      lsp.config._configs['*'] or {},
+      rtp_configs[name] or {},
+      lsp.config._configs[name] or {}
+    )
+
+    config.name = name
+
+    validate('cmd', config.cmd, { 'function', 'table' })
+    validate('cmd', config.reuse_client, 'function', true)
+    -- All other fields are validated in client.create
+
+    econfig.resolved_config = config
+  end
+
+  return assert(econfig.resolved_config)
+end
+
+local lsp_enable_autocmd_id --- @type integer?
+
+--- @param bufnr integer
+local function lsp_enable_callback(bufnr)
+  -- Only ever attach to buffers that represent an actual file.
+  if vim.bo[bufnr].buftype ~= '' then
+    return
+  end
+
+  --- @param config vim.lsp.Config
+  local function can_start(config)
+    if config.filetypes and not vim.tbl_contains(config.filetypes, vim.bo[bufnr].filetype) then
+      return false
+    elseif type(config.cmd) == 'table' and vim.fn.executable(config.cmd[1]) == 0 then
+      return false
+    end
+
+    return true
+  end
+
+  for name in vim.spairs(lsp._enabled_configs) do
+    local config = lsp._resolve_config(name)
+
+    if can_start(config) then
+      -- Deepcopy config so changes done in the client
+      -- do not propagate back to the enabled configs.
+      config = vim.deepcopy(config)
+
+      vim.lsp.start(config, {
+        bufnr = bufnr,
+        reuse_client = config.reuse_client,
+        _root_markers = config.root_markers,
+      })
+    end
+  end
+end
+
+--- Enable an LSP server to automatically start when opening a buffer.
+---
+--- Uses configuration defined with `vim.lsp.config`.
+---
+--- Examples:
+---
+--- ```lua
+---   vim.lsp.enable('clangd')
+---
+---   vim.lsp.enable({'luals', 'pyright'})
+--- ```
+---
+--- @param name string|string[] Name(s) of client(s) to enable.
+--- @param enable? boolean `true|nil` to enable, `false` to disable.
+function lsp.enable(name, enable)
+  validate('name', name, { 'string', 'table' })
+
+  local names = vim._ensure_list(name) --[[@as string[] ]]
+  for _, nm in ipairs(names) do
+    if nm == '*' then
+      error('Invalid name')
+    end
+    lsp._enabled_configs[nm] = enable == false and nil or {}
+  end
+
+  if not next(lsp._enabled_configs) then
+    if lsp_enable_autocmd_id then
+      api.nvim_del_autocmd(lsp_enable_autocmd_id)
+      lsp_enable_autocmd_id = nil
+    end
+    return
+  end
+
+  -- Only ever create autocmd once to reuse computation of config merging.
+  lsp_enable_autocmd_id = lsp_enable_autocmd_id
+    or api.nvim_create_autocmd('FileType', {
+      group = api.nvim_create_augroup('nvim.lsp.enable', {}),
+      callback = function(args)
+        lsp_enable_callback(args.buf)
+      end,
+    })
+end
+
 --- @class vim.lsp.start.Opts
 --- @inlinedoc
 ---
@@ -334,6 +568,8 @@ end
 ---
 --- Suppress error reporting if the LSP server fails to start (default false).
 --- @field silent? boolean
+---
+--- @field package _root_markers? string[]
 
 --- Create a new LSP client and start a language server or reuses an already
 --- running client if one is found matching `name` and `root_dir`.
@@ -379,6 +615,11 @@ function lsp.start(config, opts)
   local reuse_client = opts.reuse_client or reuse_client_default
   local bufnr = vim._resolve_bufnr(opts.bufnr)
 
+  if not config.root_dir and opts._root_markers then
+    config = vim.deepcopy(config)
+    config.root_dir = vim.fs.root(bufnr, opts._root_markers)
+  end
+
   for _, client in pairs(all_clients) do
     if reuse_client(client, config) then
       if opts.attach == false then
@@ -387,9 +628,8 @@ function lsp.start(config, opts)
 
       if lsp.buf_attach_client(bufnr, client.id) then
         return client.id
-      else
-        return nil
       end
+      return
     end
   end
 
@@ -398,7 +638,7 @@ function lsp.start(config, opts)
     if not opts.silent then
       vim.notify(err, vim.log.levels.WARN)
     end
-    return nil
+    return
   end
 
   if opts.attach == false then
@@ -408,8 +648,6 @@ function lsp.start(config, opts)
   if client_id and lsp.buf_attach_client(bufnr, client_id) then
     return client_id
   end
-
-  return nil
 end
 
 --- Consumes the latest progress messages from all clients and formats them as a string.
@@ -1275,7 +1513,7 @@ end
 --- and the value is a function which is called if any LSP action
 --- (code action, code lenses, ...) triggers the command.
 ---
---- If a LSP response contains a command for which no matching entry is
+--- If an LSP response contains a command for which no matching entry is
 --- available in this registry, the command will be executed via the LSP server
 --- using `workspace/executeCommand`.
 ---
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 5eefe4600e..72043c18dd 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -359,16 +359,6 @@ local function get_name(id, config)
   return tostring(id)
 end
 
---- @generic T
---- @param x elem_or_list?
---- @return T[]
-local function ensure_list(x)
-  if type(x) == 'table' then
-    return x
-  end
-  return { x }
-end
-
 --- @nodoc
 --- @param config vim.lsp.ClientConfig
 --- @return vim.lsp.Client?
@@ -395,13 +385,13 @@ function Client.create(config)
     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(),
+    capabilities = config.capabilities,
     workspace_folders = lsp._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),
-    _on_exit_cbs = ensure_list(config.on_exit),
-    _on_attach_cbs = ensure_list(config.on_attach),
+    _on_init_cbs = vim._ensure_list(config.on_init),
+    _on_exit_cbs = vim._ensure_list(config.on_exit),
+    _on_attach_cbs = vim._ensure_list(config.on_attach),
     _on_error_cb = config.on_error,
     _trace = get_trace(config.trace),
 
@@ -417,6 +407,9 @@ function Client.create(config)
     messages = { name = name, messages = {}, progress = {}, status = {} },
   }
 
+  self.capabilities =
+    vim.tbl_deep_extend('force', lsp.protocol.make_client_capabilities(), self.capabilities or {})
+
   --- @class lsp.DynamicCapabilities
   --- @nodoc
   self.dynamic_capabilities = {
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index 0d314108fe..d2cf888d89 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -28,42 +28,45 @@ local function check_log()
   report_fn(string.format('Log size: %d KB', log_size / 1000))
 end
 
+--- @param f function
+--- @return string
+local function func_tostring(f)
+  local info = debug.getinfo(f, 'S')
+  return (''):format(info.source, info.linedefined)
+end
+
 local function check_active_clients()
   vim.health.start('vim.lsp: Active Clients')
   local clients = vim.lsp.get_clients()
   if next(clients) then
     for _, client in pairs(clients) do
       local cmd ---@type string
-      if type(client.config.cmd) == 'table' then
-        cmd = table.concat(client.config.cmd --[[@as table]], ' ')
-      elseif type(client.config.cmd) == 'function' then
-        cmd = tostring(client.config.cmd)
+      local ccmd = client.config.cmd
+      if type(ccmd) == 'table' then
+        cmd = vim.inspect(ccmd)
+      elseif type(ccmd) == 'function' then
+        cmd = func_tostring(ccmd)
       end
       local dirs_info ---@type string
       if client.workspace_folders and #client.workspace_folders > 1 then
-        dirs_info = string.format(
-          '  Workspace folders:\n    %s',
-          vim
-            .iter(client.workspace_folders)
-            ---@param folder lsp.WorkspaceFolder
-            :map(function(folder)
-              return folder.name
-            end)
-            :join('\n    ')
-        )
+        local wfolders = {} --- @type string[]
+        for _, dir in ipairs(client.workspace_folders) do
+          wfolders[#wfolders + 1] = dir.name
+        end
+        dirs_info = ('- Workspace folders:\n    %s'):format(table.concat(wfolders, '\n    '))
       else
         dirs_info = string.format(
-          '  Root directory: %s',
+          '- Root directory: %s',
           client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~')
         ) or nil
       end
       report_info(table.concat({
         string.format('%s (id: %d)', client.name, client.id),
         dirs_info,
-        string.format('  Command: %s', cmd),
-        string.format('  Settings: %s', vim.inspect(client.settings, { newline = '\n  ' })),
+        string.format('- Command: %s', cmd),
+        string.format('- Settings: %s', vim.inspect(client.settings, { newline = '\n  ' })),
         string.format(
-          '  Attached buffers: %s',
+          '- Attached buffers: %s',
           vim.iter(pairs(client.attached_buffers)):map(tostring):join(', ')
         ),
       }, '\n'))
@@ -174,10 +177,45 @@ local function check_position_encodings()
   end
 end
 
+local function check_enabled_configs()
+  vim.health.start('vim.lsp: Enabled Configurations')
+
+  for name in vim.spairs(vim.lsp._enabled_configs) do
+    local config = vim.lsp._resolve_config(name)
+    local text = {} --- @type string[]
+    text[#text + 1] = ('%s:'):format(name)
+    for k, v in
+      vim.spairs(config --[[@as table]])
+    do
+      local v_str --- @type string?
+      if k == 'name' then
+        v_str = nil
+      elseif k == 'filetypes' or k == 'root_markers' then
+        v_str = table.concat(v, ', ')
+      elseif type(v) == 'function' then
+        v_str = func_tostring(v)
+      else
+        v_str = vim.inspect(v, { newline = '\n  ' })
+      end
+
+      if k == 'cmd' and type(v) == 'table' and vim.fn.executable(v[1]) == 0 then
+        report_warn(("'%s' is not executable. Configuration will not be used."):format(v[1]))
+      end
+
+      if v_str then
+        text[#text + 1] = ('- %s: %s'):format(k, v_str)
+      end
+    end
+    text[#text + 1] = ''
+    report_info(table.concat(text, '\n'))
+  end
+end
+
 --- Performs a healthcheck for LSP
 function M.check()
   check_log()
   check_active_clients()
+  check_enabled_configs()
   check_watcher()
   check_position_encodings()
 end
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 0fe8e99350..24c3f243e5 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -1409,4 +1409,14 @@ function vim._resolve_bufnr(bufnr)
   return bufnr
 end
 
+--- @generic T
+--- @param x elem_or_list?
+--- @return T[]
+function vim._ensure_list(x)
+  if type(x) == 'table' then
+    return x
+  end
+  return { x }
+end
+
 return vim
-- 
cgit 


From 492ae57aa6f8973e939e9b0ade24c44b29fffcbe Mon Sep 17 00:00:00 2001
From: Colin Kennedy 
Date: Wed, 11 Dec 2024 03:27:08 -0800
Subject: docs(annotation): return types for Vimscript functions #31546

---
 runtime/lua/vim/_meta/api_keysets_extra.lua |   1 +
 runtime/lua/vim/_meta/vimfn.lua             | 154 ++++++++++++++--------------
 2 files changed, 78 insertions(+), 77 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua
index e8e901acb2..806b3e49c0 100644
--- a/runtime/lua/vim/_meta/api_keysets_extra.lua
+++ b/runtime/lua/vim/_meta/api_keysets_extra.lua
@@ -159,6 +159,7 @@ error('Cannot require a meta file')
 --- @field bg? integer
 --- @field sp? integer
 --- @field default? true
+--- @field link? string
 --- @field blend? integer
 --- @field cterm? vim.api.keyset.hl_info.cterm
 
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index e207f641de..77c78f03d4 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -1228,7 +1228,7 @@ function vim.fn.ctxpush(types) end
 ---
 --- @param context table
 --- @param index? integer
---- @return any
+--- @return integer
 function vim.fn.ctxset(context, index) end
 
 --- Returns the size of the |context-stack|.
@@ -1421,7 +1421,7 @@ function vim.fn.dictwatcherdel(dict, pattern, callback) end
 --- editing another buffer to set 'filetype' and load a syntax
 --- file.
 ---
---- @return any
+--- @return integer
 function vim.fn.did_filetype() end
 
 --- Returns the number of filler lines above line {lnum}.
@@ -1433,7 +1433,7 @@ function vim.fn.did_filetype() end
 --- Returns 0 if the current window is not in diff mode.
 ---
 --- @param lnum integer
---- @return any
+--- @return integer
 function vim.fn.diff_filler(lnum) end
 
 --- Returns the highlight ID for diff mode at line {lnum} column
@@ -1468,7 +1468,7 @@ function vim.fn.diff_hlID(lnum, col) end
 --- <
 ---
 --- @param chars string
---- @return any
+--- @return string
 function vim.fn.digraph_get(chars) end
 
 --- Return a list of digraphs.  If the {listall} argument is given
@@ -1486,7 +1486,7 @@ function vim.fn.digraph_get(chars) end
 --- <
 ---
 --- @param listall? boolean
---- @return any
+--- @return string[][]
 function vim.fn.digraph_getlist(listall) end
 
 --- Add digraph {chars} to the list.  {chars} must be a string
@@ -1538,7 +1538,7 @@ function vim.fn.digraph_setlist(digraphlist) end
 --- - A |Blob| is empty when its length is zero.
 ---
 --- @param expr any
---- @return any
+--- @return integer
 function vim.fn.empty(expr) end
 
 --- Return all of environment variables as dictionary. You can
@@ -1561,7 +1561,7 @@ function vim.fn.environ() end
 ---
 --- @param string string
 --- @param chars string
---- @return any
+--- @return string
 function vim.fn.escape(string, chars) end
 
 --- Evaluate {string} and return the result.  Especially useful to
@@ -2368,7 +2368,7 @@ function vim.fn.foldtextresult(lnum) end
 ---
 --- @param expr1 string|table
 --- @param expr2 string|function
---- @return any
+--- @return string|table
 function vim.fn.foreach(expr1, expr2) end
 
 --- Get the full command name from a short abbreviated command
@@ -2675,7 +2675,7 @@ function vim.fn.getbufinfo(dict) end
 --- @param buf integer|string
 --- @param lnum integer
 --- @param end_? integer
---- @return any
+--- @return string[]
 function vim.fn.getbufline(buf, lnum, end_) end
 
 --- Just like `getbufline()` but only get one line and return it
@@ -2943,7 +2943,7 @@ function vim.fn.getcmdprompt() end
 --- Also see |getcmdpos()|, |setcmdpos()|, |getcmdline()| and
 --- |setcmdline()|.
 ---
---- @return any
+--- @return integer
 function vim.fn.getcmdscreenpos() end
 
 --- Return the current command-line type. Possible return values
@@ -3869,7 +3869,7 @@ function vim.fn.gettagstack(winnr) end
 --- strings.
 ---
 --- @param text string
---- @return any
+--- @return string
 function vim.fn.gettext(text) end
 
 --- Returns information about windows as a |List| with Dictionaries.
@@ -4020,7 +4020,7 @@ function vim.fn.glob(expr, nosuf, list, alllinks) end
 --- a backslash usually means a path separator.
 ---
 --- @param string string
---- @return any
+--- @return string
 function vim.fn.glob2regpat(string) end
 
 --- Perform glob() for String {expr} on all directories in {path}
@@ -4354,7 +4354,7 @@ function vim.fn.hostname() end
 --- @param string string
 --- @param from string
 --- @param to string
---- @return any
+--- @return string
 function vim.fn.iconv(string, from, to) end
 
 --- Returns a |String| which is a unique identifier of the
@@ -4374,7 +4374,7 @@ function vim.fn.iconv(string, from, to) end
 --- reuse identifiers of the garbage-collected ones.
 ---
 --- @param expr any
---- @return any
+--- @return string
 function vim.fn.id(expr) end
 
 --- The result is a Number, which is indent of line {lnum} in the
@@ -4418,7 +4418,7 @@ function vim.fn.indent(lnum) end
 --- @param expr any
 --- @param start? integer
 --- @param ic? boolean
---- @return any
+--- @return integer
 function vim.fn.index(object, expr, start, ic) end
 
 --- Returns the index of an item in {object} where {expr} is
@@ -4462,14 +4462,14 @@ function vim.fn.index(object, expr, start, ic) end
 --- @param object any
 --- @param expr any
 --- @param opts? table
---- @return any
+--- @return integer
 function vim.fn.indexof(object, expr, opts) end
 
 ---
 --- @param prompt string
 --- @param text? string
 --- @param completion? string
---- @return any
+--- @return string
 function vim.fn.input(prompt, text, completion) end
 
 --- The result is a String, which is whatever the user typed on
@@ -4583,7 +4583,7 @@ function vim.fn.input(prompt, text, completion) end
 --- <
 ---
 --- @param opts table
---- @return any
+--- @return string
 function vim.fn.input(opts) end
 
 --- @deprecated
@@ -4618,7 +4618,7 @@ function vim.fn.inputlist(textlist) end
 --- called.  Calling it more often is harmless though.
 --- Returns TRUE when there is nothing to restore, FALSE otherwise.
 ---
---- @return any
+--- @return integer
 function vim.fn.inputrestore() end
 
 --- Preserve typeahead (also from mappings) and clear it, so that
@@ -4628,7 +4628,7 @@ function vim.fn.inputrestore() end
 --- many inputrestore() calls.
 --- Returns TRUE when out of memory, FALSE otherwise.
 ---
---- @return any
+--- @return integer
 function vim.fn.inputsave() end
 
 --- This function acts much like the |input()| function with but
@@ -4643,7 +4643,7 @@ function vim.fn.inputsave() end
 ---
 --- @param prompt string
 --- @param text? string
---- @return any
+--- @return string
 function vim.fn.inputsecret(prompt, text) end
 
 --- When {object} is a |List| or a |Blob| insert {item} at the start
@@ -4689,8 +4689,8 @@ function vim.fn.interrupt() end
 ---   let bits = invert(bits)
 --- <
 ---
---- @param expr number
---- @return any
+--- @param expr integer
+--- @return integer
 function vim.fn.invert(expr) end
 
 --- The result is a Number, which is |TRUE| when {path} is an
@@ -4890,7 +4890,7 @@ function vim.fn.jobsend(...) end
 ---
 --- @param cmd string|string[]
 --- @param opts? table
---- @return any
+--- @return integer
 function vim.fn.jobstart(cmd, opts) end
 
 --- Stop |job-id| {id} by sending SIGTERM to the job process. If
@@ -4903,7 +4903,7 @@ function vim.fn.jobstart(cmd, opts) end
 --- exited or stopped.
 ---
 --- @param id integer
---- @return any
+--- @return integer
 function vim.fn.jobstop(id) end
 
 --- Waits for jobs and their |on_exit| handlers to complete.
@@ -4943,7 +4943,7 @@ function vim.fn.jobwait(jobs, timeout) end
 ---
 --- @param list any[]
 --- @param sep? string
---- @return any
+--- @return string
 function vim.fn.join(list, sep) end
 
 --- Convert {expr} from JSON object.  Accepts |readfile()|-style
@@ -4976,14 +4976,14 @@ function vim.fn.json_decode(expr) end
 --- |Blob|s are converted to arrays of the individual bytes.
 ---
 --- @param expr any
---- @return any
+--- @return string
 function vim.fn.json_encode(expr) end
 
 --- Return a |List| with all the keys of {dict}.  The |List| is in
 --- arbitrary order.  Also see |items()| and |values()|.
 ---
 --- @param dict table
---- @return any
+--- @return string[]
 function vim.fn.keys(dict) end
 
 --- Turn the internal byte representation of keys into a form that
@@ -4993,7 +4993,7 @@ function vim.fn.keys(dict) end
 --- <  
 ---
 --- @param string string
---- @return any
+--- @return string
 function vim.fn.keytrans(string) end
 
 --- @deprecated
@@ -5012,8 +5012,8 @@ function vim.fn.last_buffer_nr() end
 --- |Dictionary| is returned.
 --- Otherwise an error is given and returns zero.
 ---
---- @param expr any
---- @return any
+--- @param expr any[]
+--- @return integer
 function vim.fn.len(expr) end
 
 --- Call function {funcname} in the run-time library {libname}
@@ -5124,7 +5124,7 @@ function vim.fn.line2byte(lnum) end
 --- When {lnum} is invalid, -1 is returned.
 ---
 --- @param lnum integer
---- @return any
+--- @return integer
 function vim.fn.lispindent(lnum) end
 
 --- Return a Blob concatenating all the number values in {list}.
@@ -5137,7 +5137,7 @@ function vim.fn.lispindent(lnum) end
 --- |blob2list()| does the opposite.
 ---
 --- @param list any[]
---- @return any
+--- @return string
 function vim.fn.list2blob(list) end
 
 --- Convert each number in {list} to a character string can
@@ -5157,13 +5157,13 @@ function vim.fn.list2blob(list) end
 ---
 --- @param list any[]
 --- @param utf8? boolean
---- @return any
+--- @return string
 function vim.fn.list2str(list, utf8) end
 
 --- Return the current time, measured as seconds since 1st Jan
 --- 1970.  See also |strftime()|, |strptime()| and |getftime()|.
 ---
---- @return any
+--- @return integer
 function vim.fn.localtime() end
 
 --- Return the natural logarithm (base e) of {expr} as a |Float|.
@@ -5177,7 +5177,7 @@ function vim.fn.localtime() end
 --- <  5.0
 ---
 --- @param expr number
---- @return any
+--- @return number
 function vim.fn.log(expr) end
 
 --- Return the logarithm of Float {expr} to base 10 as a |Float|.
@@ -5190,7 +5190,7 @@ function vim.fn.log(expr) end
 --- <  -2.0
 ---
 --- @param expr number
---- @return any
+--- @return number
 function vim.fn.log10(expr) end
 
 --- {expr1} must be a |List|, |String|, |Blob| or |Dictionary|.
@@ -6090,7 +6090,7 @@ function vim.fn.menu_info(name, mode) end
 --- an error.  An empty |List| or |Dictionary| results in zero.
 ---
 --- @param expr any
---- @return any
+--- @return number
 function vim.fn.min(expr) end
 
 --- Create directory {name}.
@@ -6135,7 +6135,7 @@ function vim.fn.min(expr) end
 --- @param name string
 --- @param flags? string
 --- @param prot? string
---- @return any
+--- @return integer
 function vim.fn.mkdir(name, flags, prot) end
 
 --- Return a string that indicates the current mode.
@@ -6298,7 +6298,7 @@ function vim.fn.msgpackparse(data) end
 --- See also |prevnonblank()|.
 ---
 --- @param lnum integer
---- @return any
+--- @return integer
 function vim.fn.nextnonblank(lnum) end
 
 --- Return a string with a single character, which has the number
@@ -6317,7 +6317,7 @@ function vim.fn.nextnonblank(lnum) end
 ---
 --- @param expr integer
 --- @param utf8? boolean
---- @return any
+--- @return string
 function vim.fn.nr2char(expr, utf8) end
 
 --- Bitwise OR on the two arguments.  The arguments are converted
@@ -6351,7 +6351,7 @@ vim.fn['or'] = function(expr, expr1) end
 ---
 --- @param path string
 --- @param len? integer
---- @return any
+--- @return string
 function vim.fn.pathshorten(path, len) end
 
 --- Evaluate |perl| expression {expr} and return its result
@@ -6385,7 +6385,7 @@ function vim.fn.perleval(expr) end
 ---
 --- @param x number
 --- @param y number
---- @return any
+--- @return number
 function vim.fn.pow(x, y) end
 
 --- Return the line number of the first line at or above {lnum}
@@ -6397,7 +6397,7 @@ function vim.fn.pow(x, y) end
 --- Also see |nextnonblank()|.
 ---
 --- @param lnum integer
---- @return any
+--- @return integer
 function vim.fn.prevnonblank(lnum) end
 
 --- Return a String with {fmt}, where "%" items are replaced by
@@ -7172,7 +7172,7 @@ function vim.fn.remove(dict, key) end
 ---
 --- @param from string
 --- @param to string
---- @return any
+--- @return integer
 function vim.fn.rename(from, to) end
 
 --- Repeat {expr} {count} times and return the concatenated
@@ -7202,7 +7202,7 @@ vim.fn['repeat'] = function(expr, count) end
 --- path name) and also keeps a trailing path separator.
 ---
 --- @param filename string
---- @return any
+--- @return string
 function vim.fn.resolve(filename) end
 
 --- Reverse the order of items in {object}.  {object} can be a
@@ -7233,7 +7233,7 @@ function vim.fn.reverse(object) end
 --- <  -5.0
 ---
 --- @param expr number
---- @return any
+--- @return number
 function vim.fn.round(expr) end
 
 --- Sends {event} to {channel} via |RPC| and returns immediately.
@@ -7245,7 +7245,7 @@ function vim.fn.round(expr) end
 --- @param channel integer
 --- @param event string
 --- @param ... any
---- @return any
+--- @return integer
 function vim.fn.rpcnotify(channel, event, ...) end
 
 --- Sends a request to {channel} to invoke {method} via
@@ -7302,7 +7302,7 @@ function vim.fn.rubyeval(expr) end
 ---
 --- @param row integer
 --- @param col integer
---- @return any
+--- @return integer
 function vim.fn.screenattr(row, col) end
 
 --- The result is a Number, which is the character at position
@@ -7316,7 +7316,7 @@ function vim.fn.screenattr(row, col) end
 ---
 --- @param row integer
 --- @param col integer
---- @return any
+--- @return integer
 function vim.fn.screenchar(row, col) end
 
 --- The result is a |List| of Numbers.  The first number is the same
@@ -7327,7 +7327,7 @@ function vim.fn.screenchar(row, col) end
 ---
 --- @param row integer
 --- @param col integer
---- @return any
+--- @return integer[]
 function vim.fn.screenchars(row, col) end
 
 --- The result is a Number, which is the current screen column of
@@ -7344,7 +7344,7 @@ function vim.fn.screenchars(row, col) end
 ---   noremap GG echom screencol()
 --- <
 ---
---- @return any
+--- @return integer[]
 function vim.fn.screencol() end
 
 --- The result is a Dict with the screen position of the text
@@ -7383,7 +7383,7 @@ function vim.fn.screenpos(winid, lnum, col) end
 ---
 --- Note: Same restrictions as with |screencol()|.
 ---
---- @return any
+--- @return integer
 function vim.fn.screenrow() end
 
 --- The result is a String that contains the base character and
@@ -7395,7 +7395,7 @@ function vim.fn.screenrow() end
 ---
 --- @param row integer
 --- @param col integer
---- @return any
+--- @return string
 function vim.fn.screenstring(row, col) end
 
 --- Search for regexp pattern {pattern}.  The search starts at the
@@ -7800,7 +7800,7 @@ function vim.fn.searchpos(pattern, flags, stopline, timeout, skip) end
 ---   echo serverlist()
 --- <
 ---
---- @return any
+--- @return string[]
 function vim.fn.serverlist() end
 
 --- Opens a socket or named pipe at {address} and listens for
@@ -7837,7 +7837,7 @@ function vim.fn.serverlist() end
 --- <
 ---
 --- @param address? string
---- @return any
+--- @return string
 function vim.fn.serverstart(address) end
 
 --- Closes the pipe or socket at {address}.
@@ -7846,7 +7846,7 @@ function vim.fn.serverstart(address) end
 --- address in |serverlist()|.
 ---
 --- @param address string
---- @return any
+--- @return integer
 function vim.fn.serverstop(address) end
 
 --- Set line {lnum} to {text} in buffer {buf}.  This works like
@@ -7876,7 +7876,7 @@ function vim.fn.serverstop(address) end
 --- @param buf integer|string
 --- @param lnum integer
 --- @param text string|string[]
---- @return any
+--- @return integer
 function vim.fn.setbufline(buf, lnum, text) end
 
 --- Set option or local variable {varname} in buffer {buf} to
@@ -7981,7 +7981,7 @@ function vim.fn.setcharsearch(dict) end
 ---
 --- @param str string
 --- @param pos? integer
---- @return any
+--- @return integer
 function vim.fn.setcmdline(str, pos) end
 
 --- Set the cursor position in the command line to byte position
@@ -8291,7 +8291,7 @@ function vim.fn.setpos(expr, list) end
 --- @param list vim.quickfix.entry[]
 --- @param action? string
 --- @param what? vim.fn.setqflist.what
---- @return any
+--- @return integer
 function vim.fn.setqflist(list, action, what) end
 
 --- Set the register {regname} to {value}.
@@ -8444,7 +8444,7 @@ function vim.fn.setwinvar(nr, varname, val) end
 --- checksum of {string}.
 ---
 --- @param string string
---- @return any
+--- @return string
 function vim.fn.sha256(string) end
 
 --- Escape {string} for use as a shell command argument.
@@ -8480,7 +8480,7 @@ function vim.fn.sha256(string) end
 ---
 --- @param string string
 --- @param special? boolean
---- @return any
+--- @return string
 function vim.fn.shellescape(string, special) end
 
 --- Returns the effective value of 'shiftwidth'. This is the
@@ -8932,7 +8932,7 @@ function vim.fn.sign_unplacelist(list) end
 --- links before simplifying the path name, use |resolve()|.
 ---
 --- @param filename string
---- @return any
+--- @return string
 function vim.fn.simplify(filename) end
 
 --- Return the sine of {expr}, measured in radians, as a |Float|.
@@ -8945,7 +8945,7 @@ function vim.fn.simplify(filename) end
 --- <  0.763301
 ---
 --- @param expr number
---- @return any
+--- @return number
 function vim.fn.sin(expr) end
 
 --- Return the hyperbolic sine of {expr} as a |Float| in the range
@@ -9093,7 +9093,7 @@ function vim.fn.sort(list, how, dict) end
 --- the method can be quite slow.
 ---
 --- @param word string
---- @return any
+--- @return string
 function vim.fn.soundfold(word) end
 
 --- Without argument: The result is the badly spelled word under
@@ -9146,7 +9146,7 @@ function vim.fn.spellbadword(sentence) end
 --- @param word string
 --- @param max? integer
 --- @param capital? boolean
---- @return any
+--- @return string[]
 function vim.fn.spellsuggest(word, max, capital) end
 
 --- Make a |List| out of {string}.  When {pattern} is omitted or
@@ -9176,7 +9176,7 @@ function vim.fn.spellsuggest(word, max, capital) end
 --- @param string string
 --- @param pattern? string
 --- @param keepempty? boolean
---- @return any
+--- @return string[]
 function vim.fn.split(string, pattern, keepempty) end
 
 --- Return the non-negative square root of Float {expr} as a
@@ -10179,7 +10179,7 @@ function vim.fn.tempname() end
 ---
 --- @param cmd string|string[]
 --- @param opts? table
---- @return any
+--- @return integer
 function vim.fn.termopen(cmd, opts) end
 
 --- Return a list with information about timers.
@@ -10579,7 +10579,7 @@ function vim.fn.virtcol(expr, list, winid) end
 --- @param winid integer
 --- @param lnum integer
 --- @param col integer
---- @return any
+--- @return integer
 function vim.fn.virtcol2col(winid, lnum, col) end
 
 --- The result is a String, which describes the last Visual mode
@@ -10600,7 +10600,7 @@ function vim.fn.virtcol2col(winid, lnum, col) end
 --- the old value is returned.  See |non-zero-arg|.
 ---
 --- @param expr? boolean
---- @return any
+--- @return string
 function vim.fn.visualmode(expr) end
 
 --- Waits until {condition} evaluates to |TRUE|, where {condition}
@@ -10717,7 +10717,7 @@ function vim.fn.win_id2tabwin(expr) end
 --- Return 0 if the window cannot be found in the current tabpage.
 ---
 --- @param expr integer
---- @return any
+--- @return integer
 function vim.fn.win_id2win(expr) end
 
 --- Move window {nr}'s vertical separator (i.e., the right border)
@@ -10915,7 +10915,7 @@ function vim.fn.winline() end
 --- <
 ---
 --- @param arg? string|integer
---- @return any
+--- @return integer
 function vim.fn.winnr(arg) end
 
 --- Returns a sequence of |:resize| commands that should restore
@@ -10928,7 +10928,7 @@ function vim.fn.winnr(arg) end
 ---   exe cmd
 --- <
 ---
---- @return any
+--- @return string
 function vim.fn.winrestcmd() end
 
 --- Uses the |Dictionary| returned by |winsaveview()| to restore
@@ -10993,7 +10993,7 @@ function vim.fn.winsaveview() end
 --- option.
 ---
 --- @param nr integer
---- @return any
+--- @return integer
 function vim.fn.winwidth(nr) end
 
 --- The result is a dictionary of byte/chars/word statistics for
@@ -11078,7 +11078,7 @@ function vim.fn.writefile(object, fname, flags) end
 ---   let bits = xor(bits, 0x80)
 --- <
 ---
---- @param expr number
---- @param expr1 number
---- @return any
+--- @param expr integer
+--- @param expr1 integer
+--- @return integer
 function vim.fn.xor(expr, expr1) end
-- 
cgit 


From 3dfb9e6f60d9ca27ff140a9300cc1a43e38aa2ee Mon Sep 17 00:00:00 2001
From: Riley Bruins 
Date: Wed, 11 Dec 2024 04:34:24 -0800
Subject: feat(treesitter): include capture id in return value of
 `get_captures_at_pos()` #30559

**Problem:** Currently, it is difficult to get node(s)-level metadata
for a capture returned by `get_captures_at_pos()`. This is because it is
stored in `metadata[id]` and we do not have access to the value of `id`,
so to get this value we have to iterate over the keys of `metadata`. See
[this commit](https://github.com/neovim/neovim/commit/d63622930001b39b12f14112fc3abb55b760c447#diff-8bd4742121c2f359d0345f3c6c253a58220f1a28670cc4e1c957992232059a6cR16).

Things would be much simpler if we were given the `id` of the capture so
we could use it to just index `metadata` directly.

**Solution:** Include `id` in the data returned by
`get_captures_at_pos()`
---
 runtime/lua/vim/treesitter.lua | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 5f4e796413..89dc4e289a 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -245,14 +245,15 @@ end
 
 --- Returns a list of highlight captures at the given position
 ---
---- Each capture is represented by a table containing the capture name as a string as
---- well as a table of metadata (`priority`, `conceal`, ...; empty if none are defined).
+--- Each capture is represented by a table containing the capture name as a string, the capture's
+--- language, a table of metadata (`priority`, `conceal`, ...; empty if none are defined), and the
+--- id of the capture.
 ---
 ---@param bufnr integer Buffer number (0 for current buffer)
 ---@param row integer Position row
 ---@param col integer Position column
 ---
----@return {capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata}[]
+---@return {capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata, id: integer}[]
 function M.get_captures_at_pos(bufnr, row, col)
   bufnr = vim._resolve_bufnr(bufnr)
   local buf_highlighter = M.highlighter.active[bufnr]
@@ -285,12 +286,15 @@ function M.get_captures_at_pos(bufnr, row, col)
 
     local iter = q:query():iter_captures(root, buf_highlighter.bufnr, row, row + 1)
 
-    for capture, node, metadata in iter do
+    for id, node, metadata in iter do
       if M.is_in_node_range(node, row, col) then
         ---@diagnostic disable-next-line: invisible
-        local c = q._query.captures[capture] -- name of the capture in the query
-        if c ~= nil then
-          table.insert(matches, { capture = c, metadata = metadata, lang = tree:lang() })
+        local capture = q._query.captures[id] -- name of the capture in the query
+        if capture ~= nil then
+          table.insert(
+            matches,
+            { capture = capture, metadata = metadata, lang = tree:lang(), id = id }
+          )
         end
       end
     end
-- 
cgit 


From 442d338cb50e4cf08c58cb82b6d33b6d5df9fb1b Mon Sep 17 00:00:00 2001
From: Jonny Kong 
Date: Wed, 11 Dec 2024 08:48:17 -0500
Subject: fix(uri): uri_encode encodes brackets incorrectly for RFC2732 #31284

**Problem:**
The brackets in the RFC2732 regular expression are currently unescaped,
causing them to be misinterpreted as special characters denoting
character groups rather than as literal characters.

**Solution:**
Escape the brackets.
Fix #31270
---
 runtime/lua/vim/uri.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua
index b4e4098b91..6323f61256 100644
--- a/runtime/lua/vim/uri.lua
+++ b/runtime/lua/vim/uri.lua
@@ -15,7 +15,7 @@ local PATTERNS = {
   rfc2396 = "^A-Za-z0-9%-_.!~*'()",
   -- RFC 2732
   -- https://tools.ietf.org/html/rfc2732
-  rfc2732 = "^A-Za-z0-9%-_.!~*'()[]",
+  rfc2732 = "^A-Za-z0-9%-_.!~*'()%[%]",
   -- RFC 3986
   -- https://tools.ietf.org/html/rfc3986#section-2.2
   rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/",
-- 
cgit 


From 21961967ffef6d49512b83a23b6c93bb8b80389a Mon Sep 17 00:00:00 2001
From: Jeremy Fleischman 
Date: Wed, 11 Dec 2024 17:29:54 -0800
Subject: feat(diagnostic): update quickfix list by title #31486

Previously, there was a singleton diagnostics quickfix list. Now there's
effectively one per title (up to vim's internal limit on quickfix
lists).

Suggested by mfussenegger https://github.com/neovim/neovim/pull/30868#pullrequestreview-2385761374.
---
 runtime/lua/vim/diagnostic.lua | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 340bca4f6b..1b61cf8f71 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -2,7 +2,19 @@ local api, if_nil = vim.api, vim.F.if_nil
 
 local M = {}
 
-local _qf_id = nil
+--- @param title string
+--- @return integer?
+local function get_qf_id_for_title(title)
+  local lastqflist = vim.fn.getqflist({ nr = '$' })
+  for i = 1, lastqflist.nr do
+    local qflist = vim.fn.getqflist({ nr = i, id = 0, title = 0 })
+    if qflist.title == title then
+      return qflist.id
+    end
+  end
+
+  return nil
+end
 
 --- [diagnostic-structure]()
 ---
@@ -845,24 +857,16 @@ local function set_list(loclist, opts)
   if loclist then
     vim.fn.setloclist(winnr, {}, 'u', { title = title, items = items })
   else
-    -- Check if the diagnostics quickfix list no longer exists.
-    if _qf_id and vim.fn.getqflist({ id = _qf_id }).id == 0 then
-      _qf_id = nil
-    end
+    local qf_id = get_qf_id_for_title(title)
 
     -- If we already have a diagnostics quickfix, update it rather than creating a new one.
     -- This avoids polluting the finite set of quickfix lists, and preserves the currently selected
     -- entry.
-    vim.fn.setqflist({}, _qf_id and 'u' or ' ', {
+    vim.fn.setqflist({}, qf_id and 'u' or ' ', {
       title = title,
       items = items,
-      id = _qf_id,
+      id = qf_id,
     })
-
-    -- Get the id of the newly created quickfix list.
-    if _qf_id == nil then
-      _qf_id = vim.fn.getqflist({ id = 0 }).id
-    end
   end
   if open then
     api.nvim_command(loclist and 'lwindow' or 'botright cwindow')
@@ -2037,7 +2041,8 @@ end
 --- (default: `true`)
 --- @field open? boolean
 ---
---- Title of quickfix list. Defaults to "Diagnostics".
+--- Title of quickfix list. Defaults to "Diagnostics". If there's already a quickfix list with this
+--- title, it's updated. If not, a new quickfix list is created.
 --- @field title? string
 ---
 --- See |diagnostic-severity|.
-- 
cgit 


From 5eda7aafe995bfefd46fe859f32fa8581ab7f15d Mon Sep 17 00:00:00 2001
From: Jeremy Fleischman 
Date: Wed, 11 Dec 2024 17:32:07 -0800
Subject: fix(diagnostic): setqflist() is stuck after
 vim.lsp.buf.document_symbol #31553

Previously, when updating the quickfix diagnostics list, we'd update it,
and then open the quickfix buffer, but there was no guarantee that the
quickfix buffer would be displaying the quickfix diagnostics list (it
could very possibly be displaying some other quickfix list!).

This fixes things so we first select the quickfix list before opening the
quickfix buffer. If `open` is not specified, the behavior is the same as
before: we update the diagnostics quickfix list, but do not navigate to
it.

fixes https://github.com/neovim/neovim/issues/31540
---
 runtime/lua/vim/diagnostic.lua | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 1b61cf8f71..62997924d9 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -869,7 +869,16 @@ local function set_list(loclist, opts)
     })
   end
   if open then
-    api.nvim_command(loclist and 'lwindow' or 'botright cwindow')
+    if loclist then
+      api.nvim_command('lwindow')
+    else
+      -- First navigate to the diagnostics quickfix list.
+      local nr = vim.fn.getqflist({ id = _qf_id, nr = 0 }).nr
+      api.nvim_command(nr .. 'chistory')
+
+      -- Now open the quickfix list.
+      api.nvim_command('botright cwindow')
+    end
   end
 end
 
-- 
cgit 


From 130f4344cf1a8fdafcf62b392ead863d1a1379f3 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Sun, 8 Dec 2024 10:32:29 +0000
Subject: refactor(lsp/rpc): move transport logic to separate module

---
 runtime/lua/vim/lsp/_transport.lua | 182 +++++++++++++++++++++++
 runtime/lua/vim/lsp/rpc.lua        | 288 +++++++++++--------------------------
 2 files changed, 265 insertions(+), 205 deletions(-)
 create mode 100644 runtime/lua/vim/lsp/_transport.lua

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/_transport.lua b/runtime/lua/vim/lsp/_transport.lua
new file mode 100644
index 0000000000..19ff2a8ab0
--- /dev/null
+++ b/runtime/lua/vim/lsp/_transport.lua
@@ -0,0 +1,182 @@
+local uv = vim.uv
+local log = require('vim.lsp.log')
+
+local is_win = vim.fn.has('win32') == 1
+
+--- Checks whether a given path exists and is a directory.
+---@param filename string path to check
+---@return boolean
+local function is_dir(filename)
+  local stat = uv.fs_stat(filename)
+  return stat and stat.type == 'directory' or false
+end
+
+--- @class (private) vim.lsp.rpc.Transport
+--- @field write fun(self: vim.lsp.rpc.Transport, msg: string)
+--- @field is_closing fun(self: vim.lsp.rpc.Transport): boolean
+--- @field terminate fun(self: vim.lsp.rpc.Transport)
+
+--- @class (private,exact) vim.lsp.rpc.Transport.Run : vim.lsp.rpc.Transport
+--- @field new fun(): vim.lsp.rpc.Transport.Run
+--- @field sysobj? vim.SystemObj
+local TransportRun = {}
+
+--- @return vim.lsp.rpc.Transport.Run
+function TransportRun.new()
+  return setmetatable({}, { __index = TransportRun })
+end
+
+--- @param cmd string[] Command to start the LSP server.
+--- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams
+--- @param on_read fun(err: any, data: string)
+--- @param on_exit fun(code: integer, signal: integer)
+function TransportRun:run(cmd, extra_spawn_params, on_read, on_exit)
+  local function on_stderr(_, chunk)
+    if chunk then
+      log.error('rpc', cmd[1], 'stderr', chunk)
+    end
+  end
+
+  extra_spawn_params = extra_spawn_params or {}
+
+  if extra_spawn_params.cwd then
+    assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
+  end
+
+  local detached = not is_win
+  if extra_spawn_params.detached ~= nil then
+    detached = extra_spawn_params.detached
+  end
+
+  local ok, sysobj_or_err = pcall(vim.system, cmd, {
+    stdin = true,
+    stdout = on_read,
+    stderr = on_stderr,
+    cwd = extra_spawn_params.cwd,
+    env = extra_spawn_params.env,
+    detach = detached,
+  }, function(obj)
+    on_exit(obj.code, obj.signal)
+  end)
+
+  if not ok then
+    local err = sysobj_or_err --[[@as string]]
+    local sfx = err:match('ENOENT')
+        and '. The language server is either not installed, missing from PATH, or not executable.'
+      or string.format(' with error message: %s', err)
+
+    error(('Spawning language server with cmd: `%s` failed%s'):format(vim.inspect(cmd), sfx))
+  end
+
+  self.sysobj = sysobj_or_err --[[@as vim.SystemObj]]
+end
+
+function TransportRun:write(msg)
+  assert(self.sysobj):write(msg)
+end
+
+function TransportRun:is_closing()
+  return self.sysobj == nil or self.sysobj:is_closing()
+end
+
+function TransportRun:terminate()
+  assert(self.sysobj):kill(15)
+end
+
+--- @class (private,exact) vim.lsp.rpc.Transport.Connect : vim.lsp.rpc.Transport
+--- @field new fun(): vim.lsp.rpc.Transport.Connect
+--- @field handle? uv.uv_pipe_t|uv.uv_tcp_t
+--- Connect returns a PublicClient synchronously so the caller
+--- can immediately send messages before the connection is established
+--- -> Need to buffer them until that happens
+--- @field connected boolean
+--- @field closing boolean
+--- @field msgbuf vim.Ringbuf
+--- @field on_exit? fun(code: integer, signal: integer)
+local TransportConnect = {}
+
+--- @return vim.lsp.rpc.Transport.Connect
+function TransportConnect.new()
+  return setmetatable({
+    connected = false,
+    -- size should be enough because the client can't really do anything until initialization is done
+    -- which required a response from the server - implying the connection got established
+    msgbuf = vim.ringbuf(10),
+    closing = false,
+  }, { __index = TransportConnect })
+end
+
+--- @param host_or_path string
+--- @param port? integer
+--- @param on_read fun(err: any, data: string)
+--- @param on_exit? fun(code: integer, signal: integer)
+function TransportConnect:connect(host_or_path, port, on_read, on_exit)
+  self.on_exit = on_exit
+  self.handle = (
+    port and assert(uv.new_tcp(), 'Could not create new TCP socket')
+    or assert(uv.new_pipe(false), 'Pipe could not be opened.')
+  )
+
+  local function on_connect(err)
+    if err then
+      local address = not port and host_or_path or (host_or_path .. ':' .. port)
+      vim.schedule(function()
+        vim.notify(
+          string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)),
+          vim.log.levels.WARN
+        )
+      end)
+      return
+    end
+    self.handle:read_start(on_read)
+    self.connected = true
+    for msg in self.msgbuf do
+      self.handle:write(msg)
+    end
+  end
+
+  if not port then
+    self.handle:connect(host_or_path, on_connect)
+    return
+  end
+
+  --- @diagnostic disable-next-line:param-type-mismatch bad UV typing
+  local info = uv.getaddrinfo(host_or_path, nil)
+  local resolved_host = info and info[1] and info[1].addr or host_or_path
+  self.handle:connect(resolved_host, port, on_connect)
+end
+
+function TransportConnect:write(msg)
+  if self.connected then
+    local _, err = self.handle:write(msg)
+    if err and not self.closing then
+      log.error('Error on handle:write: %q', err)
+    end
+    return
+  end
+
+  self.msgbuf:push(msg)
+end
+
+function TransportConnect:is_closing()
+  return self.closing
+end
+
+function TransportConnect:terminate()
+  if self.closing then
+    return
+  end
+  self.closing = true
+  if self.handle then
+    self.handle:shutdown()
+    self.handle:close()
+  end
+  if self.on_exit then
+    self.on_exit(0, 0)
+  end
+end
+
+return {
+  TransportRun = TransportRun,
+  TransportConnect = TransportConnect,
+}
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 2327a37ab1..a0d1fe776b 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -1,18 +1,8 @@
-local uv = vim.uv
 local log = require('vim.lsp.log')
 local protocol = require('vim.lsp.protocol')
+local lsp_transport = require('vim.lsp._transport')
 local validate, schedule_wrap = vim.validate, vim.schedule_wrap
 
-local is_win = vim.fn.has('win32') == 1
-
---- Checks whether a given path exists and is a directory.
----@param filename string path to check
----@return boolean
-local function is_dir(filename)
-  local stat = uv.fs_stat(filename)
-  return stat and stat.type == 'directory' or false
-end
-
 --- Embeds the given string into a table and correctly computes `Content-Length`.
 ---
 ---@param message string
@@ -242,8 +232,11 @@ local default_dispatchers = {
   end,
 }
 
----@private
-function M.create_read_loop(handle_body, on_no_chunk, on_error)
+--- @private
+--- @param handle_body fun(body: string)
+--- @param on_exit? fun()
+--- @param on_error fun(err: any)
+function M.create_read_loop(handle_body, on_exit, on_error)
   local parse_chunk = coroutine.wrap(request_parser_loop) --[[@as fun(chunk: string?): vim.lsp.rpc.Headers?, string?]]
   parse_chunk()
   return function(err, chunk)
@@ -253,8 +246,8 @@ function M.create_read_loop(handle_body, on_no_chunk, on_error)
     end
 
     if not chunk then
-      if on_no_chunk then
-        on_no_chunk()
+      if on_exit then
+        on_exit()
       end
       return
     end
@@ -262,7 +255,7 @@ function M.create_read_loop(handle_body, on_no_chunk, on_error)
     while true do
       local headers, body = parse_chunk(chunk)
       if headers then
-        handle_body(body)
+        handle_body(assert(body))
         chunk = ''
       else
         break
@@ -282,14 +275,14 @@ local Client = {}
 ---@private
 function Client:encode_and_send(payload)
   log.debug('rpc.send', payload)
-  if self.transport.is_closing() then
+  if self.transport:is_closing() then
     return false
   end
   local jsonstr = assert(
     vim.json.encode(payload),
     string.format("Couldn't encode payload '%s'", vim.inspect(payload))
   )
-  self.transport.write(format_message_with_content_length(jsonstr))
+  self.transport:write(format_message_with_content_length(jsonstr))
   return true
 end
 
@@ -323,7 +316,7 @@ end
 ---@param method string The invoked LSP method
 ---@param params table? Parameters for the invoked LSP method
 ---@param callback fun(err?: lsp.ResponseError, result: any) Callback to invoke
----@param notify_reply_callback fun(message_id: integer)|nil Callback to invoke as soon as a request is no longer pending
+---@param notify_reply_callback? fun(message_id: integer) Callback to invoke as soon as a request is no longer pending
 ---@return boolean success `true` if request could be sent, `false` if not
 ---@return integer? message_id if request could be sent, `nil` if not
 function Client:request(method, params, callback, notify_reply_callback)
@@ -337,21 +330,16 @@ function Client:request(method, params, callback, notify_reply_callback)
     method = method,
     params = params,
   })
-  local message_callbacks = self.message_callbacks
-  local notify_reply_callbacks = self.notify_reply_callbacks
-  if result then
-    if message_callbacks then
-      message_callbacks[message_id] = schedule_wrap(callback)
-    else
-      return false, nil
-    end
-    if notify_reply_callback and notify_reply_callbacks then
-      notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback)
-    end
-    return result, message_id
-  else
-    return false, nil
+
+  if not result then
+    return false
+  end
+
+  self.message_callbacks[message_id] = schedule_wrap(callback)
+  if notify_reply_callback then
+    self.notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback)
   end
+  return result, message_id
 end
 
 ---@package
@@ -370,7 +358,7 @@ end
 ---@param ... any
 ---@return boolean status
 ---@return any head
----@return any|nil ...
+---@return any? ...
 function Client:pcall_handler(errkind, status, head, ...)
   if not status then
     self:on_error(errkind, head, ...)
@@ -385,7 +373,7 @@ end
 ---@param ... any
 ---@return boolean status
 ---@return any head
----@return any|nil ...
+---@return any? ...
 function Client:try_call(errkind, fn, ...)
   return self:pcall_handler(errkind, pcall(fn, ...))
 end
@@ -394,7 +382,8 @@ end
 -- time and log them. This would require storing the timestamp. I could call
 -- them with an error then, perhaps.
 
----@package
+--- @package
+--- @param body string
 function Client:handle_body(body)
   local ok, decoded = pcall(vim.json.decode, body, { luanil = { object = true } })
   if not ok then
@@ -406,7 +395,7 @@ function Client:handle_body(body)
   if type(decoded) ~= 'table' then
     self:on_error(M.client_errors.INVALID_SERVER_MESSAGE, decoded)
   elseif type(decoded.method) == 'string' and decoded.id then
-    local err --- @type lsp.ResponseError|nil
+    local err --- @type lsp.ResponseError?
     -- Schedule here so that the users functions don't trigger an error and
     -- we can still use the result.
     vim.schedule(coroutine.wrap(function()
@@ -453,45 +442,36 @@ function Client:handle_body(body)
     local result_id = assert(tonumber(decoded.id), 'response id must be a number')
 
     -- Notify the user that a response was received for the request
-    local notify_reply_callbacks = self.notify_reply_callbacks
-    local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id]
+    local notify_reply_callback = self.notify_reply_callbacks[result_id]
     if notify_reply_callback then
       validate('notify_reply_callback', notify_reply_callback, 'function')
       notify_reply_callback(result_id)
-      notify_reply_callbacks[result_id] = nil
+      self.notify_reply_callbacks[result_id] = nil
     end
 
-    local message_callbacks = self.message_callbacks
-
     -- Do not surface RequestCancelled to users, it is RPC-internal.
     if decoded.error then
-      local mute_error = false
+      assert(type(decoded.error) == 'table')
       if decoded.error.code == protocol.ErrorCodes.RequestCancelled then
         log.debug('Received cancellation ack', decoded)
-        mute_error = true
-      end
-
-      if mute_error then
         -- Clear any callback since this is cancelled now.
         -- This is safe to do assuming that these conditions hold:
         -- - The server will not send a result callback after this cancellation.
         -- - If the server sent this cancellation ACK after sending the result, the user of this RPC
         -- client will ignore the result themselves.
-        if result_id and message_callbacks then
-          message_callbacks[result_id] = nil
+        if result_id then
+          self.message_callbacks[result_id] = nil
         end
         return
       end
     end
 
-    local callback = message_callbacks and message_callbacks[result_id]
+    local callback = self.message_callbacks[result_id]
     if callback then
-      message_callbacks[result_id] = nil
+      self.message_callbacks[result_id] = nil
       validate('callback', callback, 'function')
       if decoded.error then
-        decoded.error = setmetatable(decoded.error, {
-          __tostring = M.format_rpc_error,
-        })
+        setmetatable(decoded.error, { __tostring = M.format_rpc_error })
       end
       self:try_call(
         M.client_errors.SERVER_RESULT_CALLBACK_ERROR,
@@ -517,11 +497,6 @@ function Client:handle_body(body)
   end
 end
 
----@class (private) vim.lsp.rpc.Transport
----@field write fun(msg: string)
----@field is_closing fun(): boolean
----@field terminate fun()
-
 ---@param dispatchers vim.lsp.rpc.Dispatchers
 ---@param transport vim.lsp.rpc.Transport
 ---@return vim.lsp.rpc.Client
@@ -536,11 +511,20 @@ local function new_client(dispatchers, transport)
   return setmetatable(state, { __index = Client })
 end
 
----@class vim.lsp.rpc.PublicClient
----@field request fun(method: string, params: table?, callback: fun(err: lsp.ResponseError|nil, result: any), notify_reply_callback: fun(message_id: integer)|nil):boolean,integer? see |vim.lsp.rpc.request()|
----@field notify fun(method: string, params: any):boolean see |vim.lsp.rpc.notify()|
----@field is_closing fun(): boolean
----@field terminate fun()
+--- Client RPC object
+--- @class vim.lsp.rpc.PublicClient
+---
+--- See [vim.lsp.rpc.request()]
+--- @field request fun(method: string, params: table?, callback: fun(err?: lsp.ResponseError, result: any), notify_reply_callback?: fun(message_id: integer)):boolean,integer?
+---
+--- See [vim.lsp.rpc.notify()]
+--- @field notify fun(method: string, params: any): boolean
+---
+--- Indicates if the RPC is closing.
+--- @field is_closing fun(): boolean
+---
+--- Terminates the RPC client.
+--- @field terminate fun()
 
 ---@param client vim.lsp.rpc.Client
 ---@return vim.lsp.rpc.PublicClient
@@ -551,20 +535,20 @@ local function public_client(client)
 
   ---@private
   function result.is_closing()
-    return client.transport.is_closing()
+    return client.transport:is_closing()
   end
 
   ---@private
   function result.terminate()
-    client.transport.terminate()
+    client.transport:terminate()
   end
 
   --- Sends a request to the LSP server and runs {callback} upon response.
   ---
   ---@param method (string) The invoked LSP method
   ---@param params (table?) Parameters for the invoked LSP method
-  ---@param callback fun(err: lsp.ResponseError|nil, result: any) Callback to invoke
-  ---@param notify_reply_callback fun(message_id: integer)|nil Callback to invoke as soon as a request is no longer pending
+  ---@param callback fun(err: lsp.ResponseError?, result: any) Callback to invoke
+  ---@param notify_reply_callback? fun(message_id: integer) Callback to invoke as soon as a request is no longer pending
   ---@return boolean success `true` if request could be sent, `false` if not
   ---@return integer? message_id if request could be sent, `nil` if not
   function result.request(method, params, callback, notify_reply_callback)
@@ -610,6 +594,21 @@ local function merge_dispatchers(dispatchers)
   return merged
 end
 
+--- @param client vim.lsp.rpc.Client
+--- @param on_exit? fun()
+local function create_client_read_loop(client, on_exit)
+  --- @param body string
+  local function handle_body(body)
+    client:handle_body(body)
+  end
+
+  local function on_error(err)
+    client:on_error(M.client_errors.READ_ERROR, err)
+  end
+
+  return M.create_read_loop(handle_body, on_exit, on_error)
+end
+
 --- Create a LSP RPC client factory that connects to either:
 ---
 ---  - a named pipe (windows)
@@ -623,77 +622,20 @@ end
 ---@param port integer? TCP port to connect to. If absent the first argument must be a pipe
 ---@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
 function M.connect(host_or_path, port)
+  validate('host_or_path', host_or_path, 'string')
+  validate('port', port, 'number', true)
+
   return function(dispatchers)
+    validate('dispatchers', dispatchers, 'table', true)
+
     dispatchers = merge_dispatchers(dispatchers)
-    local handle = (
-      port == nil
-        and assert(
-          uv.new_pipe(false),
-          string.format('Pipe with name %s could not be opened.', host_or_path)
-        )
-      or assert(uv.new_tcp(), 'Could not create new TCP socket')
-    )
-    local closing = false
-    -- Connect returns a PublicClient synchronously so the caller
-    -- can immediately send messages before the connection is established
-    -- -> Need to buffer them until that happens
-    local connected = false
-    -- size should be enough because the client can't really do anything until initialization is done
-    -- which required a response from the server - implying the connection got established
-    local msgbuf = vim.ringbuf(10)
-    local transport = {
-      write = function(msg)
-        if connected then
-          local _, err = handle:write(msg)
-          if err and not closing then
-            log.error('Error on handle:write: %q', err)
-          end
-        else
-          msgbuf:push(msg)
-        end
-      end,
-      is_closing = function()
-        return closing
-      end,
-      terminate = function()
-        if not closing then
-          closing = true
-          handle:shutdown()
-          handle:close()
-          dispatchers.on_exit(0, 0)
-        end
-      end,
-    }
+
+    local transport = lsp_transport.TransportConnect.new()
     local client = new_client(dispatchers, transport)
-    local function on_connect(err)
-      if err then
-        local address = port == nil and host_or_path or (host_or_path .. ':' .. port)
-        vim.schedule(function()
-          vim.notify(
-            string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)),
-            vim.log.levels.WARN
-          )
-        end)
-        return
-      end
-      local handle_body = function(body)
-        client:handle_body(body)
-      end
-      handle:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err)
-        client:on_error(M.client_errors.READ_ERROR, read_err)
-      end))
-      connected = true
-      for msg in msgbuf do
-        handle:write(msg)
-      end
-    end
-    if port == nil then
-      handle:connect(host_or_path, on_connect)
-    else
-      local info = uv.getaddrinfo(host_or_path, nil)
-      local resolved_host = info and info[1] and info[1].addr or host_or_path
-      handle:connect(resolved_host, port, on_connect)
-    end
+    local on_read = create_client_read_loop(client, function()
+      transport:terminate()
+    end)
+    transport:connect(host_or_path, port, on_read, dispatchers.on_exit)
 
     return public_client(client)
   end
@@ -713,83 +655,19 @@ end
 --- @param cmd string[] Command to start the LSP server.
 --- @param dispatchers? vim.lsp.rpc.Dispatchers
 --- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams
---- @return vim.lsp.rpc.PublicClient : Client RPC object, with these methods:
----   - `notify()` |vim.lsp.rpc.notify()|
----   - `request()` |vim.lsp.rpc.request()|
----   - `is_closing()` returns a boolean indicating if the RPC is closing.
----   - `terminate()` terminates the RPC client.
+--- @return vim.lsp.rpc.PublicClient
 function M.start(cmd, dispatchers, extra_spawn_params)
   log.info('Starting RPC client', { cmd = cmd, extra = extra_spawn_params })
 
   validate('cmd', cmd, 'table')
   validate('dispatchers', dispatchers, 'table', true)
 
-  extra_spawn_params = extra_spawn_params or {}
-
-  if extra_spawn_params.cwd then
-    assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
-  end
-
   dispatchers = merge_dispatchers(dispatchers)
 
-  local sysobj ---@type vim.SystemObj
-
-  local client = new_client(dispatchers, {
-    write = function(msg)
-      sysobj:write(msg)
-    end,
-    is_closing = function()
-      return sysobj == nil or sysobj:is_closing()
-    end,
-    terminate = function()
-      sysobj:kill(15)
-    end,
-  })
-
-  local handle_body = function(body)
-    client:handle_body(body)
-  end
-
-  local stdout_handler = M.create_read_loop(handle_body, nil, function(err)
-    client:on_error(M.client_errors.READ_ERROR, err)
-  end)
-
-  local stderr_handler = function(_, chunk)
-    if chunk then
-      log.error('rpc', cmd[1], 'stderr', chunk)
-    end
-  end
-
-  local detached = not is_win
-  if extra_spawn_params.detached ~= nil then
-    detached = extra_spawn_params.detached
-  end
-
-  local ok, sysobj_or_err = pcall(vim.system, cmd, {
-    stdin = true,
-    stdout = stdout_handler,
-    stderr = stderr_handler,
-    cwd = extra_spawn_params.cwd,
-    env = extra_spawn_params.env,
-    detach = detached,
-  }, function(obj)
-    dispatchers.on_exit(obj.code, obj.signal)
-  end)
-
-  if not ok then
-    local err = sysobj_or_err --[[@as string]]
-    local sfx --- @type string
-    if string.match(err, 'ENOENT') then
-      sfx = '. The language server is either not installed, missing from PATH, or not executable.'
-    else
-      sfx = string.format(' with error message: %s', err)
-    end
-    local msg =
-      string.format('Spawning language server with cmd: `%s` failed%s', vim.inspect(cmd), sfx)
-    error(msg)
-  end
-
-  sysobj = sysobj_or_err --[[@as vim.SystemObj]]
+  local transport = lsp_transport.TransportRun.new()
+  local client = new_client(dispatchers, transport)
+  local on_read = create_client_read_loop(client)
+  transport:run(cmd, extra_spawn_params, on_read, dispatchers.on_exit)
 
   return public_client(client)
 end
-- 
cgit 


From de794f2d24099b73419ce2cd98424f702908040f Mon Sep 17 00:00:00 2001
From: Jeremy Fleischman 
Date: Thu, 12 Dec 2024 02:41:57 -0800
Subject: fix(diagnostic): broken variable reference #31557

---
 runtime/lua/vim/diagnostic.lua | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 62997924d9..ded7a8f89d 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -854,10 +854,11 @@ local function set_list(loclist, opts)
   -- numbers beyond the end of the buffer
   local diagnostics = get_diagnostics(bufnr, opts --[[@as vim.diagnostic.GetOpts]], false)
   local items = M.toqflist(diagnostics)
+  local qf_id = nil
   if loclist then
     vim.fn.setloclist(winnr, {}, 'u', { title = title, items = items })
   else
-    local qf_id = get_qf_id_for_title(title)
+    qf_id = get_qf_id_for_title(title)
 
     -- If we already have a diagnostics quickfix, update it rather than creating a new one.
     -- This avoids polluting the finite set of quickfix lists, and preserves the currently selected
@@ -868,16 +869,17 @@ local function set_list(loclist, opts)
       id = qf_id,
     })
   end
+
   if open then
-    if loclist then
-      api.nvim_command('lwindow')
-    else
+    if qf_id then
       -- First navigate to the diagnostics quickfix list.
-      local nr = vim.fn.getqflist({ id = _qf_id, nr = 0 }).nr
+      local nr = vim.fn.getqflist({ id = qf_id, nr = 0 }).nr
       api.nvim_command(nr .. 'chistory')
 
       -- Now open the quickfix list.
       api.nvim_command('botright cwindow')
+    else
+      api.nvim_command('lwindow')
     end
   end
 end
-- 
cgit 


From 2e73ba102ac65f08fdbb6d2b87e90390abdca10c Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Thu, 12 Dec 2024 22:16:05 +0100
Subject: vim-patch:9.1.0919: filetype: some assembler files are not recognized

Problem:  filetype: some assembler are files not recognized
Solution: detect '*.nasm' files as nasm filetype and '*.masm' as masm
          filetype (Wu, Zhenyu)

closes: vim/vim#16194

https://github.com/vim/vim/commit/d66d68763d0947c292a9fdda4da6fda3650fa563

Co-authored-by: Wu, Zhenyu 
---
 runtime/lua/vim/filetype.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index b983e7d1c6..8a51d2a9d3 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -743,6 +743,7 @@ local extension = {
   mkd = detect.markdown,
   markdown = detect.markdown,
   mdown = detect.markdown,
+  masm = 'masm',
   mhtml = 'mason',
   mason = 'mason',
   master = 'master',
@@ -805,6 +806,7 @@ local extension = {
   n1ql = 'n1ql',
   nql = 'n1ql',
   nanorc = 'nanorc',
+  nasm = 'nasm',
   NSA = 'natural',
   NSC = 'natural',
   NSG = 'natural',
-- 
cgit 


From 9c20342297391c4076809964e799f2c7705b819b Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 13 Dec 2024 10:51:33 +0000
Subject: fix(lsp): reuse client if configs match and no root dir

Problem:
An LSP configuration that creates client with no root_dir or
workspace_folders can result in vim.lsp.enable attaching to it multiple
times.

Solution:
When checking existing clients, reuse a client if it wasn't initially
configured have any workspace_folders. This more closely matches the
behaviour we had prior to d9235ef
---
 runtime/lua/vim/lsp.lua | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 596e1b609b..6a8c3d1ff3 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -201,23 +201,28 @@ local function reuse_client_default(client, config)
   end
 
   local config_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir)
-    or {}
-  local config_folders_included = 0
 
-  if not next(config_folders) then
-    return false
+  if not config_folders or not next(config_folders) then
+    -- Reuse if the client was configured with no workspace folders
+    local client_config_folders =
+      lsp._get_workspace_folders(client.config.workspace_folders or client.config.root_dir)
+    return not client_config_folders or not next(client_config_folders)
   end
 
   for _, config_folder in ipairs(config_folders) do
+    local found = false
     for _, client_folder in ipairs(client.workspace_folders) do
       if config_folder.uri == client_folder.uri then
-        config_folders_included = config_folders_included + 1
+        found = true
         break
       end
     end
+    if not found then
+      return false
+    end
   end
 
-  return config_folders_included == #config_folders
+  return true
 end
 
 --- Reset defaults set by `set_defaults`.
-- 
cgit 


From f9dd6826210335d8b37455002d767d1b37c09ce4 Mon Sep 17 00:00:00 2001
From: Colin Kennedy 
Date: Wed, 11 Dec 2024 01:01:14 -0800
Subject: docs(annotations): added `---@generic` support

---
 runtime/lua/vim/_meta/vimfn.lua | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index 77c78f03d4..cf1beda15f 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -1147,8 +1147,9 @@ function vim.fn.confirm(msg, choices, default, type) end
 --- A |Dictionary| is copied in a similar way as a |List|.
 --- Also see |deepcopy()|.
 ---
---- @param expr any
---- @return any
+--- @generic T
+--- @param expr T
+--- @return T
 function vim.fn.copy(expr) end
 
 --- Return the cosine of {expr}, measured in radians, as a |Float|.
@@ -1308,9 +1309,10 @@ function vim.fn.debugbreak(pid) end
 --- {noref} set to 1 will fail.
 --- Also see |copy()|.
 ---
---- @param expr any
+--- @generic T
+--- @param expr T
 --- @param noref? boolean
---- @return any
+--- @return T
 function vim.fn.deepcopy(expr, noref) end
 
 --- Without {flags} or with {flags} empty: Deletes the file by the
@@ -4769,7 +4771,7 @@ function vim.fn.isnan(expr) end
 --- cases, items() returns a List with the index and the value at
 --- the index.
 ---
---- @param dict any
+--- @param dict table
 --- @return any
 function vim.fn.items(dict) end
 
@@ -5952,7 +5954,7 @@ function vim.fn.matchstrpos(expr, pat, start, count) end
 --- an error.  An empty |List| or |Dictionary| results in zero.
 ---
 --- @param expr any
---- @return any
+--- @return number
 function vim.fn.max(expr) end
 
 --- Returns a |List| of |Dictionaries| describing |menus| (defined
@@ -7016,10 +7018,11 @@ function vim.fn.readfile(fname, type, max) end
 ---   echo reduce('xyz', { acc, val -> acc .. ',' .. val })
 --- <
 ---
+--- @generic T
 --- @param object any
---- @param func function
+--- @param func fun(accumulator: T, current: any): any
 --- @param initial? any
---- @return any
+--- @return T
 function vim.fn.reduce(object, func, initial) end
 
 --- Returns the single letter name of the register being executed.
@@ -7215,8 +7218,9 @@ function vim.fn.resolve(filename) end
 ---   let revlist = reverse(copy(mylist))
 --- <
 ---
---- @param object any
---- @return any
+--- @generic T
+--- @param object T[]
+--- @return T[]
 function vim.fn.reverse(object) end
 
 --- Round off {expr} to the nearest integral value and return it
@@ -9079,10 +9083,11 @@ function vim.fn.sockconnect(mode, address, opts) end
 ---   eval mylist->sort({i1, i2 -> i1 - i2})
 --- <
 ---
---- @param list any
+--- @generic T
+--- @param list T[]
 --- @param how? string|function
 --- @param dict? any
---- @return any
+--- @return T[]
 function vim.fn.sort(list, how, dict) end
 
 --- Return the sound-folded equivalent of {word}.  Uses the first
-- 
cgit 


From caa93b5e1e621e63777def5d9fdd49d613e1c274 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 15 Dec 2024 07:05:12 +0800
Subject: vim-patch:ed89206: runtime(doc): add a note about inclusive motions
 and exclusive selection

related: vim/vim#16202

https://github.com/vim/vim/commit/ed89206efe404a94e8424ccfe03c978fd93470f1

Co-authored-by: Christian Brabandt 
---
 runtime/lua/vim/_meta/options.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index fecbece655..63bf0df5f6 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -5214,6 +5214,8 @@ vim.go.sect = vim.go.sections
 --- selection.
 --- When "old" is used and 'virtualedit' allows the cursor to move past
 --- the end of line the line break still isn't included.
+--- When "exclusive" is used, cursor position in visual mode will be
+--- adjusted for inclusive motions `inclusive-motion-selection-exclusive`.
 --- Note that when "exclusive" is used and selecting from the end
 --- backwards, you cannot include the last character of a line, when
 --- starting in Normal mode and 'virtualedit' empty.
-- 
cgit 


From 6c2c77b128e74c69be0d4fd79637c742f7eaba34 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sun, 15 Dec 2024 12:04:33 +0100
Subject: vim-patch:9.1.0926: filetype: Pixi lock files are not recognized

Problem:  filetype: Pixi lock files are not recognized
Solution: detect "pixi.lock" file as yaml filetype
          (Brandon Maier)

Reference:
https://pixi.sh/latest/features/lockfile/

closes: vim/vim#16212

https://github.com/vim/vim/commit/7d1bb90dcf711c732a49e0a45e56028a4853a17d

Co-authored-by: Brandon Maier 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 8a51d2a9d3..eb68e24acf 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1874,6 +1874,7 @@ local filename = {
   ['.clangd'] = 'yaml',
   ['.clang-format'] = 'yaml',
   ['.clang-tidy'] = 'yaml',
+  ['pixi.lock'] = 'yaml',
   ['yarn.lock'] = 'yaml',
   matplotlibrc = 'yaml',
   ['.condarc'] = 'yaml',
-- 
cgit 


From cc38630d39d2aa99183756d55849117daf6dfbfa Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sun, 15 Dec 2024 19:27:12 +0100
Subject: vim-patch:9.1.0929: filetype: lalrpop files are not recognized

Problem:  filetype: lalrpop files are not recognized
Solution: detect '*.lalrpop' files as lalrpop filetype
          (David Thievon)

References:
https://github.com/lalrpop/lalrpop

closes: vim/vim#16223

https://github.com/vim/vim/commit/5a2e0cf5f11c611c9b01f1bd6e7294edf0dd2bf4

Co-authored-by: David Thievon 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index eb68e24acf..83e82392de 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -672,6 +672,7 @@ local extension = {
   k = 'kwt',
   ACE = 'lace',
   ace = 'lace',
+  lalrpop = 'lalrpop',
   latte = 'latte',
   lte = 'latte',
   ld = 'ld',
-- 
cgit 


From 6c975515c5608b500ac96fae598f0b5a48e03ddb Mon Sep 17 00:00:00 2001
From: Juan Cruz De La Torre 
Date: Mon, 16 Dec 2024 11:38:57 -0300
Subject: fix(diagnostic): vim.diagnostic.setqflist() opens loclist on first
 call #31585

Problem:
Regression from de794f2d2409: `vim.diagnostic.setqflist{open=true}` attempts to
open the location list instead of the diagnostics quickfix list if it didn't
exist before. This is because we are using `qf_id` to decide which to open, but
`qf_id=nil` when there is no existing diagnostics quickfix list with a given
title ("Diagnostics" by default).

Solution:
- Revert to using `loclist` to decide which to open.
- Add tests.
---
 runtime/lua/vim/diagnostic.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index ded7a8f89d..39ef18e0b4 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -871,7 +871,7 @@ local function set_list(loclist, opts)
   end
 
   if open then
-    if qf_id then
+    if not loclist then
       -- First navigate to the diagnostics quickfix list.
       local nr = vim.fn.getqflist({ id = qf_id, nr = 0 }).nr
       api.nvim_command(nr .. 'chistory')
-- 
cgit 


From 022449b5223659d515b78bada7de2fac8718820a Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Mon, 16 Dec 2024 08:34:16 -0800
Subject: fix(api): generic error messages, not using TRY_WRAP #31596

Problem:
- API functions using `try_start` directly, do not surface the
  underlying error message, and instead show generic messages.
- Error-handling code is duplicated in the API impl.
- Failure modes are not tested.

Solution:
- Use `TRY_WRAP`.
- Add tests.
---
 runtime/lua/vim/_meta/api.lua | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index b2385197bd..55274963ed 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -885,10 +885,8 @@ function vim.api.nvim_cmd(cmd, opts) end
 ---
 --- On execution error: fails with Vimscript error, updates v:errmsg.
 ---
---- Prefer using `nvim_cmd()` or `nvim_exec2()` over this. To evaluate multiple lines of Vim script
---- or an Ex command directly, use `nvim_exec2()`. To construct an Ex command using a structured
---- format and then execute it, use `nvim_cmd()`. To modify an Ex command before evaluating it, use
---- `nvim_parse_cmd()` in conjunction with `nvim_cmd()`.
+--- Prefer `nvim_cmd()` or `nvim_exec2()` instead. To modify an Ex command in a structured way
+--- before executing it, modify the result of `nvim_parse_cmd()` then pass it to `nvim_cmd()`.
 ---
 --- @param command string Ex command string
 function vim.api.nvim_command(command) end
-- 
cgit 


From 0dd933265ff2e64786fd30f949e767e10f401519 Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Tue, 17 Dec 2024 07:11:41 -0600
Subject: feat(terminal)!: cursor shape and blink (#31562)

When a terminal application running inside the terminal emulator sets
the cursor shape or blink status of the cursor, update the cursor in the
parent terminal to match.

This removes the "virtual cursor" that has been in use by the terminal
emulator since the beginning. The original rationale for using the
virtual cursor was to avoid having to support additional UI methods to
change the cursor color for other (non-TUI) UIs, instead relying on the
TermCursor and TermCursorNC highlight groups.

The TermCursor highlight group is now used in the default 'guicursor'
value, which has a new entry for Terminal mode. However, the
TermCursorNC highlight group is no longer supported: since terminal
windows now use the real cursor, when the window is not focused there is
no cursor displayed in the window at all, so there is nothing to
highlight. Users can still use the StatusLineTermNC highlight group to
differentiate non-focused terminal windows.

BREAKING CHANGE: The TermCursorNC highlight group is no longer supported.
---
 runtime/lua/vim/_meta/options.lua | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 63bf0df5f6..e5cea884c5 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -2783,6 +2783,7 @@ vim.go.gp = vim.go.grepprg
 --- 	ci	Command-line Insert mode
 --- 	cr	Command-line Replace mode
 --- 	sm	showmatch in Insert mode
+--- 	t	Terminal mode
 --- 	a	all modes
 --- The argument-list is a dash separated list of these arguments:
 --- 	hor{N}	horizontal bar, {N} percent of the character height
@@ -2802,7 +2803,8 @@ vim.go.gp = vim.go.grepprg
 --- ```vim
 --- 			set guicursor=n:blinkon0
 --- ```
---- - Default is "blinkon0" for each mode.
+---
+--- 		Default is "blinkon0" for each mode.
 --- 	{group-name}
 --- 		Highlight group that decides the color and font of the
 --- 		cursor.
@@ -2848,7 +2850,7 @@ vim.go.gp = vim.go.grepprg
 ---
 ---
 --- @type string
-vim.o.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20"
+vim.o.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20,t:block-blinkon500-blinkoff500-TermCursor"
 vim.o.gcr = vim.o.guicursor
 vim.go.guicursor = vim.o.guicursor
 vim.go.gcr = vim.go.guicursor
-- 
cgit 


From 07d5dc8938a7f5d8cf2b702ef4f26af926b6ac45 Mon Sep 17 00:00:00 2001
From: Peter Lithammer 
Date: Wed, 18 Dec 2024 15:31:25 +0100
Subject: feat(lsp): show server version in `:checkhealth` #31611

Problem:
Language server version information missing from `:checkhealth vim.lsp`.

Solution:
Store `InitializeResult.serverInfo.version` from the `initialize`
response and display for each client in `:checkhealth vim.lsp`.
---
 runtime/lua/vim/lsp/client.lua | 6 ++++++
 runtime/lua/vim/lsp/health.lua | 3 +++
 2 files changed, 9 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 72043c18dd..d51a45b473 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -174,6 +174,10 @@ local validate = vim.validate
 --- capabilities.
 --- @field server_capabilities lsp.ServerCapabilities?
 ---
+--- Response from the server sent on `initialize` describing information about
+--- the server.
+--- @field server_info lsp.ServerInfo?
+---
 --- A ring buffer (|vim.ringbuf()|) containing progress messages
 --- sent by the server.
 --- @field progress vim.lsp.Client.Progress
@@ -556,6 +560,8 @@ function Client:initialize()
       self.offset_encoding = self.server_capabilities.positionEncoding
     end
 
+    self.server_info = result.serverInfo
+
     if next(self.settings) then
       self:notify(ms.workspace_didChangeConfiguration, { settings = self.settings })
     end
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index d2cf888d89..d94bc70a65 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -40,6 +40,8 @@ local function check_active_clients()
   local clients = vim.lsp.get_clients()
   if next(clients) then
     for _, client in pairs(clients) do
+      local server_version = vim.tbl_get(client, 'server_info', 'version')
+        or '? (no serverInfo.version response)'
       local cmd ---@type string
       local ccmd = client.config.cmd
       if type(ccmd) == 'table' then
@@ -62,6 +64,7 @@ local function check_active_clients()
       end
       report_info(table.concat({
         string.format('%s (id: %d)', client.name, client.id),
+        string.format('- Version: %s', server_version),
         dirs_info,
         string.format('- Command: %s', cmd),
         string.format('- Settings: %s', vim.inspect(client.settings, { newline = '\n  ' })),
-- 
cgit 


From 888a803755c58db56b5b20fcf6b812de877056c9 Mon Sep 17 00:00:00 2001
From: phanium <91544758+phanen@users.noreply.github.com>
Date: Wed, 18 Dec 2024 22:37:12 +0800
Subject: fix(lsp): vim.lsp.start fails if existing client has no
 workspace_folders #31608
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:
regression since https://github.com/neovim/neovim/pull/31340

`nvim -l repro.lua`:
```lua
vim.lsp.start { cmd = { 'lua-language-server' }, name = 'lua_ls' }
vim.lsp.start { cmd = { 'lua-language-server' }, name = 'lua_ls', root_dir = 'foo' }

-- swapped case will be ok:
-- vim.lsp.start { cmd = { 'lua-language-server' }, name = 'lua_ls', root_dir = 'foo' }
-- vim.lsp.start { cmd = { 'lua-language-server' }, name = 'lua_ls' }
```

Failure:
```
E5113: Error while calling lua chunk: /…/lua/vim/lsp.lua:214: bad argument #1 to
'ipairs' (table expected, got nil)
stack traceback:
        [C]: in function 'ipairs'
        /…/lua/vim/lsp.lua:214: in function 'reuse_client'
        /…/lua/vim/lsp.lua:629: in function 'start'
        repro.lua:34: in main chunk
```
---
 runtime/lua/vim/lsp.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 6a8c3d1ff3..5a93da4298 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -211,7 +211,7 @@ local function reuse_client_default(client, config)
 
   for _, config_folder in ipairs(config_folders) do
     local found = false
-    for _, client_folder in ipairs(client.workspace_folders) do
+    for _, client_folder in ipairs(client.workspace_folders or {}) do
       if config_folder.uri == client_folder.uri then
         found = true
         break
-- 
cgit 


From a5a4149e9754a96c063a357c18397aa7906edf53 Mon Sep 17 00:00:00 2001
From: luukvbaal 
Date: Thu, 19 Dec 2024 16:04:33 +0100
Subject: docs(api): specify when decor provider on_buf is called #31634

---
 runtime/lua/vim/_meta/api.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 55274963ed..8d759278d0 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -2134,8 +2134,8 @@ function vim.api.nvim_set_current_win(window) end
 ---   ```
 ---     ["start", tick]
 ---   ```
---- - on_buf: called for each buffer being redrawn (before
----   window callbacks)
+--- - on_buf: called for each buffer being redrawn (once per edit,
+---   before window callbacks)
 ---   ```
 ---     ["buf", bufnr, tick]
 ---   ```
-- 
cgit 


From 8ef41f590224dfeea2e51d9fec150e363fd72ee0 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Thu, 19 Dec 2024 07:07:04 -0800
Subject: feat(jobs): jobstart(…,{term=true}), deprecate termopen() #31343
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:
`termopen` has long been a superficial wrapper around `jobstart`, and
has no real purpose. Also, `vim.system` and `nvim_open_term` presumably
will replace all features of `jobstart` and `termopen`, so centralizing
the logic will help with that.

Solution:
- Introduce `eval/deprecated.c`, where all deprecated eval funcs will live.
- Introduce "term" flag of `jobstart`.
- Deprecate `termopen`.
---
 runtime/lua/vim/_defaults.lua   |  2 +-
 runtime/lua/vim/_meta/api.lua   |  4 ++--
 runtime/lua/vim/_meta/vimfn.lua | 28 ++++++++++++----------------
 3 files changed, 15 insertions(+), 19 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 0b8a54e957..4216a2acb7 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -435,7 +435,7 @@ do
     group = nvim_terminal_augroup,
     desc = 'Treat term:// buffers as terminal buffers',
     nested = true,
-    command = "if !exists('b:term_title')|call termopen(matchstr(expand(\"\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'cwd': expand(get(matchlist(expand(\"\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})",
+    command = "if !exists('b:term_title')|call jobstart(matchstr(expand(\"\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'term': v:true, 'cwd': expand(get(matchlist(expand(\"\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})",
   })
 
   vim.api.nvim_create_autocmd({ 'TermClose' }, {
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 8d759278d0..bea6df43bb 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -272,12 +272,12 @@ function vim.api.nvim_buf_attach(buffer, send_buffer, opts) end
 --- This temporarily switches current buffer to "buffer".
 --- If the current window already shows "buffer", the window is not switched.
 --- If a window inside the current tabpage (including a float) already shows the
---- buffer, then one of these windows will be set as current window temporarily.
+--- buffer, then one of those windows will be set as current window temporarily.
 --- Otherwise a temporary scratch window (called the "autocmd window" for
 --- historical reasons) will be used.
 ---
 --- This is useful e.g. to call Vimscript functions that only work with the
---- current buffer/window currently, like `termopen()`.
+--- current buffer/window currently, like `jobstart(…, {'term': v:true})`.
 ---
 --- @param buffer integer Buffer handle, or 0 for current buffer
 --- @param fun function Function to call inside the buffer (currently Lua callable
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index cf1beda15f..b580357c85 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -4805,7 +4805,7 @@ function vim.fn.jobresize(job, width, height) end
 --- @return any
 function vim.fn.jobsend(...) end
 
---- Note: Prefer |vim.system()| in Lua (unless using the `pty` option).
+--- Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`).
 ---
 --- Spawns {cmd} as a job.
 --- If {cmd} is a List it runs directly (no 'shell').
@@ -4813,8 +4813,11 @@ function vim.fn.jobsend(...) end
 ---   call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
 --- <(See |shell-unquoting| for details.)
 ---
---- Example: >vim
----   call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}})
+--- Example: start a job and handle its output: >vim
+---   call jobstart(['nvim', '-h'], {'on_stdout':{j,d,e->append(line('.'),d)}})
+--- <
+--- Example: start a job in a |terminal| connected to the current buffer: >vim
+---   call jobstart(['nvim', '-h'], {'term':v:true})
 --- <
 --- Returns |job-id| on success, 0 on invalid arguments (or job
 --- table is full), -1 if {cmd}[0] or 'shell' is not executable.
@@ -4879,6 +4882,10 @@ function vim.fn.jobsend(...) end
 ---   stdin:      (string) Either "pipe" (default) to connect the
 ---         job's stdin to a channel or "null" to disconnect
 ---         stdin.
+---   term:      (boolean) Spawns {cmd} in a new pseudo-terminal session
+---           connected to the current (unmodified) buffer. Implies "pty".
+---           Default "height" and "width" are set to the current window
+---           dimensions. |jobstart()|. Defaults $TERM to "xterm-256color".
 ---   width:      (number) Width of the `pty` terminal.
 ---
 --- {opts} is passed as |self| dictionary to the callback; the
@@ -10168,19 +10175,8 @@ function vim.fn.tanh(expr) end
 --- @return string
 function vim.fn.tempname() end
 
---- Spawns {cmd} in a new pseudo-terminal session connected
---- to the current (unmodified) buffer. Parameters and behavior
---- are the same as |jobstart()| except "pty", "width", "height",
---- and "TERM" are ignored: "height" and "width" are taken from
---- the current window. Note that termopen() implies a "pty" arg
---- to jobstart(), and thus has the implications documented at
---- |jobstart()|.
----
---- Returns the same values as jobstart().
----
---- Terminal environment is initialized as in |jobstart-env|,
---- except $TERM is set to "xterm-256color". Full behavior is
---- described in |terminal|.
+--- @deprecated
+--- Use |jobstart()| with `{term: v:true}` instead.
 ---
 --- @param cmd string|string[]
 --- @param opts? table
-- 
cgit 


From 4e130c1ee446f4389a8c76c5e81b53bff8b9193c Mon Sep 17 00:00:00 2001
From: Dan Pascu 
Date: Fri, 20 Dec 2024 11:43:56 +0200
Subject: fix(vim.system): invalid MAX_TIMEOUT for 32-bit systems #31638

The maximum signed value on 32-bit systems is 2 ^ 31 - 1. When using 2 ^ 31 for
the default timeout, the value would overflow on such systems resulting in
a negative value, which caused a stack trace when calling wait() without
a timeout.
---
 runtime/lua/vim/_system.lua | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua
index c0a0570e13..157172447a 100644
--- a/runtime/lua/vim/_system.lua
+++ b/runtime/lua/vim/_system.lua
@@ -79,7 +79,8 @@ function SystemObj:_timeout(signal)
   self:kill(signal or SIG.TERM)
 end
 
-local MAX_TIMEOUT = 2 ^ 31
+-- Use max 32-bit signed int value to avoid overflow on 32-bit systems. #31633
+local MAX_TIMEOUT = 2 ^ 31 - 1
 
 --- @param timeout? integer
 --- @return vim.SystemCompleted
-- 
cgit 


From 0fea57bc7127f4e160c1d66cb3ec910c9ec409a6 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 20 Dec 2024 19:56:22 +0100
Subject: vim-patch:9.1.0950: filetype: fennelrc files are not recognized

Problem:  filetype: fennelrc files are not recognized
Solution: detect 'fennelrc' files as fennel filetype
          (Wu Zhenyu)

References:
https://github.com/bakpakin/Fennel/issues/193

closes: vim/vim#16262

https://github.com/vim/vim/commit/f173f4249fc785fb3e2b341bcfb0f21192cd4bf5

Co-authored-by: Wu, Zhenyu 
---
 runtime/lua/vim/filetype.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 83e82392de..e1751103c4 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1544,6 +1544,8 @@ local filename = {
   ['filter-rules'] = 'elmfilt',
   ['exim.conf'] = 'exim',
   exports = 'exports',
+  fennelrc = 'fennel',
+  ['.fennelrc'] = 'fennel',
   ['.fetchmailrc'] = 'fetchmail',
   fvSchemes = detect.foam,
   fvSolution = detect.foam,
-- 
cgit 


From 130b5fd85f074ac5e40cb4b07c6c9dc6f91512bd Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Sat, 21 Dec 2024 08:27:27 -0600
Subject: feat(lsp): return table from lsp/ files on runtimepath (#31663)

Problem: LSP configs on the runtimepath must have the same name as the
LSP server and must also explicitly set the name in vim.lsp.config. This
is redundant and creates a footgun where a user may accidentally use the
wrong name when assigning to the vim.lsp.config table.

Solution: Return a table from lsp/ runtimepath files instead
---
 runtime/lua/vim/lsp.lua | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 5a93da4298..19d0377585 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -450,16 +450,20 @@ function lsp._resolve_config(name)
   if not econfig.resolved_config then
     -- Resolve configs from lsp/*.lua
     -- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
-    local orig_configs = lsp.config._configs
-    lsp.config._configs = {}
-    pcall(vim.cmd.runtime, { ('lsp/%s.lua'):format(name), bang = true })
-    local rtp_configs = lsp.config._configs
-    lsp.config._configs = orig_configs
+    local rtp_config = {} ---@type vim.lsp.Config
+    for _, v in ipairs(api.nvim_get_runtime_file(('lsp/%s.lua'):format(name), true)) do
+      local config = assert(loadfile(v))() ---@type any?
+      if type(config) == 'table' then
+        rtp_config = vim.tbl_deep_extend('force', rtp_config, config)
+      else
+        log.warn(string.format('%s does not return a table, ignoring', v))
+      end
+    end
 
     local config = vim.tbl_deep_extend(
       'force',
       lsp.config._configs['*'] or {},
-      rtp_configs[name] or {},
+      rtp_config,
       lsp.config._configs[name] or {}
     )
 
-- 
cgit 


From c7a4197a5c344f02241eed0761c86487ee5bbd96 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sun, 22 Dec 2024 11:15:12 +0100
Subject: vim-patch:9.1.0951: filetype: jshell files are not recognized

Problem:  filetype: jshell files are not recognized
Solution: detect '*.jsh' files as java filetype
          (Konfekt)

closes: vim/vim#16260

https://github.com/vim/vim/commit/62e3014ab1146d7f78694c97fc6974f1af2cc5af

Co-authored-by: Konfekt 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index e1751103c4..31de25a714 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -616,6 +616,7 @@ local extension = {
   janet = 'janet',
   jav = 'java',
   java = 'java',
+  jsh = 'java',
   jj = 'javacc',
   jjt = 'javacc',
   es = 'javascript',
-- 
cgit 


From 665a0e85c4788cb2847e270c333c0aee306f07ad Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sun, 22 Dec 2024 15:15:57 +0100
Subject: vim-patch:9.1.0953: filetype: APKBUILD files not correctly detected

Problem:  filetype: APKBUILD files not correctly detected
Solution: detect 'APKBUILD' files as apkbuild filetype,
          include a apkbuild syntax script (which basically
          just sources the sh.vim syntax file)
          (Hugo Osvaldo Barrera)

Vim plugins (e.g.: ALE, nvim-lspconfig, etc) rely on filetype to
determine which integrations/helpers are applicable. They expect
filetype=apkbuild for APKBUILD files.

On the other hand, plugins also enable bash-specific linters and
functionality when filetype=bash, but APKBUILD files are POSIX sh, not
bash, so these often provide bogus results.

Change the filetype for APKBUILD to a 'apkbuild', so that tools and
ftplugin can properly target these files. This filetype will use the
existing `sh` syntax rules, since these are applicable for them.

https://github.com/vim/vim/commit/7cb24917a112ba473cb351bdcdc48b8adbd46793

Co-authored-by: Hugo Osvaldo Barrera' via vim_dev 
---
 runtime/lua/vim/filetype.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 31de25a714..6fa1684704 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1439,6 +1439,7 @@ local filename = {
   ['/etc/asound.conf'] = 'alsaconf',
   ['build.xml'] = 'ant',
   ['.htaccess'] = 'apache',
+  APKBUILD = 'apkbuild',
   ['apt.conf'] = 'aptconf',
   ['/.aptitude/config'] = 'aptconf',
   ['=tagging-method'] = 'arch',
@@ -1798,7 +1799,6 @@ local filename = {
   ['.kshrc'] = detect.ksh,
   ['.profile'] = detect.sh,
   ['/etc/profile'] = detect.sh,
-  APKBUILD = detect.bash,
   PKGBUILD = detect.bash,
   ['.tcshrc'] = detect.tcsh,
   ['tcsh.login'] = detect.tcsh,
-- 
cgit 


From 55c5d0de262b8a9eb03a65f6e6f45e8d26213eb4 Mon Sep 17 00:00:00 2001
From: Lukasz Piepiora 
Date: Mon, 23 Dec 2024 14:08:20 +0100
Subject: docs(api): vim.version.range():has() method #31622

Problem:
The :has() method of the vim.version.range() result is not documented
though it's mentioned in examples.

Solution:
Mention it in the range() result doc.
---
 runtime/lua/vim/version.lua | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua
index d64ef98d2d..06c54ac033 100644
--- a/runtime/lua/vim/version.lua
+++ b/runtime/lua/vim/version.lua
@@ -227,8 +227,7 @@ end
 ---@field to? vim.Version
 local VersionRange = {}
 
---- @private
----
+---@nodoc
 ---@param version string|vim.Version
 function VersionRange:has(version)
   if type(version) == 'string' then
-- 
cgit 


From 4cbeb6fa3cc764fd5605bccf7362f7a249d6df81 Mon Sep 17 00:00:00 2001
From: Yorick Peterse 
Date: Mon, 23 Dec 2024 20:57:15 +0100
Subject: fix(diagnostic): silence :chistory #31701

vim.diagnostic.set_list() uses chistory to restore the actively selected
entry whenever necessary. This however also results in it displaying
some output in the message bar, but this output isn't useful (and can
even be distracting) when opening the quickfix window. This fixes this
by silencing the chistory command.
---
 runtime/lua/vim/diagnostic.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 39ef18e0b4..90f967fe79 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -874,7 +874,7 @@ local function set_list(loclist, opts)
     if not loclist then
       -- First navigate to the diagnostics quickfix list.
       local nr = vim.fn.getqflist({ id = qf_id, nr = 0 }).nr
-      api.nvim_command(nr .. 'chistory')
+      api.nvim_command(('silent %dchistory'):format(nr))
 
       -- Now open the quickfix list.
       api.nvim_command('botright cwindow')
-- 
cgit 


From 14ee1de7e58276be4b80bc262cd0435eb9eeb01c Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Tue, 24 Dec 2024 10:31:35 +0100
Subject: vim-patch:9.1.0958: filetype: supertux2 config files detected as lisp

Problem:  filetype: supertux2 config files detected as lisp
Solution: detect supertux2 config files as scheme instead
          (Wu, Zhenyu)

References:
https://github.com/SuperTux/supertux/wiki/S-Expression

supertux uses #t and #f as bool type, which is same as scheme, not
common lisp

closes: vim/vim#16287

https://github.com/vim/vim/commit/e62d93ead10b4c5818e3c0b7551f1784d24bfe33

Co-authored-by: Wu, Zhenyu 
---
 runtime/lua/vim/filetype.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 6fa1684704..80d25fe515 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -699,7 +699,6 @@ local extension = {
   el = 'lisp',
   lsp = 'lisp',
   asd = 'lisp',
-  stsg = 'lisp',
   lt = 'lite',
   lite = 'lite',
   livemd = 'livebook',
@@ -1072,6 +1071,7 @@ local extension = {
   ss = 'scheme',
   scm = 'scheme',
   sld = 'scheme',
+  stsg = 'scheme',
   sce = 'scilab',
   sci = 'scilab',
   scss = 'scss',
@@ -2159,8 +2159,8 @@ local pattern = {
     ['/gitolite%-admin/conf/'] = starsetf('gitolite'),
     ['/%.i3/config$'] = 'i3config',
     ['/i3/config$'] = 'i3config',
-    ['/supertux2/config$'] = 'lisp',
     ['/%.mplayer/config$'] = 'mplayerconf',
+    ['/supertux2/config$'] = 'scheme',
     ['/neofetch/config%.conf$'] = 'sh',
     ['/%.ssh/config$'] = 'sshconfig',
     ['/%.sway/config$'] = 'swayconfig',
-- 
cgit 


From b51110f4a11af114401e626cd4c1f1aec23e81c5 Mon Sep 17 00:00:00 2001
From: Shihua Zeng <76579810+Bekaboo@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:56:10 -0500
Subject: docs(api): return type of nvim_get_keymap() #31708

---
 runtime/lua/vim/_meta/api.lua               |  4 ++--
 runtime/lua/vim/_meta/api_keysets_extra.lua | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index bea6df43bb..7297c8ad38 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -452,7 +452,7 @@ function vim.api.nvim_buf_get_extmarks(buffer, ns_id, start, end_, opts) end
 ---
 --- @param buffer integer Buffer handle, or 0 for current buffer
 --- @param mode string Mode short-name ("n", "i", "v", ...)
---- @return vim.api.keyset.keymap[] # Array of |maparg()|-like dictionaries describing mappings.
+--- @return vim.api.keyset.get_keymap[] # Array of |maparg()|-like dictionaries describing mappings.
 --- The "buffer" key holds the associated buffer handle.
 function vim.api.nvim_buf_get_keymap(buffer, mode) end
 
@@ -1414,7 +1414,7 @@ function vim.api.nvim_get_hl_ns(opts) end
 --- Gets a list of global (non-buffer-local) `mapping` definitions.
 ---
 --- @param mode string Mode short-name ("n", "i", "v", ...)
---- @return vim.api.keyset.keymap[] # Array of |maparg()|-like dictionaries describing mappings.
+--- @return vim.api.keyset.get_keymap[] # Array of |maparg()|-like dictionaries describing mappings.
 --- The "buffer" key is always zero.
 function vim.api.nvim_get_keymap(mode) end
 
diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua
index 806b3e49c0..fbef6fa3bc 100644
--- a/runtime/lua/vim/_meta/api_keysets_extra.lua
+++ b/runtime/lua/vim/_meta/api_keysets_extra.lua
@@ -173,6 +173,26 @@ error('Cannot require a meta file')
 --- @field force? true
 --- @field cterm? vim.api.keyset.hl_info.cterm
 
+--- @class vim.api.keyset.get_keymap
+--- @field abbr? 0|1
+--- @field buffer? 0|1
+--- @field callback? function
+--- @field desc? string
+--- @field expr? 0|1
+--- @field lhs? string
+--- @field lhsraw? string
+--- @field lhsrawalt? string
+--- @field lnum? integer
+--- @field mode? string
+--- @field mode_bits? integer
+--- @field noremap? 0|1
+--- @field nowait? 0|1
+--- @field rhs? string
+--- @field script? 0|1
+--- @field scriptversion? integer
+--- @field sid? integer
+--- @field silent? 0|1
+
 --- @class vim.api.keyset.get_mode
 --- @field blocking boolean
 --- @field mode string
-- 
cgit 


From 01e1598072249011ac2cec7318559cea6179e509 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Wed, 25 Dec 2024 11:41:36 +0100
Subject: vim-patch:9.1.0960: filetype: hy history files are not recognized

Problem:  filetype: hy history files are not recognized
Solution: detect '*.hy', '.hy-history' files as hy filetype,
          detect '.lips_repl_history' files are scheme filetype
          (Wu, Zhenyu)

closes: vim/vim#16298

https://github.com/vim/vim/commit/a32daed55933df49a7aed571cc6e400ae01c7976

Co-authored-by: Wu, Zhenyu 
---
 runtime/lua/vim/filetype.lua | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 80d25fe515..2b5a201c0f 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -592,6 +592,7 @@ local extension = {
   hw = detect.hw,
   module = detect.hw,
   pkg = detect.hw,
+  hy = 'hy',
   iba = 'ibasic',
   ibi = 'ibasic',
   icn = 'icon',
@@ -1603,6 +1604,7 @@ local filename = {
   ['/etc/host.conf'] = 'hostconf',
   ['/etc/hosts.allow'] = 'hostsaccess',
   ['/etc/hosts.deny'] = 'hostsaccess',
+  ['.hy-history'] = 'hy',
   ['hyprland.conf'] = 'hyprlang',
   ['hyprpaper.conf'] = 'hyprlang',
   ['hypridle.conf'] = 'hyprlang',
@@ -1778,6 +1780,7 @@ local filename = {
   ['Rantfile'] = 'ruby',
   Vagrantfile = 'ruby',
   ['smb.conf'] = 'samba',
+  ['.lips_repl_history'] = 'scheme',
   screenrc = 'screen',
   ['.screenrc'] = 'screen',
   ['/etc/sensors3.conf'] = 'sensors',
-- 
cgit 


From 7567f7d3226ab247eb2b743460492c1101045b0e Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Wed, 25 Dec 2024 11:44:46 +0100
Subject: vim-patch:9.1.0961: filetype: TI gel files are not recognized

Problem:  filetype: TI gel files are not recognized
Solution: detect '*.gel' files as gel filetype, include
          get filetype and syntax plugins
          (Wu, Zhenyu)

References:
https://downloads.ti.com/ccs/esd/documents/users_guide/ccs_debug-gel.html

closes: vim/vim#16226

https://github.com/vim/vim/commit/9360de9027aa286e802363ede59c9e97025ae123

Co-authored-by: Wu, Zhenyu 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 2b5a201c0f..ad74a16e09 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -504,6 +504,7 @@ local extension = {
   gdshader = 'gdshader',
   shader = 'gdshader',
   ged = 'gedcom',
+  gel = 'gel',
   gmi = 'gemtext',
   gemini = 'gemtext',
   gift = 'gift',
-- 
cgit 


From 69fbb58385d14a623be6652fcd2d4c3ff65de1d3 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Thu, 26 Dec 2024 18:56:31 +0100
Subject: vim-patch:9.1.0962: filetype: bun.lock file is not recognized

Problem:  filetype: bun.lock file is not recognized
Solution: detect 'bun.lock' file as jsonc filetype
          (Anton Kastritskii)

closes: vim/vim#16308

https://github.com/vim/vim/commit/f07ae5b3bdb7331ee0e65adcb74402eef74f0a2b

Co-authored-by: Anton Kastritskii 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index ad74a16e09..d6a29ed84d 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1636,6 +1636,7 @@ local filename = {
   ['.luaurc'] = 'jsonc',
   ['.swrc'] = 'jsonc',
   ['.vsconfig'] = 'jsonc',
+  ['bun.lock'] = 'jsonc',
   ['.justfile'] = 'just',
   ['justfile'] = 'just',
   ['Justfile'] = 'just',
-- 
cgit 


From 35247b00a44e838ed7d657a9b94964dc0664d28d Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Fri, 27 Dec 2024 10:09:22 -0600
Subject: feat(lsp): support function for client root_dir (#31630)

If root_dir is a function it is evaluated when the client is created to
determine the root directory.

This enables dynamically determining the root directory based on e.g.
project or directory structure (example: finding a parent Cargo.toml
file that contains "[workspace]" in a Rust project).
---
 runtime/lua/vim/lsp.lua | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 19d0377585..1c8356d64d 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -334,6 +334,11 @@ end
 --- rootUri, and rootPath on initialization. Unused if `root_dir` is provided.
 --- @field root_markers? string[]
 ---
+--- Directory where the LSP server will base its workspaceFolders, rootUri, and rootPath on
+--- initialization. If a function, it accepts a single callback argument which must be called with
+--- the value of root_dir to use. The LSP server will not be started until the callback is called.
+--- @field root_dir? string|fun(cb:fun(string))
+---
 --- Predicate used to decide if a client should be re-used. Used on all
 --- running clients. The default implementation re-uses a client if name and
 --- root_dir matches.
@@ -499,6 +504,15 @@ local function lsp_enable_callback(bufnr)
     return true
   end
 
+  --- @param config vim.lsp.Config
+  local function start(config)
+    return vim.lsp.start(config, {
+      bufnr = bufnr,
+      reuse_client = config.reuse_client,
+      _root_markers = config.root_markers,
+    })
+  end
+
   for name in vim.spairs(lsp._enabled_configs) do
     local config = lsp._resolve_config(name)
 
@@ -507,11 +521,14 @@ local function lsp_enable_callback(bufnr)
       -- do not propagate back to the enabled configs.
       config = vim.deepcopy(config)
 
-      vim.lsp.start(config, {
-        bufnr = bufnr,
-        reuse_client = config.reuse_client,
-        _root_markers = config.root_markers,
-      })
+      if type(config.root_dir) == 'function' then
+        config.root_dir(function(root_dir)
+          config.root_dir = root_dir
+          start(config)
+        end)
+      else
+        start(config)
+      end
     end
   end
 end
-- 
cgit 


From 518070731003e30ea7eee96e76ccdf7b950c90da Mon Sep 17 00:00:00 2001
From: Famiu Haque 
Date: Fri, 5 Apr 2024 14:48:13 +0600
Subject: feat(lua): add `vim.fs.abspath`

Problem: There is currently no way to check if a given path is absolute or convert a relative path to an absolute path through the Lua stdlib. `vim.fs.joinpath` does not work when the path is absolute. There is also currently no way to resolve `C:foo\bar` style paths in Windows.

Solution: Add `vim.fs.abspath`, which allows converting any path to an absolute path. This also allows checking if current path is absolute by doing `vim.fs.abspath(path) == path`. It also has support for `C:foo\bar` style paths in Windows.
---
 runtime/lua/vim/fs.lua | 69 ++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 61 insertions(+), 8 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index 2f007d97c3..f2cd210cac 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -505,6 +505,27 @@ local function path_resolve_dot(path)
   return (is_path_absolute and '/' or '') .. table.concat(new_path_components, '/')
 end
 
+--- Expand tilde (~) character at the beginning of the path to the user's home directory.
+---
+--- @param path string Path to expand.
+--- @param sep string|nil Path separator to use. Uses os_sep by default.
+--- @return string Expanded path.
+local function expand_home(path, sep)
+  sep = sep or os_sep
+
+  if vim.startswith(path, '~') then
+    local home = uv.os_homedir() or '~' --- @type string
+
+    if home:sub(-1) == sep then
+      home = home:sub(1, -2)
+    end
+
+    path = home .. path:sub(2)
+  end
+
+  return path
+end
+
 --- @class vim.fs.normalize.Opts
 --- @inlinedoc
 ---
@@ -568,14 +589,8 @@ function M.normalize(path, opts)
     return ''
   end
 
-  -- Expand ~ to users home directory
-  if vim.startswith(path, '~') then
-    local home = uv.os_homedir() or '~'
-    if home:sub(-1) == os_sep_local then
-      home = home:sub(1, -2)
-    end
-    path = home .. path:sub(2)
-  end
+  -- Expand ~ to user's home directory
+  path = expand_home(path, os_sep_local)
 
   -- Expand environment variables if `opts.expand_env` isn't `false`
   if opts.expand_env == nil or opts.expand_env then
@@ -679,4 +694,42 @@ function M.rm(path, opts)
   end
 end
 
+--- Convert path to an absolute path. A tilde (~) character at the beginning of the path is expanded
+--- to the user's home directory. Does not check if the path exists, normalize the path, resolve
+--- symlinks or hardlinks (including `.` and `..`), or expand environment variables. If the path is
+--- already absolute, it is returned unchanged. Also converts `\` path separators to `/`.
+---
+--- @param path string Path
+--- @return string Absolute path
+function M.abspath(path)
+  vim.validate('path', path, 'string')
+
+  -- Expand ~ to user's home directory
+  path = expand_home(path)
+
+  -- Convert path separator to `/`
+  path = path:gsub(os_sep, '/')
+
+  local prefix = ''
+
+  if iswin then
+    prefix, path = split_windows_path(path)
+  end
+
+  if vim.startswith(path, '/') then
+    -- Path is already absolute, do nothing
+    return prefix .. path
+  end
+
+  -- Windows allows paths like C:foo/bar, these paths are relative to the current working directory
+  -- of the drive specified in the path
+  local cwd = (iswin and prefix:match('^%w:$')) and uv.fs_realpath(prefix) or uv.cwd()
+  assert(cwd ~= nil)
+  -- Convert cwd path separator to `/`
+  cwd = cwd:gsub(os_sep, '/')
+
+  -- Prefix is not needed for expanding relative paths, as `cwd` already contains it.
+  return M.joinpath(cwd, path)
+end
+
 return M
-- 
cgit 


From 2b07b14eacf3197754c63f42f9c880e85960eef2 Mon Sep 17 00:00:00 2001
From: Luca Saccarola <96259932+saccarosium@users.noreply.github.com>
Date: Sat, 28 Dec 2024 12:20:50 +0100
Subject: vim-patch:9.1.0965: filetype: sh filetype set when detecting the use
 of bash (#31749)

Problem:  filetype: sh filetype set when detecting the use of bash
Solution: when bash is detected, use 'bash' filetype instead
          (Luca Saccarola)

closes: vim/vim#16309

https://github.com/vim/vim/commit/b9b762c21f2b61e0e7d8fee43d4d3dc8ecffd721
---
 runtime/lua/vim/filetype/detect.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 4f2fef5b1f..0d9c1ebc2b 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -1494,6 +1494,7 @@ local function sh(path, contents, name)
       vim.b[b].is_kornshell = nil
       vim.b[b].is_sh = nil
     end
+    return M.shell(path, contents, 'bash'), on_detect
     -- Ubuntu links sh to dash
   elseif matchregex(name, [[\<\(sh\|dash\)\>]]) then
     on_detect = function(b)
-- 
cgit 


From 48acbc4d645fe99532b33051006a65a57d36b981 Mon Sep 17 00:00:00 2001
From: Jaehwang Jung 
Date: Sun, 29 Dec 2024 16:00:47 +0900
Subject: fix(treesitter.foldexpr): refresh in the buffers affected by
 OptionSet

---
 runtime/lua/vim/treesitter/_fold.lua | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index 0cb5b497c7..10ba074ab5 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -378,9 +378,13 @@ api.nvim_create_autocmd('OptionSet', {
   pattern = { 'foldminlines', 'foldnestmax' },
   desc = 'Refresh treesitter folds',
   callback = function()
-    for bufnr, _ in pairs(foldinfos) do
+    local bufs = vim.v.option_type == 'local' and { api.nvim_get_current_buf() }
+      or vim.tbl_keys(foldinfos)
+    for _, bufnr in ipairs(bufs) do
       foldinfos[bufnr] = FoldInfo.new()
-      compute_folds_levels(bufnr, foldinfos[bufnr])
+      api.nvim_buf_call(bufnr, function()
+        compute_folds_levels(bufnr, foldinfos[bufnr])
+      end)
       foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr))
     end
   end,
-- 
cgit 


From 493b6899ee898ec43217a8ae270b24bcc3b47281 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sun, 29 Dec 2024 15:27:01 +0100
Subject: vim-patch:9.1.0971: filetype: SLNX files are not recognized

Problem:  filetype: SLNX files are not recognized
Solution: detect '*.slnx' files as xml filetype
          (Gustav Eikaas)

References:
https://blog.ndepend.com/slnx-the-new-net-solution-xml-file-format/
https://blog.jetbrains.com/dotnet/2024/10/04/support-for-slnx-solution-files/

closes: vim/vim#16334

https://github.com/vim/vim/commit/3b3318b64043dcf29d6f06322739f695a5cc257e

Co-authored-by: GustavEikaas 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index d6a29ed84d..f267e5ef6f 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1337,6 +1337,7 @@ local extension = {
   xlb = 'xml',
   xlc = 'xml',
   xba = 'xml',
+  slnx = 'xml',
   xpm = detect_line1('XPM2', 'xpm2', 'xpm'),
   xpm2 = 'xpm2',
   xqy = 'xquery',
-- 
cgit 


From 0e880b5612b0dece2863c70ee608971726ec5f2c Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sun, 29 Dec 2024 15:33:40 +0100
Subject: vim-patch:9.1.0972: filetype: TI linker map files are not recognized

Problem:  filetype: TI linker map files are not recognized
Solution: detect TI linker map files as lnkmap filetype
          (Wu, Zhenyu)

References:
https://downloads.ti.com/docs/esd/SPRUI03A/Content/SPRUI03A_HTML/linker_description.html

closes: vim/vim#16324

https://github.com/vim/vim/commit/5113831d16c05f3a8b47da0c6f95a641d5fc7b2e

Co-authored-by: Wu, Zhenyu 
---
 runtime/lua/vim/filetype.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index f267e5ef6f..ef77a560d8 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -736,7 +736,7 @@ local extension = {
   mk = detect.make,
   mak = detect.make,
   page = 'mallard',
-  map = 'map',
+  map = detect_line1('^%*+$', 'lnkmap', 'map'),
   mws = 'maple',
   mpl = 'maple',
   mv = 'maple',
-- 
cgit 


From 02097e43c8cf33b4302717a489d322ac47fa15c2 Mon Sep 17 00:00:00 2001
From: Maria José Solano 
Date: Sun, 29 Dec 2024 07:35:57 -0800
Subject: fix(lsp): check if sig_help window is focusable when configuring
 cycle keymap

---
 runtime/lua/vim/lsp/buf.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 1926a0228d..69407bc6f8 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -329,7 +329,6 @@ local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help')
 --- @class vim.lsp.buf.signature_help.Opts : vim.lsp.util.open_floating_preview.Opts
 --- @field silent? boolean
 
--- TODO(lewis6991): support multiple clients
 --- Displays signature information about the symbol under the cursor in a
 --- floating window.
 --- @param config? vim.lsp.buf.signature_help.Opts
@@ -356,6 +355,7 @@ function M.signature_help(config)
 
     local ft = vim.bo[ctx.bufnr].filetype
     local total = #signatures
+    local can_cycle = total > 1 and config.focusable
     local idx = 0
 
     --- @param update_win? integer
@@ -371,7 +371,7 @@ function M.signature_help(config)
         return
       end
 
-      local sfx = total > 1 and string.format(' (%d/%d) ( to cycle)', idx, total) or ''
+      local sfx = can_cycle and string.format(' (%d/%d) ( to cycle)', idx, total) or ''
       local title = string.format('Signature Help: %s%s', client.name, sfx)
       if config.border then
         config.title = title
@@ -402,7 +402,7 @@ function M.signature_help(config)
 
     local fbuf, fwin = show_signature()
 
-    if total > 1 then
+    if can_cycle then
       vim.keymap.set('n', '', function()
         show_signature(fwin)
       end, {
-- 
cgit 


From e4bc8b5967d22840c1e52c97acab0f77107cd48c Mon Sep 17 00:00:00 2001
From: Igor 
Date: Sun, 29 Dec 2024 12:23:24 -0300
Subject: fix(treesitter.foldexpr): only refresh valid buffers

Problem: autocmd to refresh folds always uses the current buffer if the
option type is local. However, the current buffer may not have a parser,
and thus the assert that checks for a parser could fail.

Solution: check if the foldinfo contains the buffer, and only refresh if
so.
---
 runtime/lua/vim/treesitter/_fold.lua | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index 10ba074ab5..207ac1ab67 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -378,8 +378,10 @@ api.nvim_create_autocmd('OptionSet', {
   pattern = { 'foldminlines', 'foldnestmax' },
   desc = 'Refresh treesitter folds',
   callback = function()
-    local bufs = vim.v.option_type == 'local' and { api.nvim_get_current_buf() }
-      or vim.tbl_keys(foldinfos)
+    local buf = api.nvim_get_current_buf()
+    local bufs = vim.v.option_type == 'global' and vim.tbl_keys(foldinfos)
+      or foldinfos[buf] and { buf }
+      or {}
     for _, bufnr in ipairs(bufs) do
       foldinfos[bufnr] = FoldInfo.new()
       api.nvim_buf_call(bufnr, function()
-- 
cgit 


From 7b739248a183dc56e38441a79d1630205750924d Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Mon, 30 Dec 2024 11:26:43 +0100
Subject: vim-patch:9.1.0977: filetype: msbuild filetypes are not recognized

Problem:  filetype: msbuild filetypes are not recognized
Solution: detect msbuild files as xml filetype
          (Gustav Eikaas)

closes: vim/vim#16339

https://github.com/vim/vim/commit/32b7e3a8c99d369b02154df74cbe42a37c7c7e68

Co-authored-by: GustavEikaas 
---
 runtime/lua/vim/filetype.lua | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index ef77a560d8..cef3d667d3 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1881,6 +1881,9 @@ local filename = {
   ['/etc/blkid.tab'] = 'xml',
   ['/etc/blkid.tab.old'] = 'xml',
   ['fonts.conf'] = 'xml',
+  ['Directory.Packages.props'] = 'xml',
+  ['Directory.Build.props'] = 'xml',
+  ['Directory.Build.targets'] = 'xml',
   ['.clangd'] = 'yaml',
   ['.clang-format'] = 'yaml',
   ['.clang-tidy'] = 'yaml',
-- 
cgit 


From bdc0b5f5054afb8ba3418f9d6c9b1b3e944f3e3a Mon Sep 17 00:00:00 2001
From: glepnir 
Date: Tue, 31 Dec 2024 19:12:50 +0800
Subject: vim-patch:9.1.0983: not able to get the displayed items in
 complete_i… (#31796)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

vim-patch:9.1.0983: not able to get the displayed items in complete_info()

Problem:  not able to get the displayed items in complete_info()
          (Evgeni Chasnovski)
Solution: return the visible items via the "matches" key for
          complete_info() (glepnir)

fixes: vim/vim#10007
closes: vim/vim#16307

https://github.com/vim/vim/commit/d4088edae21659e14ab5f763c820f4eab9d36981
---
 runtime/lua/vim/_meta/vimfn.lua | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index b580357c85..6662fca84f 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -1023,10 +1023,15 @@ function vim.fn.complete_check() end
 ---     See |complete_info_mode| for the values.
 ---    pum_visible  |TRUE| if popup menu is visible.
 ---     See |pumvisible()|.
----    items  List of completion matches.  Each item is a
----     dictionary containing the entries "word",
+---    items  List of all completion candidates.  Each item
+---     is a dictionary containing the entries "word",
 ---     "abbr", "menu", "kind", "info" and "user_data".
 ---     See |complete-items|.
+---    matches  Same as "items", but only returns items that
+---     are matching current query. If both "matches"
+---     and "items" are in "what", the returned list
+---     will still be named "items", but each item
+---     will have an additional "match" field.
 ---    selected  Selected item index.  First index is zero.
 ---     Index is -1 if no item is selected (showing
 ---     typed text only, or the last completion after
-- 
cgit 


From 1877cd5fcdc0bbe5f3d0685d42d4e295fb819724 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Tue, 31 Dec 2024 11:53:02 +0100
Subject: vim-patch:9.1.0982: TI linker files are not recognized

Problem:  TI linker files are not recognized
Solution: inspect '*.cmd' files and detect TI linker files
          as 'lnk' filetype, include a lnk ftplugin and syntax
          script (Wu, Zhenyu)

closes: vim/vim#16320

https://github.com/vim/vim/commit/39a4eb0b2ca901b59800fad086550053556e59dc

Co-authored-by: Wu, Zhenyu 
---
 runtime/lua/vim/filetype.lua        |  2 +-
 runtime/lua/vim/filetype/detect.lua | 18 ++++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index cef3d667d3..173de8b5d5 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1393,7 +1393,7 @@ local extension = {
   txt = detect.txt,
   xml = detect.xml,
   y = detect.y,
-  cmd = detect_line1('^/%*', 'rexx', 'dosbatch'),
+  cmd = detect.cmd,
   rul = detect.rul,
   cpy = detect_line1('^##', 'python', 'cobol'),
   dsl = detect_line1('^%s*
Date: Sun, 29 Dec 2024 13:44:42 +0100
Subject: feat(lsp): return resolved config for vim.lsp.config[name]

Allows to retrieve the configuration as it will be used by `lsp.enable`
- including the parts merged from `*` and rtp.

This is useful for explicit startup control
(`vim.lsp.start(vim.lsp.config[name])`)

Closes https://github.com/neovim/neovim/issues/31640
---
 runtime/lua/vim/lsp.lua        | 71 ++++++++++++++++++------------------------
 runtime/lua/vim/lsp/health.lua |  2 +-
 2 files changed, 31 insertions(+), 42 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 1c8356d64d..5b92926a21 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -420,9 +420,33 @@ lsp.config = setmetatable({ _configs = {} }, {
   --- @return vim.lsp.Config
   __index = function(self, name)
     validate('name', name, 'string')
-    invalidate_enabled_config(name)
+
+    local rconfig = lsp._enabled_configs[name] or {}
     self._configs[name] = self._configs[name] or {}
-    return self._configs[name]
+
+    if not rconfig.resolved_config then
+      -- Resolve configs from lsp/*.lua
+      -- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
+      local rtp_config = {} ---@type vim.lsp.Config
+      for _, v in ipairs(api.nvim_get_runtime_file(('lsp/%s.lua'):format(name), true)) do
+        local config = assert(loadfile(v))() ---@type any?
+        if type(config) == 'table' then
+          rtp_config = vim.tbl_deep_extend('force', rtp_config, config)
+        else
+          log.warn(string.format('%s does not return a table, ignoring', v))
+        end
+      end
+
+      rconfig.resolved_config = vim.tbl_deep_extend(
+        'force',
+        lsp.config._configs['*'] or {},
+        rtp_config,
+        lsp.config._configs[name] or {}
+      )
+      rconfig.resolved_config.name = name
+    end
+
+    return rconfig.resolved_config
   end,
 
   --- @param self vim.lsp.config
@@ -446,44 +470,6 @@ lsp.config = setmetatable({ _configs = {} }, {
   end,
 })
 
---- @private
---- @param name string
---- @return vim.lsp.Config
-function lsp._resolve_config(name)
-  local econfig = lsp._enabled_configs[name] or {}
-
-  if not econfig.resolved_config then
-    -- Resolve configs from lsp/*.lua
-    -- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
-    local rtp_config = {} ---@type vim.lsp.Config
-    for _, v in ipairs(api.nvim_get_runtime_file(('lsp/%s.lua'):format(name), true)) do
-      local config = assert(loadfile(v))() ---@type any?
-      if type(config) == 'table' then
-        rtp_config = vim.tbl_deep_extend('force', rtp_config, config)
-      else
-        log.warn(string.format('%s does not return a table, ignoring', v))
-      end
-    end
-
-    local config = vim.tbl_deep_extend(
-      'force',
-      lsp.config._configs['*'] or {},
-      rtp_config,
-      lsp.config._configs[name] or {}
-    )
-
-    config.name = name
-
-    validate('cmd', config.cmd, { 'function', 'table' })
-    validate('cmd', config.reuse_client, 'function', true)
-    -- All other fields are validated in client.create
-
-    econfig.resolved_config = config
-  end
-
-  return assert(econfig.resolved_config)
-end
-
 local lsp_enable_autocmd_id --- @type integer?
 
 --- @param bufnr integer
@@ -514,7 +500,9 @@ local function lsp_enable_callback(bufnr)
   end
 
   for name in vim.spairs(lsp._enabled_configs) do
-    local config = lsp._resolve_config(name)
+    local config = lsp.config[name]
+    validate('cmd', config.cmd, { 'function', 'table' })
+    validate('cmd', config.reuse_client, 'function', true)
 
     if can_start(config) then
       -- Deepcopy config so changes done in the client
@@ -522,6 +510,7 @@ local function lsp_enable_callback(bufnr)
       config = vim.deepcopy(config)
 
       if type(config.root_dir) == 'function' then
+        ---@param root_dir string
         config.root_dir(function(root_dir)
           config.root_dir = root_dir
           start(config)
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index d94bc70a65..8af9f2f791 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -184,7 +184,7 @@ local function check_enabled_configs()
   vim.health.start('vim.lsp: Enabled Configurations')
 
   for name in vim.spairs(vim.lsp._enabled_configs) do
-    local config = vim.lsp._resolve_config(name)
+    local config = vim.lsp.config[name]
     local text = {} --- @type string[]
     text[#text + 1] = ('%s:'):format(name)
     for k, v in
-- 
cgit 


From 0bef3b911cc262a007fb4412d864c1832d1268ad Mon Sep 17 00:00:00 2001
From: Gustav Eikaas <46537983+GustavEikaas@users.noreply.github.com>
Date: Tue, 31 Dec 2024 16:40:05 +0100
Subject: fix(vim.fs): joinpath() does not normalize slashes on Windows #31782

---
 runtime/lua/vim/fs.lua | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index f2cd210cac..1b774d5cab 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -105,14 +105,19 @@ function M.basename(file)
   return file:match('/$') and '' or (file:match('[^/]*$'))
 end
 
---- Concatenate directories and/or file paths into a single path with normalization
---- (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`)
+--- Concatenates partial paths into one path. Slashes are normalized (redundant slashes are removed, and on Windows backslashes are replaced with forward-slashes)
+--- (e.g., `"foo/"` and `"/bar"` get joined to `"foo/bar"`)
+--- (windows: e.g `"a\foo\"` and `"\bar"` are joined to `"a/foo/bar"`)
 ---
 ---@since 12
 ---@param ... string
 ---@return string
 function M.joinpath(...)
-  return (table.concat({ ... }, '/'):gsub('//+', '/'))
+  local path = table.concat({ ... }, '/')
+  if iswin then
+    path = path:gsub('\\', '/')
+  end
+  return (path:gsub('//+', '/'))
 end
 
 ---@alias Iterator fun(): string?, string?
-- 
cgit 


From dc692f553aae367a03f286e0d59561247941f96c Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Wed, 1 Jan 2025 12:29:51 -0800
Subject: docs: misc #31479

---
 runtime/lua/vim/_meta/json.lua       | 14 +++++-----
 runtime/lua/vim/fs.lua               | 10 ++++---
 runtime/lua/vim/treesitter/query.lua | 51 ++++++++++++++++++++++--------------
 3 files changed, 46 insertions(+), 29 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/json.lua b/runtime/lua/vim/_meta/json.lua
index 1a7e87db9c..0d59de5fa6 100644
--- a/runtime/lua/vim/_meta/json.lua
+++ b/runtime/lua/vim/_meta/json.lua
@@ -25,18 +25,18 @@ vim.json = {}
 ---
 ---@param str string Stringified JSON data.
 ---@param opts? table Options table with keys:
----                                 - luanil: (table) Table with keys:
----                                   * object: (boolean) When true, converts `null` in JSON objects
----                                                       to Lua `nil` instead of |vim.NIL|.
----                                   * array: (boolean) When true, converts `null` in JSON arrays
----                                                      to Lua `nil` instead of |vim.NIL|.
+---                               - luanil: (table) Table with keys:
+---                                 - object: (boolean) When true, converts `null` in JSON objects
+---                                   to Lua `nil` instead of |vim.NIL|.
+---                                 - array: (boolean) When true, converts `null` in JSON arrays
+---                                   to Lua `nil` instead of |vim.NIL|.
 ---@return any
 function vim.json.decode(str, opts) end
 
 --- Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
 ---@param obj any
 ---@param opts? table Options table with keys:
----                                 - escape_slash: (boolean) (default false) When true, escapes `/`
----                                                           character in JSON strings
+---                                 - escape_slash: (boolean) (default false) Escape slash
+---                                   characters "/" in string values.
 ---@return string
 function vim.json.encode(obj, opts) end
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index 1b774d5cab..d042df96e5 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -105,9 +105,13 @@ function M.basename(file)
   return file:match('/$') and '' or (file:match('[^/]*$'))
 end
 
---- Concatenates partial paths into one path. Slashes are normalized (redundant slashes are removed, and on Windows backslashes are replaced with forward-slashes)
---- (e.g., `"foo/"` and `"/bar"` get joined to `"foo/bar"`)
---- (windows: e.g `"a\foo\"` and `"\bar"` are joined to `"a/foo/bar"`)
+--- Concatenates partial paths (one absolute or relative path followed by zero or more relative
+--- paths). Slashes are normalized: redundant slashes are removed, and (on Windows) backslashes are
+--- replaced with forward-slashes.
+---
+--- Examples:
+--- - "foo/", "/bar" => "foo/bar"
+--- - Windows: "a\foo\", "\bar" => "a/foo/bar"
 ---
 ---@since 12
 ---@param ... string
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index dbe3d54c2f..f9c497337f 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -1,3 +1,6 @@
+--- @brief This Lua |treesitter-query| interface allows you to create queries and use them to parse
+--- text. See |vim.treesitter.query.parse()| for a working example.
+
 local api = vim.api
 local language = require('vim.treesitter.language')
 local memoize = vim.func._memoize
@@ -8,9 +11,9 @@ local M = {}
 ---Parsed query, see |vim.treesitter.query.parse()|
 ---
 ---@class vim.treesitter.Query
----@field lang string name of the language for this parser
+---@field lang string parser language name
 ---@field captures string[] list of (unique) capture names defined in query
----@field info vim.treesitter.QueryInfo contains information used in the query (e.g. captures, predicates, directives)
+---@field info vim.treesitter.QueryInfo query context (e.g. captures, predicates, directives)
 ---@field query TSQuery userdata query object
 local Query = {}
 Query.__index = Query
@@ -228,20 +231,28 @@ M.get = memoize('concat-2', function(lang, query_name)
   return M.parse(lang, query_string)
 end)
 
---- Parse {query} as a string. (If the query is in a file, the caller
---- should read the contents into a string before calling).
----
---- Returns a `Query` (see |lua-treesitter-query|) object which can be used to
---- search nodes in the syntax tree for the patterns defined in {query}
---- using the `iter_captures` and `iter_matches` methods.
+--- Parses a {query} string and returns a `Query` object (|lua-treesitter-query|), which can be used
+--- to search the tree for the query patterns (via |Query:iter_captures()|, |Query:iter_matches()|),
+--- or inspect the query via these fields:
+---   - `captures`: a list of unique capture names defined in the query (alias: `info.captures`).
+---   - `info.patterns`: information about predicates.
 ---
---- Exposes `info` and `captures` with additional context about {query}.
----   - `captures` contains the list of unique capture names defined in {query}.
----   - `info.captures` also points to `captures`.
----   - `info.patterns` contains information about predicates.
+--- Example (select the code then run `:'<,'>lua` to try it):
+--- ```lua
+--- local query = vim.treesitter.query.parse('vimdoc', [[
+---   ; query
+---   ((h1) @str
+---     (#trim! @str 1 1 1 1))
+--- ]])
+--- local tree = vim.treesitter.get_parser():parse()[1]
+--- for id, node, metadata in query:iter_captures(tree:root(), 0) do
+---    -- Print the node name and source text.
+---    vim.print({node:type(), vim.treesitter.get_node_text(node, vim.api.nvim_get_current_buf())})
+--- end
+--- ```
 ---
 ---@param lang string Language to use for the query
----@param query string Query in s-expr syntax
+---@param query string Query text, in s-expr syntax
 ---
 ---@return vim.treesitter.Query : Parsed query
 ---
@@ -847,20 +858,22 @@ local function match_id_hash(_, match)
   return (match:info())
 end
 
---- Iterate over all captures from all matches inside {node}
+--- Iterates over all captures from all matches in {node}.
 ---
---- {source} is needed if the query contains predicates; then the caller
+--- {source} is required if the query contains predicates; then the caller
 --- must ensure to use a freshly parsed tree consistent with the current
 --- text of the buffer (if relevant). {start} and {stop} can be used to limit
 --- matches inside a row range (this is typically used with root node
 --- as the {node}, i.e., to get syntax highlight matches in the current
 --- viewport). When omitted, the {start} and {stop} row values are used from the given node.
 ---
---- The iterator returns four values: a numeric id identifying the capture,
---- the captured node, metadata from any directives processing the match,
---- and the match itself.
---- The following example shows how to get captures by name:
+--- The iterator returns four values:
+--- 1. the numeric id identifying the capture
+--- 2. the captured node
+--- 3. metadata from any directives processing the match
+--- 4. the match itself
 ---
+--- Example: how to get captures by name:
 --- ```lua
 --- for id, node, metadata, match in query:iter_captures(tree:root(), bufnr, first, last) do
 ---   local name = query.captures[id] -- name of the capture in the query
-- 
cgit 


From 6dc0eb9f41e6453fe003dd3a28c58b701fd003c9 Mon Sep 17 00:00:00 2001
From: dundargoc 
Date: Wed, 1 Jan 2025 13:13:40 +0100
Subject: fix(vim.fs.abspath): correctly handle UNC paths

---
 runtime/lua/vim/fs.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index d042df96e5..ee713e4b58 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -725,7 +725,7 @@ function M.abspath(path)
     prefix, path = split_windows_path(path)
   end
 
-  if vim.startswith(path, '/') then
+  if prefix == '//' or vim.startswith(path, '/') then
     -- Path is already absolute, do nothing
     return prefix .. path
   end
-- 
cgit 


From 9d9ee3476e6478850ce8822c85154f0c98570371 Mon Sep 17 00:00:00 2001
From: Jaehwang Jung 
Date: Thu, 2 Jan 2025 17:33:16 +0900
Subject: fix(lsp): ensure watcher cancel

* Cancel watcher in the "force" case.
* Cancel watcher outside the async callback. It seems nvim doesn't wait
  async jobs on quitting, leaving detached inotifywait processes.
* Clean up cancelling callbacks.
---
 runtime/lua/vim/lsp/_watchfiles.lua | 1 +
 runtime/lua/vim/lsp/client.lua      | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua
index 248969885c..4711b3cc9b 100644
--- a/runtime/lua/vim/lsp/_watchfiles.lua
+++ b/runtime/lua/vim/lsp/_watchfiles.lua
@@ -174,6 +174,7 @@ function M.cancel(client_id)
       cancel()
     end
   end
+  cancels[client_id] = nil
 end
 
 return M
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index d51a45b473..5d11312c77 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -805,6 +805,8 @@ function Client:stop(force)
     return
   end
 
+  vim.lsp._watchfiles.cancel(self.id)
+
   if force or not self.initialized or self._graceful_shutdown_failed then
     rpc.terminate()
     return
@@ -819,7 +821,6 @@ function Client:stop(force)
       rpc.terminate()
       self._graceful_shutdown_failed = true
     end
-    vim.lsp._watchfiles.cancel(self.id)
   end)
 end
 
-- 
cgit 


From 48e2a73610ca5639408f79b3d8eebd3e5f57a327 Mon Sep 17 00:00:00 2001
From: luukvbaal 
Date: Thu, 2 Jan 2025 14:51:03 +0100
Subject: feat(ui)!: emit prompt "messages" as cmdline events #31525

Problem:  Prompts are emitted as messages events, where cmdline events
          are more appropriate. The user input is also emitted as
          message events in fast context, so cannot be displayed with
          vim.ui_attach().
Solution: Prompt for user input through cmdline prompts.
---
 runtime/lua/vim/_meta/builtin.lua | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua
index b8779b66fe..9fa2e242c4 100644
--- a/runtime/lua/vim/_meta/builtin.lua
+++ b/runtime/lua/vim/_meta/builtin.lua
@@ -233,9 +233,8 @@ function vim.wait(time, callback, interval, fast_only) end
 --- {callback} receives event name plus additional parameters. See |ui-popupmenu|
 --- and the sections below for event format for respective events.
 ---
---- Callbacks for `msg_show` events are executed in |api-fast| context unless
---- Nvim will wait for input, in which case messages should be shown
---- immediately.
+--- Callbacks for `msg_show` events are executed in |api-fast| context; showing
+--- the message should be scheduled.
 ---
 --- Excessive errors inside the callback will result in forced detachment.
 ---
-- 
cgit 


From b365036ab3f5e91439a5397ed0f32b651d60f08c Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 3 Jan 2025 10:42:52 +0100
Subject: vim-patch:9.1.0986: filetype: 'jj' filetype is a bit imprecise

Problem:  filetype: 'jj' filetype is a bit imprecise
Solution: rename 'jj' filetype to 'jjdescription'
          (Gregory Anders)

closes: vim/vim#16364

https://github.com/vim/vim/commit/58c44e8833365e1a777330491c2799ae324ed893

Co-authored-by: Gregory Anders 
---
 runtime/lua/vim/filetype.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 173de8b5d5..849bc4af92 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -631,7 +631,7 @@ local extension = {
   clp = 'jess',
   jgr = 'jgraph',
   jinja = 'jinja',
-  jjdescription = 'jj',
+  jjdescription = 'jjdescription',
   j73 = 'jovial',
   jov = 'jovial',
   jovial = 'jovial',
-- 
cgit 


From fa298fd2f4b0c46df434938cbc17467e263cc094 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 3 Jan 2025 10:46:22 +0100
Subject: vim-patch:9.1.0987: filetype: cake files are not recognized

Problem:  filetype: cake files are not recognized
Solution: detect '*.cake' files as cs filetype
          (Zoe Roux)

References:
https://cakebuild.net/

closes: vim/vim#16367

https://github.com/vim/vim/commit/a407573f30a978b3aa61532bbd9b0ae94a87dc32

Co-authored-by: Zoe Roux 
---
 runtime/lua/vim/filetype.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 849bc4af92..b6d6906589 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -353,6 +353,7 @@ local extension = {
   cql = 'cqlang',
   crm = 'crm',
   cr = 'crystal',
+  cake = 'cs',
   csx = 'cs',
   cs = 'cs',
   csc = 'csc',
-- 
cgit 


From b52531a9cbbd1843490333452cd124e8be070690 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Fri, 3 Jan 2025 08:29:36 -0800
Subject: docs: misc #31822

* docs: drop "lua-" prefix from most treesitter tags
* docs: move mouse section from tui.txt to gui.txt
* docs: misc
---
 runtime/lua/vim/_options.lua | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua
index 77d7054626..dc37595578 100644
--- a/runtime/lua/vim/_options.lua
+++ b/runtime/lua/vim/_options.lua
@@ -229,10 +229,8 @@ end
 --- global value of a |global-local| option, see |:setglobal|.
 --- 
---- Get or set |options|. Like `:set`. Invalid key is an error. ---- ---- Note: this works on both buffer-scoped and window-scoped options using the ---- current buffer and window. +--- Get or set |options|. Works like `:set`, so buffer/window-scoped options target the current +--- buffer/window. Invalid key is an error. --- --- Example: --- -- cgit From a8ace2c58a318552869462a36859aabf1cdfaa68 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 2 Jan 2025 16:29:00 +0100 Subject: fix(vim.fs.normalize): normalize case for windows drive letter Also add tests for the current path casing behavior so it doesn't get accidentally changed. --- runtime/lua/vim/fs.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index ee713e4b58..04a6e43db1 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -629,8 +629,8 @@ function M.normalize(path, opts) return prefix .. path end - -- Remove extraneous slashes from the prefix - prefix = prefix:gsub('/+', '/') + -- Ensure capital drive and remove extraneous slashes from the prefix + prefix = prefix:gsub('^%a:', string.upper):gsub('/+', '/') end if not opts._fast then -- cgit From 5e9040648739252b1b8d38c9b46b111767f34cf2 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sat, 4 Jan 2025 15:27:36 -0800 Subject: fix(health): set nomodifiable in checkhealth buffers --- runtime/lua/vim/health.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 52a7a13966..3e831371b8 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -407,6 +407,9 @@ function M._check(mods, plugin_names) -- Clear the 'Running healthchecks...' message. vim.cmd.redraw() vim.print('') + + -- Once we're done writing checks, set nomodifiable. + vim.bo[bufnr].modifiable = false end return M -- cgit From b61051ccb4c23958d43d285b8b801af11620264f Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Sun, 1 Sep 2024 16:54:30 -0700 Subject: feat(func): allow manual cache invalidation for _memoize This commit also adds some tests for the existing memoization functionality. --- runtime/lua/vim/func.lua | 14 +++++---- runtime/lua/vim/func/_memoize.lua | 56 +++++++++++++++++++++++++----------- runtime/lua/vim/treesitter/query.lua | 4 +-- 3 files changed, 49 insertions(+), 25 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/func.lua b/runtime/lua/vim/func.lua index f71659ffb4..fc8fa62c71 100644 --- a/runtime/lua/vim/func.lua +++ b/runtime/lua/vim/func.lua @@ -3,9 +3,6 @@ local M = {} -- TODO(lewis6991): Private for now until: -- - There are other places in the codebase that could benefit from this -- (e.g. LSP), but might require other changes to accommodate. --- - Invalidation of the cache needs to be controllable. Using weak tables --- is an acceptable invalidation policy, but it shouldn't be the only --- one. -- - I don't think the story around `hash` is completely thought out. We -- may be able to have a good default hash by hashing each argument, -- so basically a better 'concat'. @@ -17,6 +14,10 @@ local M = {} --- Internally uses a |lua-weaktable| to cache the results of {fn} meaning the --- cache will be invalidated whenever Lua does garbage collection. --- +--- The cache can also be manually invalidated by calling `:clear()` on the returned object. +--- Calling this function with no arguments clears the entire cache; otherwise, the arguments will +--- be interpreted as function inputs, and only the cache entry at their hash will be cleared. +--- --- The memoized function returns shared references so be wary about --- mutating return values. --- @@ -32,11 +33,12 @@ local M = {} --- first n arguments passed to {fn}. --- --- @param fn F Function to memoize. ---- @param strong? boolean Do not use a weak table +--- @param weak? boolean Use a weak table (default `true`) --- @return F # Memoized version of {fn} --- @nodoc -function M._memoize(hash, fn, strong) - return require('vim.func._memoize')(hash, fn, strong) +function M._memoize(hash, fn, weak) + -- this is wrapped in a function to lazily require the module + return require('vim.func._memoize')(hash, fn, weak) end return M diff --git a/runtime/lua/vim/func/_memoize.lua b/runtime/lua/vim/func/_memoize.lua index 6e557905a7..c46f878067 100644 --- a/runtime/lua/vim/func/_memoize.lua +++ b/runtime/lua/vim/func/_memoize.lua @@ -1,5 +1,7 @@ --- Module for private utility functions +--- @alias vim.func.MemoObj { _hash: (fun(...): any), _weak: boolean?, _cache: table } + --- @param argc integer? --- @return fun(...): any local function concat_hash(argc) @@ -33,29 +35,49 @@ local function resolve_hash(hash) return hash end +--- @param weak boolean? +--- @return table +local create_cache = function(weak) + return setmetatable({}, { + __mode = weak ~= false and 'kv', + }) +end + --- @generic F: function --- @param hash integer|string|fun(...): any --- @param fn F ---- @param strong? boolean +--- @param weak? boolean --- @return F -return function(hash, fn, strong) +return function(hash, fn, weak) vim.validate('hash', hash, { 'number', 'string', 'function' }) vim.validate('fn', fn, 'function') + vim.validate('weak', weak, 'boolean', true) - ---@type table> - local cache = {} - if not strong then - setmetatable(cache, { __mode = 'kv' }) - end - - hash = resolve_hash(hash) + --- @type vim.func.MemoObj + local obj = { + _cache = create_cache(weak), + _hash = resolve_hash(hash), + _weak = weak, + --- @param self vim.func.MemoObj + clear = function(self, ...) + if select('#', ...) == 0 then + self._cache = create_cache(self._weak) + return + end + local key = self._hash(...) + self._cache[key] = nil + end, + } - return function(...) - local key = hash(...) - if cache[key] == nil then - cache[key] = vim.F.pack_len(fn(...)) - end - - return vim.F.unpack_len(cache[key]) - end + return setmetatable(obj, { + --- @param self vim.func.MemoObj + __call = function(self, ...) + local key = self._hash(...) + local cache = self._cache + if cache[key] == nil then + cache[key] = vim.F.pack_len(fn(...)) + end + return vim.F.unpack_len(cache[key]) + end, + }) end diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index f9c497337f..2b3b9096a6 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -902,8 +902,8 @@ function Query:iter_captures(node, source, start, stop) local cursor = vim._create_ts_querycursor(node, self.query, start, stop, { match_limit = 256 }) - local apply_directives = memoize(match_id_hash, self.apply_directives, true) - local match_preds = memoize(match_id_hash, self.match_preds, true) + local apply_directives = memoize(match_id_hash, self.apply_directives, false) + local match_preds = memoize(match_id_hash, self.match_preds, false) local function iter(end_line) local capture, captured_node, match = cursor:next_capture() -- cgit From 548f19ccc3018aa563d25cf99a9ce70a56b115fe Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sun, 5 Jan 2025 09:51:51 -0800 Subject: feat(health): close checkhealth buffers with q #31870 --- runtime/lua/vim/health.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 3e831371b8..ebad000c5b 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -408,6 +408,15 @@ function M._check(mods, plugin_names) vim.cmd.redraw() vim.print('') + -- Quit with 'q' inside healthcheck buffers. + vim.api.nvim_buf_set_keymap( + bufnr, + 'n', + 'q', + 'q', + { silent = true, noremap = true, nowait = true } + ) + -- Once we're done writing checks, set nomodifiable. vim.bo[bufnr].modifiable = false end -- cgit From 570a8da01b55c3aad1f057be236f55ccf82ed8af Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 5 Jan 2025 11:31:39 -0800 Subject: fix(health): "q" should not close last window #31876 --- runtime/lua/vim/health.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index ebad000c5b..01166628b1 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -409,13 +409,12 @@ function M._check(mods, plugin_names) vim.print('') -- Quit with 'q' inside healthcheck buffers. - vim.api.nvim_buf_set_keymap( - bufnr, - 'n', - 'q', - 'q', - { silent = true, noremap = true, nowait = true } - ) + vim.keymap.set('n', 'q', function() + local ok, _ = pcall(vim.cmd.close) + if not ok then + vim.cmd.bdelete() + end + end, { buffer = bufnr, silent = true, noremap = true, nowait = true }) -- Once we're done writing checks, set nomodifiable. vim.bo[bufnr].modifiable = false -- cgit From 8d2ee542a82a0d162198f27de316ddfc81e8761c Mon Sep 17 00:00:00 2001 From: vanaigr Date: Wed, 18 Dec 2024 12:23:28 -0600 Subject: perf(decor): join predicates and matches cache --- runtime/lua/vim/treesitter/query.lua | 81 ++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 36 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 2b3b9096a6..01fdb708eb 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -762,16 +762,7 @@ end ---@private ---@param match TSQueryMatch ---@param source integer|string -function Query:match_preds(match, source) - local _, pattern = match:info() - local preds = self.info.patterns[pattern] - - if not preds then - return true - end - - local captures = match:captures() - +function Query:match_preds(preds, pattern, captures, source) for _, pred in pairs(preds) do -- Here we only want to return if a predicate DOES NOT match, and -- continue on the other case. This way unknown predicates will not be considered, @@ -807,17 +798,9 @@ end ---@private ---@param match TSQueryMatch ---@return vim.treesitter.query.TSMetadata metadata -function Query:apply_directives(match, source) +function Query:apply_directives(preds, pattern, captures, source) ---@type vim.treesitter.query.TSMetadata local metadata = {} - local _, pattern = match:info() - local preds = self.info.patterns[pattern] - - if not preds then - return metadata - end - - local captures = match:captures() for _, pred in pairs(preds) do if is_directive(pred[1]) then @@ -902,8 +885,10 @@ function Query:iter_captures(node, source, start, stop) local cursor = vim._create_ts_querycursor(node, self.query, start, stop, { match_limit = 256 }) - local apply_directives = memoize(match_id_hash, self.apply_directives, false) - local match_preds = memoize(match_id_hash, self.match_preds, false) + -- For faster checks that a match is not in the cache. + local highest_cached_match_id = -1 + ---@type table + local match_cache = {} local function iter(end_line) local capture, captured_node, match = cursor:next_capture() @@ -912,16 +897,35 @@ function Query:iter_captures(node, source, start, stop) return end - if not match_preds(self, match, source) then - local match_id = match:info() - cursor:remove_match(match_id) - if end_line and captured_node:range() > end_line then - return nil, captured_node, nil, nil - end - return iter(end_line) -- tail call: try next match + local match_id, pattern = match:info() + + --- @type vim.treesitter.query.TSMetadata + local metadata + if match_id <= highest_cached_match_id then + metadata = match_cache[match_id] end - local metadata = apply_directives(self, match, source) + if not metadata then + local preds = self.info.patterns[pattern] + if preds then + local captures = match:captures() + + if not self:match_preds(preds, pattern, captures, source) then + cursor:remove_match(match_id) + if end_line and captured_node:range() > end_line then + return nil, captured_node, nil, nil + end + return iter(end_line) -- tail call: try next match + end + + metadata = self:apply_directives(preds, pattern, captures, source) + else + metadata = {} + end + + highest_cached_match_id = math.max(highest_cached_match_id, match_id) + match_cache[match_id] = metadata + end return capture, captured_node, metadata, match end @@ -985,16 +989,21 @@ function Query:iter_matches(node, source, start, stop, opts) end local match_id, pattern = match:info() + local preds = self.info.patterns[pattern] + local captures = match:captures() - if not self:match_preds(match, source) then - cursor:remove_match(match_id) - return iter() -- tail call: try next match + --- @type vim.treesitter.query.TSMetadata + local metadata + if preds then + if not self:match_preds(preds, pattern, captures, source) then + cursor:remove_match(match_id) + return iter() -- tail call: try next match + end + metadata = self:apply_directives(preds, pattern, captures, source) + else + metadata = {} end - local metadata = self:apply_directives(match, source) - - local captures = match:captures() - if opts.all == false then -- Convert the match table into the old buggy version for backward -- compatibility. This is slow, but we only do it when the caller explicitly opted into it by -- cgit From dd234135ad20119917831fd8ffcb19d8562022ca Mon Sep 17 00:00:00 2001 From: vanaigr Date: Wed, 18 Dec 2024 01:06:41 -0600 Subject: refactor: split predicates and directives --- runtime/lua/vim/treesitter/highlighter.lua | 4 +- runtime/lua/vim/treesitter/query.lua | 172 +++++++++++++++++------------ 2 files changed, 106 insertions(+), 70 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 8ce8652f7d..96503c38ea 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -299,6 +299,8 @@ local function on_line_impl(self, buf, line, is_spell_nav) state.highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1) end + local captures = state.highlighter_query:query().captures + while line >= state.next_row do local capture, node, metadata, match = state.iter(line) @@ -311,7 +313,7 @@ local function on_line_impl(self, buf, line, is_spell_nav) if capture then local hl = state.highlighter_query:get_hl_from_capture(capture) - local capture_name = state.highlighter_query:query().captures[capture] + local capture_name = captures[capture] local spell, spell_pri_offset = get_spell(capture_name) diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 01fdb708eb..1fc001b39f 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -7,6 +7,59 @@ local memoize = vim.func._memoize local M = {} +local function is_directive(name) + return string.sub(name, -1) == '!' +end + +---@nodoc +---@class vim.treesitter.query.ProcessedPredicate +---@field [1] string predicate name +---@field [2] boolean should match +---@field [3] (integer|string)[] the original predicate + +---@alias vim.treesitter.query.ProcessedDirective (integer|string)[] + +---@nodoc +---@class vim.treesitter.query.ProcessedPattern { +---@field predicates vim.treesitter.query.ProcessedPredicate[] +---@field directives vim.treesitter.query.ProcessedDirective[] + +--- Splits the query patterns into predicates and directives. +---@param patterns table +---@return table +local function process_patterns(patterns) + ---@type table + local processed_patterns = {} + + for k, pattern_list in pairs(patterns) do + ---@type vim.treesitter.query.ProcessedPredicate[] + local predicates = {} + ---@type vim.treesitter.query.ProcessedDirective[] + local directives = {} + + for _, pattern in ipairs(pattern_list) do + -- Note: tree-sitter strips the leading # from predicates for us. + local pred_name = pattern[1] + ---@cast pred_name string + + if is_directive(pred_name) then + table.insert(directives, pattern) + else + local should_match = true + if pred_name:match('^not%-') then + pred_name = pred_name:sub(5) + should_match = false + end + table.insert(predicates, { pred_name, should_match, pattern }) + end + end + + processed_patterns[k] = { predicates = predicates, directives = directives } + end + + return processed_patterns +end + ---@nodoc ---Parsed query, see |vim.treesitter.query.parse()| --- @@ -15,6 +68,7 @@ local M = {} ---@field captures string[] list of (unique) capture names defined in query ---@field info vim.treesitter.QueryInfo query context (e.g. captures, predicates, directives) ---@field query TSQuery userdata query object +---@field private _processed_patterns table local Query = {} Query.__index = Query @@ -33,6 +87,7 @@ function Query.new(lang, ts_query) patterns = query_info.patterns, } self.captures = self.info.captures + self._processed_patterns = process_patterns(self.info.patterns) return self end @@ -751,67 +806,50 @@ function M.list_predicates() return vim.tbl_keys(predicate_handlers) end -local function xor(x, y) - return (x or y) and not (x and y) -end - -local function is_directive(name) - return string.sub(name, -1) == '!' -end - ---@private ----@param match TSQueryMatch +---@param pattern_i integer +---@param predicates vim.treesitter.query.ProcessedPredicate[] +---@param captures table ---@param source integer|string -function Query:match_preds(preds, pattern, captures, source) - for _, pred in pairs(preds) do - -- Here we only want to return if a predicate DOES NOT match, and - -- continue on the other case. This way unknown predicates will not be considered, - -- which allows some testing and easier user extensibility (#12173). - -- Also, tree-sitter strips the leading # from predicates for us. - local is_not = false - - -- Skip over directives... they will get processed after all the predicates. - if not is_directive(pred[1]) then - local pred_name = pred[1] - if pred_name:match('^not%-') then - pred_name = pred_name:sub(5) - is_not = true - end - - local handler = predicate_handlers[pred_name] - - if not handler then - error(string.format('No handler for %s', pred[1])) - return false - end - - local pred_matches = handler(captures, pattern, source, pred) +---@return boolean whether the predicates match +function Query:_match_predicates(predicates, pattern_i, captures, source) + for _, predicate in ipairs(predicates) do + local processed_name = predicate[1] + local should_match = predicate[2] + local orig_predicate = predicate[3] + + local handler = predicate_handlers[processed_name] + if not handler then + error(string.format('No handler for %s', orig_predicate[1])) + return false + end - if not xor(is_not, pred_matches) then - return false - end + local does_match = handler(captures, pattern_i, source, orig_predicate) + if does_match ~= should_match then + return false end end return true end ---@private ----@param match TSQueryMatch +---@param pattern_i integer +---@param directives vim.treesitter.query.ProcessedDirective[] +---@param source integer|string +---@param captures table ---@return vim.treesitter.query.TSMetadata metadata -function Query:apply_directives(preds, pattern, captures, source) +function Query:_apply_directives(directives, pattern_i, captures, source) ---@type vim.treesitter.query.TSMetadata local metadata = {} - for _, pred in pairs(preds) do - if is_directive(pred[1]) then - local handler = directive_handlers[pred[1]] + for _, directive in pairs(directives) do + local handler = directive_handlers[directive[1]] - if not handler then - error(string.format('No handler for %s', pred[1])) - end - - handler(captures, pattern, source, pred, metadata) + if not handler then + error(string.format('No handler for %s', directive[1])) end + + handler(captures, pattern_i, source, directive, metadata) end return metadata @@ -835,12 +873,6 @@ local function value_or_node_range(start, stop, node) return start, stop end ---- @param match TSQueryMatch ---- @return integer -local function match_id_hash(_, match) - return (match:info()) -end - --- Iterates over all captures from all matches in {node}. --- --- {source} is required if the query contains predicates; then the caller @@ -897,7 +929,7 @@ function Query:iter_captures(node, source, start, stop) return end - local match_id, pattern = match:info() + local match_id, pattern_i = match:info() --- @type vim.treesitter.query.TSMetadata local metadata @@ -906,11 +938,14 @@ function Query:iter_captures(node, source, start, stop) end if not metadata then - local preds = self.info.patterns[pattern] - if preds then + metadata = {} + + local processed_pattern = self._processed_patterns[pattern_i] + if processed_pattern then local captures = match:captures() - if not self:match_preds(preds, pattern, captures, source) then + local predicates = processed_pattern.predicates + if not self:_match_predicates(predicates, pattern_i, captures, source) then cursor:remove_match(match_id) if end_line and captured_node:range() > end_line then return nil, captured_node, nil, nil @@ -918,9 +953,8 @@ function Query:iter_captures(node, source, start, stop) return iter(end_line) -- tail call: try next match end - metadata = self:apply_directives(preds, pattern, captures, source) - else - metadata = {} + local directives = processed_pattern.directives + metadata = self:_apply_directives(directives, pattern_i, captures, source) end highest_cached_match_id = math.max(highest_cached_match_id, match_id) @@ -988,20 +1022,20 @@ function Query:iter_matches(node, source, start, stop, opts) return end - local match_id, pattern = match:info() - local preds = self.info.patterns[pattern] + local match_id, pattern_i = match:info() + local processed_pattern = self._processed_patterns[pattern_i] local captures = match:captures() --- @type vim.treesitter.query.TSMetadata - local metadata - if preds then - if not self:match_preds(preds, pattern, captures, source) then + local metadata = {} + if processed_pattern then + local predicates = processed_pattern.predicates + if not self:_match_predicates(predicates, pattern_i, captures, source) then cursor:remove_match(match_id) return iter() -- tail call: try next match end - metadata = self:apply_directives(preds, pattern, captures, source) - else - metadata = {} + local directives = processed_pattern.directives + metadata = self:_apply_directives(directives, pattern_i, captures, source) end if opts.all == false then @@ -1012,11 +1046,11 @@ function Query:iter_matches(node, source, start, stop, opts) for k, v in pairs(captures or {}) do old_match[k] = v[#v] end - return pattern, old_match, metadata + return pattern_i, old_match, metadata end -- TODO(lewis6991): create a new function that returns {match, metadata} - return pattern, captures, metadata + return pattern_i, captures, metadata end return iter end -- cgit From 86770108e2c6e08c2b8b95f1611923ba99b854dd Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Mon, 6 Jan 2025 15:05:50 +0100 Subject: fix(lsp): open_floating_preview() zindex relative to current window #31886 Problem: open_floating_preview() may be hidden behind current window if that is floating and has a higher zindex. Solution: Open floating preview with zindex higher than current window. --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 6bee5bc31f..89b774816b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -883,7 +883,7 @@ function M.make_floating_popup_options(width, height, opts) style = 'minimal', width = width, border = opts.border or default_border, - zindex = opts.zindex or 50, + zindex = opts.zindex or (api.nvim_win_get_config(0).zindex or 49) + 1, title = title, title_pos = title_pos, } -- cgit From 30de00687b899824bb319dfb3f7989ea3f936617 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Mon, 6 Jan 2025 16:56:53 -0800 Subject: refactor(treesitter): simplify condition #31889 --- runtime/lua/vim/treesitter/languagetree.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 4b42164dc8..330eb45749 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -443,7 +443,7 @@ function LanguageTree:parse(range) end end - if not self._injections_processed and range ~= false and range ~= nil then + if not self._injections_processed and range then query_time = self:_add_injections() self._injections_processed = true end -- cgit From d5308637bf1aac2b97fccf73a0ffdef304eaa1d6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 3 Jan 2025 20:12:15 +0800 Subject: vim-patch:9.1.0984: exception handling can be improved Problem: exception handling can be improved Solution: add v:stacktrace and getstacktrace() closes: vim/vim#16360 https://github.com/vim/vim/commit/663d18d6102f40d14e36096ec590445e61026ed6 Co-authored-by: ichizok Co-authored-by: Naruhiko Nishino --- runtime/lua/vim/_meta/vimfn.lua | 14 ++++++++++++++ runtime/lua/vim/_meta/vvars.lua | 12 ++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 6662fca84f..3de8b9951c 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -3770,6 +3770,20 @@ function vim.fn.getregtype(regname) end --- @return vim.fn.getscriptinfo.ret[] function vim.fn.getscriptinfo(opts) end +--- Returns the current stack trace of Vim scripts. +--- Stack trace is a |List|, of which each item is a |Dictionary| +--- with the following items: +--- funcref The funcref if the stack is at the function, +--- otherwise this item is not exist. +--- event The string of the event description if the +--- stack is at autocmd event, otherwise this item +--- is not exist. +--- lnum The line number of the script on the stack. +--- filepath The file path of the script on the stack. +--- +--- @return table[] +function vim.fn.getstacktrace() end + --- If {tabnr} is not specified, then information about all the --- tab pages is returned as a |List|. Each List item is a --- |Dictionary|. Otherwise, {tabnr} specifies the tab page diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index 445da4e02f..c1b8695bbf 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -203,7 +203,8 @@ vim.v.errors = ... vim.v.event = ... --- The value of the exception most recently caught and not ---- finished. See also `v:throwpoint` and `throw-variables`. +--- finished. See also `v:stacktrace`, `v:throwpoint`, and +--- `throw-variables`. --- Example: --- --- ```vim @@ -616,6 +617,13 @@ vim.v.servername = ... --- @type integer vim.v.shell_error = ... +--- The stack trace of the exception most recently caught and +--- not finished. Refer to `getstacktrace()` for the structure of +--- stack trace. See also `v:exception`, `v:throwpoint`, and +--- `throw-variables`. +--- @type table[] +vim.v.stacktrace = ... + --- Last given status message. --- Modifiable (can be set). --- @type string @@ -718,7 +726,7 @@ vim.v.this_session = ... --- The point where the exception most recently caught and not --- finished was thrown. Not set when commands are typed. See ---- also `v:exception` and `throw-variables`. +--- also `v:exception`, `v:stacktrace`, and `throw-variables`. --- Example: --- --- ```vim -- cgit From 8fa4306eb910028ee8df8685ae9b1649608c2608 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 7 Jan 2025 09:05:52 +0800 Subject: vim-patch:9.1.0991: v:stacktrace has wrong type in Vim9 script Problem: v:stacktrace has wrong type in Vim9 script. Solution: Change the type to t_list_dict_any. Fix grammar in docs. (zeertzjq) closes: vim/vim#16390 https://github.com/vim/vim/commit/6655bef33047b826e0ccb8c686f3f57e47161b1c --- runtime/lua/vim/_meta/vimfn.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 3de8b9951c..031b109b38 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -3773,12 +3773,12 @@ function vim.fn.getscriptinfo(opts) end --- Returns the current stack trace of Vim scripts. --- Stack trace is a |List|, of which each item is a |Dictionary| --- with the following items: ---- funcref The funcref if the stack is at the function, ---- otherwise this item is not exist. +--- funcref The funcref if the stack is at a function, +--- otherwise this item is omitted. --- event The string of the event description if the ---- stack is at autocmd event, otherwise this item ---- is not exist. ---- lnum The line number of the script on the stack. +--- stack is at an autocmd event, otherwise this +--- item is omitted. +--- lnum The line number in the script on the stack. --- filepath The file path of the script on the stack. --- --- @return table[] -- cgit From d9ee0d2984e5fc30cb032785d32f42c72c7e64e1 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Wed, 1 Jan 2025 11:33:45 -0800 Subject: perf(treesitter): don't fetch parser for each fold line **Problem:** The treesitter `foldexpr` calls `get_parser()` for each line in the buffer when calculating folds. This can be incredibly slow for buffers where a parser cannot be found (because the result is not cached), and exponentially more so when the user has many `runtimepath`s. **Solution:** Only fetch the parser when it is needed; that is, only when initializing fold data for a buffer. Co-authored-by: Jongwook Choi Co-authored-by: Justin M. Keyes --- runtime/lua/vim/treesitter/_fold.lua | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index 207ac1ab67..d16013eca2 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -19,14 +19,19 @@ local api = vim.api ---The range on which to evaluate foldexpr. ---When in insert mode, the evaluation is deferred to InsertLeave. ---@field foldupdate_range? Range2 +--- +---The treesitter parser associated with this buffer. +---@field parser? vim.treesitter.LanguageTree local FoldInfo = {} FoldInfo.__index = FoldInfo ---@private -function FoldInfo.new() +---@param bufnr integer +function FoldInfo.new(bufnr) return setmetatable({ levels0 = {}, levels = {}, + parser = ts.get_parser(bufnr, nil, { error = false }), }, FoldInfo) end @@ -69,7 +74,10 @@ local function compute_folds_levels(bufnr, info, srow, erow, parse_injections) srow = srow or 0 erow = erow or api.nvim_buf_line_count(bufnr) - local parser = assert(ts.get_parser(bufnr, nil, { error = false })) + local parser = info.parser + if not parser then + return + end parser:parse(parse_injections and { srow, erow } or nil) @@ -347,13 +355,21 @@ function M.foldexpr(lnum) lnum = lnum or vim.v.lnum local bufnr = api.nvim_get_current_buf() - local parser = ts.get_parser(bufnr, nil, { error = false }) - if not parser then - return '0' - end - if not foldinfos[bufnr] then - foldinfos[bufnr] = FoldInfo.new() + foldinfos[bufnr] = FoldInfo.new(bufnr) + api.nvim_create_autocmd('BufUnload', { + buffer = bufnr, + once = true, + callback = function() + foldinfos[bufnr] = nil + end, + }) + + local parser = foldinfos[bufnr].parser + if not parser then + return '0' + end + compute_folds_levels(bufnr, foldinfos[bufnr]) parser:register_cbs({ @@ -383,7 +399,7 @@ api.nvim_create_autocmd('OptionSet', { or foldinfos[buf] and { buf } or {} for _, bufnr in ipairs(bufs) do - foldinfos[bufnr] = FoldInfo.new() + foldinfos[bufnr] = FoldInfo.new(bufnr) api.nvim_buf_call(bufnr, function() compute_folds_levels(bufnr, foldinfos[bufnr]) end) -- cgit From b12b91c2743954dbe8599caa60e58e5d74aa4e76 Mon Sep 17 00:00:00 2001 From: glepnir Date: Wed, 8 Jan 2025 00:09:01 +0800 Subject: feat(health): show :checkhealth in floating window #31086 Problem: health can not shown in a floating window Solution: add g:health variable --- runtime/lua/vim/health.lua | 41 +++++++++++++++++++++++++++++++++++------ runtime/lua/vim/lsp/util.lua | 8 +++++--- 2 files changed, 40 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 01166628b1..6dc902489f 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -11,6 +11,17 @@ --- < --- Plugin authors are encouraged to write new healthchecks. |health-dev| --- +--- *g:health* +--- g:health This global variable controls the behavior and appearance of the +--- `health` floating window. It should be a dictionary containing the +--- following optional keys: +--- - `style`: string? Determines the display style of the `health` window. +--- Set to `'float'` to enable a floating window. Other +--- styles are not currently supported. +--- +--- Example: >lua +--- vim.g.health = { style = 'float' } +--- --- Commands *health-commands* --- --- *:che* *:checkhealth* @@ -331,13 +342,31 @@ function M._check(mods, plugin_names) local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$') - -- When no command modifiers are used: - -- - If the current buffer is empty, open healthcheck directly. - -- - If not specified otherwise open healthcheck in a tab. - local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer' - local bufnr = vim.api.nvim_create_buf(true, true) - vim.cmd(buf_cmd .. ' ' .. bufnr) + if + vim.g.health + and type(vim.g.health) == 'table' + and vim.tbl_get(vim.g.health, 'style') == 'float' + then + local max_height = math.floor(vim.o.lines * 0.8) + local max_width = 80 + local float_bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', { + height = max_height, + width = max_width, + offset_x = math.floor((vim.o.columns - max_width) / 2), + offset_y = math.floor((vim.o.lines - max_height) / 2) - 1, + relative = 'editor', + }) + vim.api.nvim_set_current_win(float_winid) + vim.bo[float_bufnr].modifiable = true + vim.wo[float_winid].list = false + else + -- When no command modifiers are used: + -- - If the current buffer is empty, open healthcheck directly. + -- - If not specified otherwise open healthcheck in a tab. + local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer' + vim.cmd(buf_cmd .. ' ' .. bufnr) + end if vim.fn.bufexists('health://') == 1 then vim.cmd.bwipe('health://') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 89b774816b..5cccb3321f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -875,11 +875,13 @@ function M.make_floating_popup_options(width, height, opts) return { anchor = anchor, + row = row + (opts.offset_y or 0), col = col + (opts.offset_x or 0), height = height, focusable = opts.focusable, - relative = opts.relative == 'mouse' and 'mouse' or 'cursor', - row = row + (opts.offset_y or 0), + relative = opts.relative == 'mouse' and 'mouse' + or opts.relative == 'editor' and 'editor' + or 'cursor', style = 'minimal', width = width, border = opts.border or default_border, @@ -1494,7 +1496,7 @@ end --- @field title_pos? 'left'|'center'|'right' --- --- (default: `'cursor'`) ---- @field relative? 'mouse'|'cursor' +--- @field relative? 'mouse'|'cursor'|'editor' --- --- - "auto": place window based on which side of the cursor has more lines --- - "above": place the window above the cursor unless there are not enough lines -- cgit From 561580aba5307cbd4d4a6c00cb39048831109bc7 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 7 Jan 2025 21:32:55 +0100 Subject: vim-patch:9.1.0995: filetype: shaderslang files are not detected Problem: filetype: shaderslang files are not detected Solution: detect '*.slang' files as shaderslang filetype, include a filetype and syntax script (mtvare6) Reference: https://shader-slang.com/ closes: vim/vim#16387 https://github.com/vim/vim/commit/616219f684744bcfad61a53c13166cda9b141dea Co-authored-by: mtvare6 --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index b6d6906589..033a04cdc7 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1097,6 +1097,7 @@ local extension = { la = 'sh', lai = 'sh', mdd = 'sh', + slang = 'shaderslang', sieve = 'sieve', siv = 'sieve', sig = detect.sig, -- cgit From 5b9518b43663f9e77e5f041006df921350bf5061 Mon Sep 17 00:00:00 2001 From: notomo <18519692+notomo@users.noreply.github.com> Date: Wed, 8 Jan 2025 21:37:29 +0900 Subject: fix(api): nvim_set_decoration_provider callback return type #31912 Problem: incorrect return type doc causes luals `Annotations specify that at most 0 return value(s) are required, found 1 returned here instead.` diagnosis Solution: correct return type doc --- runtime/lua/vim/_meta/api_keysets.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index e11dddb2d3..c08ab0663b 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -227,10 +227,10 @@ error('Cannot require a meta file') --- @field do_source? boolean --- @class vim.api.keyset.set_decoration_provider ---- @field on_start? fun(_: "start", tick: integer) +--- @field on_start? fun(_: "start", tick: integer): boolean? --- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer) ---- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer) ---- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer) +--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer): boolean? +--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer): boolean? --- @field on_end? fun(_: "end", tick: integer) --- @field _on_hl_def? fun(_: "hl_def") --- @field _on_spell_nav? fun(_: "spell_nav") -- cgit From 3f0adf90debb35b5a937480151a659d654106ff6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Jan 2025 17:30:26 +0800 Subject: vim-patch:9.1.0998: filetype: TI assembly files are not recognized (#31929) Problem: filetype: TI assembly files are not recognized Solution: inspect '*.sa' and assembly files and detect TI assembly files, include filetype plugin and syntax script for TI assembly files (Wu, Zhenyu) closes: vim/vim#15827 https://github.com/vim/vim/commit/4f73c07abff420bad9fa5befc2c284c00b984993 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 2 +- runtime/lua/vim/filetype/detect.lua | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 033a04cdc7..dee1bd88ca 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1064,11 +1064,11 @@ local extension = { builder = 'ruby', rake = 'ruby', rs = 'rust', + sa = detect.sa, sage = 'sage', sls = 'salt', sas = 'sas', sass = 'sass', - sa = 'sather', sbt = 'sbt', scala = 'scala', ss = 'scheme', diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index a1c17bc1af..2d989fdbac 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -34,6 +34,12 @@ local matchregex = vim.filetype._matchregex -- can be detected from the first five lines of the file. --- @type vim.filetype.mapfn function M.asm(path, bufnr) + -- tiasm uses `* commment` + local lines = table.concat(getlines(bufnr, 1, 10), '\n') + if findany(lines, { '^%*', '\n%*', 'Texas Instruments Incorporated' }) then + return 'tiasm' + end + local syntax = vim.b[bufnr].asmsyntax if not syntax or syntax == '' then syntax = M.asm_syntax(path, bufnr) @@ -1424,6 +1430,15 @@ function M.sig(_, bufnr) end end +--- @type vim.filetype.mapfn +function M.sa(_, bufnr) + local lines = table.concat(getlines(bufnr, 1, 4), '\n') + if findany(lines, { '^;', '\n;' }) then + return 'tiasm' + end + return 'sather' +end + -- This function checks the first 25 lines of file extension "sc" to resolve -- detection between scala and SuperCollider --- @type vim.filetype.mapfn -- cgit From ead5683ff9994c0fbfc6c38e0911d9455777550b Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Tue, 7 Jan 2025 14:20:45 +0100 Subject: feat(api): add err field to nvim_echo() opts Problem: We want to deprecate `nvim_err_write(ln)()` but there is no obvious replacement (from Lua). Meanwhile we already have `nvim_echo()` with an `opts` argument. Solution: Add `err` argument to `nvim_echo()` that directly maps to `:echoerr`. --- runtime/lua/vim/_meta/api.lua | 2 ++ runtime/lua/vim/_meta/api_keysets.lua | 1 + 2 files changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 7297c8ad38..b0651efca4 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1104,6 +1104,8 @@ function vim.api.nvim_del_var(name) end --- `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. +--- - err: Treat the message like `:echoerr`. Omitted `hlgroup` +--- uses `hl-ErrorMsg` instead. --- - verbose: Message is printed as a result of 'verbose' option. --- If Nvim was invoked with -V3log_file, the message will be --- redirected to the log_file and suppressed from direct output. diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index e11dddb2d3..1baae2fd71 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -88,6 +88,7 @@ error('Cannot require a meta file') --- @field pattern? string|string[] --- @class vim.api.keyset.echo_opts +--- @field err? boolean --- @field verbose? boolean --- @class vim.api.keyset.empty -- cgit From 5c92b40b4b173c7d85106689fef811e41994abb0 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Tue, 7 Jan 2025 16:38:29 +0100 Subject: feat(api): deprecate nvim_out/err_write(ln) --- runtime/lua/vim/_defaults.lua | 2 +- runtime/lua/vim/_editor.lua | 10 +++------- runtime/lua/vim/_meta/api.lua | 19 ++++++------------- runtime/lua/vim/lsp/client.lua | 6 +++--- runtime/lua/vim/lsp/handlers.lua | 5 ++--- 5 files changed, 15 insertions(+), 27 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 4216a2acb7..d71116117e 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -224,7 +224,7 @@ do local function cmd(opts) local ok, err = pcall(vim.api.nvim_cmd, opts, {}) if not ok then - vim.api.nvim_err_writeln(err:sub(#'Vim:' + 1)) + vim.api.nvim_echo({ { err:sub(#'Vim:' + 1) } }, true, { err = true }) end end diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 44f17b3f85..66815a967e 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -58,6 +58,7 @@ vim._extra = { --- @private vim.log = { + --- @enum vim.log.levels levels = { TRACE = 0, DEBUG = 1, @@ -620,13 +621,8 @@ end ---@param opts table|nil Optional parameters. Unused by default. ---@diagnostic disable-next-line: unused-local function vim.notify(msg, level, opts) -- luacheck: no unused args - if level == vim.log.levels.ERROR then - vim.api.nvim_err_writeln(msg) - elseif level == vim.log.levels.WARN then - vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, {}) - else - vim.api.nvim_echo({ { msg } }, true, {}) - end + local chunks = { { msg, level == vim.log.levels.WARN and 'WarningMsg' or nil } } + vim.api.nvim_echo(chunks, true, { err = level == vim.log.levels.ERROR }) end do diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index b0651efca4..f6d8153c27 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1111,17 +1111,12 @@ function vim.api.nvim_del_var(name) end --- redirected to the log_file and suppressed from direct output. function vim.api.nvim_echo(chunks, history, opts) end ---- Writes a message to the Vim error buffer. Does not append "\n", the ---- message is buffered (won't display) until a linefeed is written. ---- ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_err_write(str) end ---- Writes a message to the Vim error buffer. Appends "\n", so the buffer is ---- flushed (and displayed). ---- ---- @see vim.api.nvim_err_write ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_err_writeln(str) end --- Evaluates a Vimscript `expression`. Dicts and Lists are recursively expanded. @@ -1861,10 +1856,8 @@ function vim.api.nvim_open_term(buffer, opts) end --- @return integer # Window handle, or 0 on error function vim.api.nvim_open_win(buffer, enter, config) end ---- Writes a message to the Vim output buffer. Does not append "\n", the ---- message is buffered (won't display) until a linefeed is written. ---- ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_out_write(str) end --- Parse command line. diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 5d11312c77..a99363d3d6 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -702,14 +702,14 @@ local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'err --- --- @param ... string List to write to the buffer local function err_message(...) - local message = table.concat(vim.iter({ ... }):flatten():totable()) + local chunks = { { table.concat({ ... }) } } if vim.in_fast_event() then vim.schedule(function() - api.nvim_err_writeln(message) + vim.api.nvim_echo(chunks, true, { err = true }) api.nvim_command('redraw') end) else - api.nvim_err_writeln(message) + vim.api.nvim_echo(chunks, true, { err = true }) api.nvim_command('redraw') end end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 1945040bda..3779c342e8 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -582,9 +582,8 @@ NSC['window/showMessage'] = function(_, params, ctx) if message_type == protocol.MessageType.Error then err_message('LSP[', client_name, '] ', message) else - --- @type string - local message_type_name = protocol.MessageType[message_type] - api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) + message = ('LSP[%s][%s] %s\n'):format(client_name, protocol.MessageType[message_type], message) + api.nvim_echo({ { message } }, true, { err = true }) end return params end -- cgit From 0c296ab22484b4c009d119908d1614a6c6d96b2c Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Thu, 9 Jan 2025 08:36:16 -0800 Subject: feat(docs): "yxx" runs Lua/Vimscript code examples #31904 `yxx` in Normal mode over a Lua or Vimscript code block section will execute the code. Co-authored-by: Justin M. Keyes --- runtime/lua/vim/shared.lua | 4 ++-- runtime/lua/vim/treesitter/query.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 24c3f243e5..02b12490af 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -959,7 +959,7 @@ do --- function vim.startswith(s, prefix) --- vim.validate('s', s, 'string') --- vim.validate('prefix', prefix, 'string') - --- ... + --- -- ... --- end --- ``` --- @@ -979,7 +979,7 @@ do --- age={age, 'number'}, --- hobbies={hobbies, 'table'}, --- } - --- ... + --- -- ... --- end --- ``` --- diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 1fc001b39f..b9bcbe9a80 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -292,7 +292,7 @@ end) --- - `captures`: a list of unique capture names defined in the query (alias: `info.captures`). --- - `info.patterns`: information about predicates. --- ---- Example (select the code then run `:'<,'>lua` to try it): +--- Example (to try it, use `yxx` or select the code then run `:'<,'>lua`): --- ```lua --- local query = vim.treesitter.query.parse('vimdoc', [[ --- ; query @@ -983,7 +983,7 @@ end --- -- `node` was captured by the `name` capture in the match --- --- local node_data = metadata[id] -- Node level metadata ---- ... use the info here ... +--- -- ... use the info here ... --- end --- end --- end -- cgit From 7c00e0efbb18e8627ac59eaadf564a9f1b2bafcd Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 9 Jan 2025 09:26:45 -0800 Subject: docs: misc #31867 --- runtime/lua/vim/_editor.lua | 4 ++-- runtime/lua/vim/_meta/api.lua | 37 +++++++++++++++++++++++++------------ runtime/lua/vim/health.lua | 30 ++++++++++++++++++------------ runtime/lua/vim/lsp/util.lua | 3 +-- 4 files changed, 46 insertions(+), 28 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 66815a967e..d4e6280b06 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -391,7 +391,7 @@ end local VIM_CMD_ARG_MAX = 20 ---- Executes Vim script commands. +--- Executes Vimscript (|Ex-commands|). --- --- Note that `vim.cmd` can be indexed with a command name to return a callable function to the --- command. @@ -426,7 +426,7 @@ local VIM_CMD_ARG_MAX = 20 --- ``` --- ---@param command string|table Command(s) to execute. ---- If a string, executes multiple lines of Vim script at once. In this +--- If a string, executes multiple lines of Vimscript at once. In this --- case, it is an alias to |nvim_exec2()|, where `opts.output` is set --- to false. Thus it works identical to |:source|. --- If a table, executes a single command. In this case, it is an alias diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index f6d8153c27..b5d8a0937d 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1097,18 +1097,20 @@ function vim.api.nvim_del_user_command(name) end --- @param name string Variable name function vim.api.nvim_del_var(name) end ---- Echo a message. +--- Prints a message given by a list of `[text, hl_group]` "chunks". --- ---- @param chunks any[] A list of `[text, hl_group]` arrays, each representing a ---- text chunk with specified highlight group name or ID. ---- `hl_group` element can be omitted for no highlight. +--- Example: +--- ```lua +--- vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {}) +--- ``` +--- +--- @param chunks any[] List of `[text, hl_group]` pairs, where each is a `text` string highlighted by +--- the (optional) name or ID `hl_group`. --- @param history boolean if true, add to `message-history`. --- @param opts vim.api.keyset.echo_opts Optional parameters. ---- - err: Treat the message like `:echoerr`. Omitted `hlgroup` ---- uses `hl-ErrorMsg` instead. ---- - verbose: Message is printed as a result of 'verbose' option. ---- If Nvim was invoked with -V3log_file, the message will be ---- redirected to the log_file and suppressed from direct output. +--- - err: Treat the message like `:echoerr`. Sets `hl_group` to `hl-ErrorMsg` by default. +--- - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log` +--- will write the message to the "log" file instead of standard output. function vim.api.nvim_echo(chunks, history, opts) end --- @deprecated @@ -1276,6 +1278,8 @@ function vim.api.nvim_get_autocmds(opts) end --- Gets information about a channel. --- +--- See `nvim_list_uis()` for an example of how to get channel info. +--- --- @param chan integer channel_id, or 0 for current channel --- @return table # Channel info dict with these keys: --- - "id" Channel id. @@ -1293,8 +1297,8 @@ function vim.api.nvim_get_autocmds(opts) end --- "/dev/pts/1". If unknown, the key will still be present if a pty is used (e.g. --- for conpty on Windows). --- - "buffer" (optional) Buffer connected to |terminal| instance. ---- - "client" (optional) Info about the peer (client on the other end of the RPC channel), ---- which it provided via |nvim_set_client_info()|. +--- - "client" (optional) Info about the peer (client on the other end of the channel), as set +--- by |nvim_set_client_info()|. --- function vim.api.nvim_get_chan_info(chan) end @@ -1616,6 +1620,14 @@ function vim.api.nvim_list_tabpages() end --- Gets a list of dictionaries representing attached UIs. --- +--- Example: The Nvim builtin `TUI` sets its channel info as described in `startup-tui`. In +--- particular, it sets `client.name` to "nvim-tui". So you can check if the TUI is running by +--- inspecting the client name of each UI: +--- +--- ```lua +--- vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name) +--- ``` +--- --- @return any[] # Array of UI dictionaries, each with these keys: --- - "height" Requested height of the UI --- - "width" Requested width of the UI @@ -1661,7 +1673,8 @@ function vim.api.nvim_notify(msg, log_level, opts) end --- in a virtual terminal having the intended size. --- --- Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you ---- can use Nvim as a "scrollback pager" (for terminals like kitty): [terminal-scrollback-pager]() +--- can use Nvim as a "scrollback pager" (for terminals like kitty): [ansi-colorize]() +--- [terminal-scrollback-pager]() --- --- ```lua --- vim.api.nvim_create_user_command('TermHl', function() diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 6dc902489f..ee376f3a11 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -11,18 +11,7 @@ --- < --- Plugin authors are encouraged to write new healthchecks. |health-dev| --- ---- *g:health* ---- g:health This global variable controls the behavior and appearance of the ---- `health` floating window. It should be a dictionary containing the ---- following optional keys: ---- - `style`: string? Determines the display style of the `health` window. ---- Set to `'float'` to enable a floating window. Other ---- styles are not currently supported. ---- ---- Example: >lua ---- vim.g.health = { style = 'float' } ---- ---- Commands *health-commands* +--- COMMANDS *health-commands* --- --- *:che* *:checkhealth* --- :che[ckhealth] Run all healthchecks. @@ -50,6 +39,23 @@ --- :checkhealth vim* --- < --- +--- USAGE *health-usage* +--- +--- Local mappings in the healthcheck buffer: +--- +--- q Closes the window. +--- +--- Global configuration: +--- +--- *g:health* +--- g:health Dictionary with the following optional keys: +--- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in +--- a floating window instead of the default behavior. +--- +--- Example: >lua +--- vim.g.health = { style = 'float' } +--- +--- -------------------------------------------------------------------------------- --- Create a healthcheck *health-dev* --- --- Healthchecks are functions that check the user environment, configuration, or diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 5cccb3321f..ccd68f0fdf 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -879,8 +879,7 @@ function M.make_floating_popup_options(width, height, opts) col = col + (opts.offset_x or 0), height = height, focusable = opts.focusable, - relative = opts.relative == 'mouse' and 'mouse' - or opts.relative == 'editor' and 'editor' + relative = (opts.relative == 'mouse' or opts.relative == 'editor') and opts.relative or 'cursor', style = 'minimal', width = width, -- cgit From 0717dfbfaf36887dab277527eb0a93bf2182297c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 10 Jan 2025 11:42:04 -0800 Subject: refactor(api): deprecate nvim_notify #31938 Problem: The `nvim_notify` API (note: unrelated to `vim.notify()` Lua API) was not given any real motivation in https://github.com/neovim/neovim/pull/13843 There are, and were, idiomatic and ergonomic alternatives already. Solution: Deprecate `nvim_notify`. --- runtime/lua/vim/_meta/api.lua | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index b5d8a0937d..8930f131f6 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1647,14 +1647,10 @@ function vim.api.nvim_list_wins() end --- @return any function vim.api.nvim_load_context(dict) end ---- Notify the user with a message ---- ---- Relays the call to vim.notify . By default forwards your message in the ---- echo area but can be overridden to trigger desktop notifications. ---- ---- @param msg string Message to display to the user ---- @param log_level integer The log level ---- @param opts table Reserved for future use. +--- @deprecated +--- @param msg string +--- @param log_level integer +--- @param opts table --- @return any function vim.api.nvim_notify(msg, log_level, opts) end -- cgit From cb02c20569b56545a1657d4f7f8f29171f1037d7 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Fri, 10 Jan 2025 12:25:46 -0800 Subject: refactor(treesitter.foldexpr): remove unused parse_injections parameter --- runtime/lua/vim/treesitter/_fold.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index d16013eca2..7f1d1b14d5 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -69,8 +69,7 @@ end ---@param info TS.FoldInfo ---@param srow integer? ---@param erow integer? 0-indexed, exclusive ----@param parse_injections? boolean -local function compute_folds_levels(bufnr, info, srow, erow, parse_injections) +local function compute_folds_levels(bufnr, info, srow, erow) srow = srow or 0 erow = erow or api.nvim_buf_line_count(bufnr) @@ -79,7 +78,7 @@ local function compute_folds_levels(bufnr, info, srow, erow, parse_injections) return end - parser:parse(parse_injections and { srow, erow } or nil) + parser:parse() local enter_counts = {} ---@type table local leave_counts = {} ---@type table -- cgit From aa2b44fbb07f3ab4dd00ea4a3ae7c5d31bc20a9d Mon Sep 17 00:00:00 2001 From: Guilherme Soares <48023091+guilhas07@users.noreply.github.com> Date: Fri, 10 Jan 2025 22:46:19 +0000 Subject: fix(treesitter): don't return error message on success #31955 Problem: The `vim.treesitter.language.add` function returns a error message even when it succeeds. Solution: Don't return error message on success. --- runtime/lua/vim/treesitter/language.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 446051dfd7..238a078703 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -133,8 +133,9 @@ function M.add(lang, opts) path = paths[1] end - return loadparser(path, lang, symbol_name) or nil, - string.format('Cannot load parser %s for language "%s"', path, lang) + local res = loadparser(path, lang, symbol_name) + return res, + res == nil and string.format('Cannot load parser %s for language "%s"', path, lang) or nil end --- @param x string|string[] -- cgit From 6a425e7045cca609d95612c0f2cd08d0265238a9 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 24 Nov 2024 11:29:39 +0100 Subject: docs: misc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Axel Co-authored-by: Colin Kennedy Co-authored-by: Daiki Noda Co-authored-by: Evgeni Chasnovski Co-authored-by: Jean-Jacq du Plessis <1030058+jj-du-plessis@users.noreply.github.com> Co-authored-by: Juan Giordana Co-authored-by: Lincoln Wallace Co-authored-by: Matti Hellström Co-authored-by: Steven Locorotondo Co-authored-by: Yochem van Rosmalen Co-authored-by: glepnir Co-authored-by: ifish --- runtime/lua/vim/_editor.lua | 2 +- runtime/lua/vim/_meta/options.lua | 4 ++-- runtime/lua/vim/_meta/vimfn.lua | 2 +- runtime/lua/vim/_options.lua | 2 +- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/diagnostic.lua | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index d4e6280b06..4b28b63746 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -93,7 +93,7 @@ local utfs = { --- --- -- Runs synchronously: --- local obj = vim.system({'echo', 'hello'}, { text = true }):wait() ---- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' } +--- -- { code = 0, signal = 0, stdout = 'hello\n', stderr = '' } --- --- ``` --- diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index e5cea884c5..940441a849 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -1087,8 +1087,8 @@ vim.go.cia = vim.go.completeitemalign --- "menu" or "menuone". No effect if "longest" is present. --- --- noselect Same as "noinsert", except that no menu item is ---- pre-selected. If both "noinsert" and "noselect" are present, ---- "noselect" has precedence. +--- pre-selected. If both "noinsert" and "noselect" are +--- present, "noselect" has precedence. --- --- fuzzy Enable `fuzzy-matching` for completion candidates. This --- allows for more flexible and intuitive matching, where diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 031b109b38..6316ab2bfc 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -7537,7 +7537,7 @@ function vim.fn.screenstring(row, col) end --- @param stopline? integer --- @param timeout? integer --- @param skip? string|function ---- @return any +--- @return integer function vim.fn.search(pattern, flags, stopline, timeout, skip) end --- Get or update the last search count, like what is displayed diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua index dc37595578..8338c5ead7 100644 --- a/runtime/lua/vim/_options.lua +++ b/runtime/lua/vim/_options.lua @@ -768,7 +768,7 @@ end --- --- --- A special interface |vim.opt| exists for conveniently interacting with list- ---- and map-style option from Lua: It allows accessing them as Lua tables and +--- and map-style options from Lua: It allows accessing them as Lua tables and --- offers object-oriented method for adding and removing entries. --- --- Examples: ~ diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5b92926a21..23f4e104d0 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1389,7 +1389,7 @@ end --- |LspAttach| autocommand. Example: --- --- ```lua ---- vim.api.nvim_create_autocommand('LspAttach', { +--- vim.api.nvim_create_autocmd('LspAttach', { --- callback = function(args) --- local client = vim.lsp.get_client_by_id(args.data.client_id) --- if client:supports_method('textDocument/foldingRange') then diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 9a879d9f38..8c1f3f10d4 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -208,7 +208,7 @@ end --- @param uri string --- @param client_id? integer ---- @param diagnostics vim.Diagnostic[] +--- @param diagnostics lsp.Diagnostic[] --- @param is_pull boolean local function handle_diagnostics(uri, client_id, diagnostics, is_pull) local fname = vim.uri_to_fname(uri) -- cgit From 9e0d40f7e45f483e54d38be1266f63240808b4b0 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 11 Jan 2025 10:58:10 +0100 Subject: vim-patch:668e9f2: runtime(filetype): don't detect string interpolation as angular fixes: vim/vim#16375 https://github.com/vim/vim/commit/668e9f24037fc7c362ffdf5fc1d5c5b1a8b0e855 Co-authored-by: Christian Brabandt --- runtime/lua/vim/filetype/detect.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 2d989fdbac..30a9951f6a 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -757,7 +757,7 @@ function M.html(_, bufnr) if matchregex( line, - [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content\|{{.*}}]] + [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content]] ) then return 'htmlangular' -- cgit From a119aae4d3deebea453ccd7c2dea814aed2c9f21 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Sat, 11 Jan 2025 12:54:43 -0600 Subject: feat(diagnostic)!: filter diagnostics by severity before passing to handlers (#30070) BREAKING CHANGE: This changes the list of diagnostics that are passed to a diagnostic handler. If a handler is already filtering by severity itself then this won't break anything, since the handler's filtering will become a no-op. But handlers which depend on receiving the full list of diagnostics may break. Note that diagnostics are only filtered if the handler's configuration has the `severity` option set. If `severity` is not set, the handler still receives the full list of diagnostics. --- runtime/lua/vim/diagnostic.lua | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 90f967fe79..6466c7d6e8 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1395,10 +1395,6 @@ M.handlers.signs = { return end - if opts.signs and opts.signs.severity then - diagnostics = filter_by_severity(opts.signs.severity, diagnostics) - end - -- 10 is the default sign priority when none is explicitly specified local priority = opts.signs and opts.signs.priority or 10 local get_priority = severity_to_extmark_priority(priority, opts) @@ -1501,10 +1497,6 @@ M.handlers.underline = { return end - if opts.underline and opts.underline.severity then - diagnostics = filter_by_severity(opts.underline.severity, diagnostics) - end - local ns = M.get_namespace(namespace) if not ns.user_data.underline_ns then ns.user_data.underline_ns = @@ -1565,7 +1557,6 @@ M.handlers.virtual_text = { return end - local severity --- @type vim.diagnostic.SeverityFilter? if opts.virtual_text then if opts.virtual_text.format then diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics) @@ -1576,9 +1567,6 @@ M.handlers.virtual_text = { then diagnostics = prefix_source(diagnostics) end - if opts.virtual_text.severity then - severity = opts.virtual_text.severity - end end local ns = M.get_namespace(namespace) @@ -1590,9 +1578,6 @@ M.handlers.virtual_text = { local virt_text_ns = ns.user_data.virt_text_ns local buffer_line_diagnostics = diagnostic_lines(diagnostics) for line, line_diagnostics in pairs(buffer_line_diagnostics) do - if severity then - line_diagnostics = filter_by_severity(severity, line_diagnostics) - end local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text) if virt_texts then @@ -1797,7 +1782,8 @@ function M.show(namespace, bufnr, diagnostics, opts) for handler_name, handler in pairs(M.handlers) do if handler.show and opts_res[handler_name] then - handler.show(namespace, bufnr, diagnostics, opts_res) + local filtered = filter_by_severity(opts_res[handler_name].severity, diagnostics) + handler.show(namespace, bufnr, filtered, opts_res) end end end -- cgit From bf58b757c4c2e1bf2a4afe4279150ca68a68691f Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 12 Jan 2025 11:42:02 +0100 Subject: vim-patch:9.1.1007: filetype: various ignore are not recognized Problem: filetype: various ignore are not recognized Solution: detect rg/docker/npm/vvsce ignore files as 'gitgnore' filetype (Wu, Zhenyu) Not only prettier, but many programs also support ignore files (like rg, docker, npm, vscode). So use the gitignore filetype for them due to same syntax closes: vim/vim#16428 https://github.com/vim/vim/commit/8cbe2e0a0a78f57bb545a97695bfedd6a95e6992 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index dee1bd88ca..bc866db399 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1574,6 +1574,11 @@ local filename = { ['.gitmodules'] = 'gitconfig', ['.gitattributes'] = 'gitattributes', ['.gitignore'] = 'gitignore', + ['.ignore'] = 'gitignore', + ['.dockerignore'] = 'gitignore', + ['.npmignore'] = 'gitignore', + ['.rgignore'] = 'gitignore', + ['.vscodeignore'] = 'gitignore', ['gitolite.conf'] = 'gitolite', ['git-rebase-todo'] = 'gitrebase', gkrellmrc = 'gkrellmrc', -- cgit From 3fdc4302415972eb5d98ba832372236be3d22572 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Sat, 11 Jan 2025 15:44:07 -0800 Subject: perf(treesitter): cache queries strongly **Problem:** Query parsing uses a weak cache which is invalidated frequently **Solution:** Make the cache strong, and invalidate it manually when necessary (that is, when `rtp` is changed or `query.set()` is called) Co-authored-by: Christian Clason --- runtime/lua/vim/treesitter/query.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index b9bcbe9a80..b0b0fecd38 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -262,6 +262,7 @@ local explicit_queries = setmetatable({}, { ---@param query_name string Name of the query (e.g., "highlights") ---@param text string Query text (unparsed). function M.set(lang, query_name, text) + M.get:clear(lang, query_name) explicit_queries[lang][query_name] = M.parse(lang, text) end @@ -284,7 +285,15 @@ M.get = memoize('concat-2', function(lang, query_name) end return M.parse(lang, query_string) -end) +end, false) + +api.nvim_create_autocmd('OptionSet', { + pattern = { 'runtimepath' }, + group = api.nvim_create_augroup('ts_query_cache_reset', { clear = true }), + callback = function() + M.get:clear() + end, +}) --- Parses a {query} string and returns a `Query` object (|lua-treesitter-query|), which can be used --- to search the tree for the query patterns (via |Query:iter_captures()|, |Query:iter_matches()|), @@ -316,7 +325,7 @@ M.parse = memoize('concat-2', function(lang, query) assert(language.add(lang)) local ts_query = vim._ts_parse_query(lang, query) return Query.new(lang, ts_query) -end) +end, false) --- Implementations of predicates that can optionally be prefixed with "any-". --- -- cgit From 45e606b1fddbfeee8fe28385b5371ca6f2fba71b Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Wed, 18 Dec 2024 10:48:33 -0800 Subject: feat(treesitter): async parsing **Problem:** Parsing can be slow for large files, and it is a blocking operation which can be disruptive and annoying. **Solution:** Provide a function for asynchronous parsing, which accepts a callback to be run after parsing completes. Co-authored-by: Lewis Russell Co-authored-by: Luuk van Baal Co-authored-by: VanaIgr --- runtime/lua/vim/_meta/options.lua | 4 +- runtime/lua/vim/treesitter.lua | 8 +- runtime/lua/vim/treesitter/highlighter.lua | 19 ++-- runtime/lua/vim/treesitter/languagetree.lua | 143 ++++++++++++++++++++++++++-- runtime/lua/vim/treesitter/query.lua | 14 ++- 5 files changed, 163 insertions(+), 25 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 940441a849..c9871c8660 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -4845,8 +4845,8 @@ vim.go.redrawdebug = vim.o.redrawdebug vim.go.rdb = vim.go.redrawdebug --- Time in milliseconds for redrawing the display. Applies to ---- 'hlsearch', 'inccommand', `:match` highlighting and syntax ---- highlighting. +--- 'hlsearch', 'inccommand', `:match` highlighting, syntax highlighting, +--- and async `LanguageTree:parse()`. --- When redrawing takes more than this many milliseconds no further --- matches will be highlighted. --- For syntax highlighting the time applies per window. When over the diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 89dc4e289a..9b7c8233d8 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -61,7 +61,7 @@ function M._create_parser(bufnr, lang, opts) { on_bytes = bytes_cb, on_detach = detach_cb, on_reload = reload_cb, preview = true } ) - self:parse() + self:parse(nil, function() end) return self end @@ -397,6 +397,8 @@ end --- Note: By default, disables regex syntax highlighting, which may be required for some plugins. --- In this case, add `vim.bo.syntax = 'on'` after the call to `start`. --- +--- Note: By default, the highlighter parses code asynchronously, using a segment time of 3ms. +--- --- Example: --- --- ```lua @@ -408,8 +410,8 @@ end --- }) --- ``` --- ----@param bufnr (integer|nil) Buffer to be highlighted (default: current buffer) ----@param lang (string|nil) Language of the parser (default: from buffer filetype) +---@param bufnr integer? Buffer to be highlighted (default: current buffer) +---@param lang string? Language of the parser (default: from buffer filetype) function M.start(bufnr, lang) bufnr = vim._resolve_bufnr(bufnr) local parser = assert(M.get_parser(bufnr, lang, { error = false })) diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 96503c38ea..04e6ee8a9e 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -69,6 +69,7 @@ end ---@field private _queries table ---@field tree vim.treesitter.LanguageTree ---@field private redraw_count integer +---@field parsing boolean true if we are parsing asynchronously local TSHighlighter = { active = {}, } @@ -147,7 +148,7 @@ function TSHighlighter.new(tree, opts) vim.opt_local.spelloptions:append('noplainbuffer') end) - self.tree:parse() + self.tree:parse(nil, function() end) return self end @@ -384,19 +385,23 @@ function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _) end ---@private ----@param _win integer ---@param buf integer ---@param topline integer ---@param botline integer -function TSHighlighter._on_win(_, _win, buf, topline, botline) +function TSHighlighter._on_win(_, _, buf, topline, botline) local self = TSHighlighter.active[buf] - if not self then + if not self or self.parsing then return false end - self.tree:parse({ topline, botline + 1 }) - self:prepare_highlight_states(topline, botline + 1) + self.parsing = self.tree:parse({ topline, botline + 1 }, function(_, trees) + if trees and self.parsing then + self.parsing = false + api.nvim__redraw({ buf = buf, valid = false, flush = false }) + end + end) == nil self.redraw_count = self.redraw_count + 1 - return true + self:prepare_highlight_states(topline, botline) + return #self._highlight_states > 0 end api.nvim_set_decoration_provider(ns, { diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 330eb45749..945a2301a9 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -44,6 +44,8 @@ local query = require('vim.treesitter.query') local language = require('vim.treesitter.language') local Range = require('vim.treesitter._range') +local default_parse_timeout_ms = 3 + ---@alias TSCallbackName ---| 'changedtree' ---| 'bytes' @@ -76,6 +78,10 @@ local TSCallbackNames = { ---@field private _injections_processed boolean ---@field private _opts table Options ---@field private _parser TSParser Parser for language +---Table of regions for which the tree is currently running an async parse +---@field private _ranges_being_parsed table +---Table of callback queues, keyed by each region for which the callbacks should be run +---@field private _cb_queues table)[]> ---@field private _has_regions boolean ---@field private _regions table? ---List of regions this tree should manage and parse. If nil then regions are @@ -130,6 +136,8 @@ function LanguageTree.new(source, lang, opts) _injections_processed = false, _valid = false, _parser = vim._create_ts_parser(lang), + _ranges_being_parsed = {}, + _cb_queues = {}, _callbacks = {}, _callbacks_rec = {}, } @@ -232,6 +240,7 @@ end ---@param reload boolean|nil function LanguageTree:invalidate(reload) self._valid = false + self._parser:reset() -- buffer was reloaded, reparse all trees if reload then @@ -334,10 +343,12 @@ end --- @private --- @param range boolean|Range? +--- @param timeout integer? --- @return Range6[] changes --- @return integer no_regions_parsed --- @return number total_parse_time -function LanguageTree:_parse_regions(range) +--- @return boolean finished whether async parsing still needs time +function LanguageTree:_parse_regions(range, timeout) local changes = {} local no_regions_parsed = 0 local total_parse_time = 0 @@ -357,9 +368,14 @@ function LanguageTree:_parse_regions(range) ) then self._parser:set_included_ranges(ranges) + self._parser:set_timeout(timeout and timeout * 1000 or 0) -- ms -> micros local parse_time, tree, tree_changes = tcall(self._parser.parse, self._parser, self._trees[i], self._source, true) + if not tree then + return changes, no_regions_parsed, total_parse_time, false + end + -- Pass ranges if this is an initial parse local cb_changes = self._trees[i] and tree_changes or tree:included_ranges(true) @@ -373,7 +389,7 @@ function LanguageTree:_parse_regions(range) end end - return changes, no_regions_parsed, total_parse_time + return changes, no_regions_parsed, total_parse_time, true end --- @private @@ -409,6 +425,82 @@ function LanguageTree:_add_injections() return query_time end +--- @param range boolean|Range? +--- @return string +local function range_to_string(range) + return type(range) == 'table' and table.concat(range, ',') or tostring(range) +end + +--- @private +--- @param range boolean|Range? +--- @param callback fun(err?: string, trees?: table) +function LanguageTree:_push_async_callback(range, callback) + local key = range_to_string(range) + self._cb_queues[key] = self._cb_queues[key] or {} + local queue = self._cb_queues[key] + queue[#queue + 1] = callback +end + +--- @private +--- @param range boolean|Range? +--- @param err? string +--- @param trees? table +function LanguageTree:_run_async_callbacks(range, err, trees) + local key = range_to_string(range) + for _, cb in ipairs(self._cb_queues[key]) do + cb(err, trees) + end + self._ranges_being_parsed[key] = false + self._cb_queues[key] = {} +end + +--- Run an asynchronous parse, calling {on_parse} when complete. +--- +--- @private +--- @param range boolean|Range? +--- @param on_parse fun(err?: string, trees?: table) +--- @return table? trees the list of parsed trees, if parsing completed synchronously +function LanguageTree:_async_parse(range, on_parse) + self:_push_async_callback(range, on_parse) + + -- If we are already running an async parse, just queue the callback. + local range_string = range_to_string(range) + if not self._ranges_being_parsed[range_string] then + self._ranges_being_parsed[range_string] = true + else + return + end + + local buf = vim.b[self._source] + local ct = buf.changedtick + local total_parse_time = 0 + local redrawtime = vim.o.redrawtime + local timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ms or nil + + local function step() + -- If buffer was changed in the middle of parsing, reset parse state + if buf.changedtick ~= ct then + ct = buf.changedtick + total_parse_time = 0 + end + + local parse_time, trees, finished = tcall(self._parse, self, range, timeout) + total_parse_time = total_parse_time + parse_time + + if finished then + self:_run_async_callbacks(range, nil, trees) + return trees + elseif total_parse_time > redrawtime then + self:_run_async_callbacks(range, 'TIMEOUT', nil) + return nil + else + vim.schedule(step) + end + end + + return step() +end + --- Recursively parse all regions in the language tree using |treesitter-parsers| --- for the corresponding languages and run injection queries on the parsed trees --- to determine whether child trees should be created and parsed. @@ -420,11 +512,33 @@ end --- Set to `true` to run a complete parse of the source (Note: Can be slow!) --- Set to `false|nil` to only parse regions with empty ranges (typically --- only the root tree without injections). ---- @return table -function LanguageTree:parse(range) +--- @param on_parse fun(err?: string, trees?: table)? Function invoked when parsing completes. +--- When provided and `vim.g._ts_force_sync_parsing` is not set, parsing will run +--- asynchronously. The first argument to the function is a string respresenting the error type, +--- in case of a failure (currently only possible for timeouts). The second argument is the list +--- of trees returned by the parse (upon success), or `nil` if the parse timed out (determined +--- by 'redrawtime'). +--- +--- If parsing was still able to finish synchronously (within 3ms), `parse()` returns the list +--- of trees. Otherwise, it returns `nil`. +--- @return table? +function LanguageTree:parse(range, on_parse) + if on_parse then + return self:_async_parse(range, on_parse) + end + local trees, _ = self:_parse(range) + return trees +end + +--- @private +--- @param range boolean|Range|nil +--- @param timeout integer? +--- @return table trees +--- @return boolean finished +function LanguageTree:_parse(range, timeout) if self:is_valid() then self:_log('valid') - return self._trees + return self._trees, true end local changes --- @type Range6[]? @@ -433,10 +547,15 @@ function LanguageTree:parse(range) local no_regions_parsed = 0 local query_time = 0 local total_parse_time = 0 + local is_finished --- @type boolean -- At least 1 region is invalid if not self:is_valid(true) then - changes, no_regions_parsed, total_parse_time = self:_parse_regions(range) + changes, no_regions_parsed, total_parse_time, is_finished = self:_parse_regions(range, timeout) + timeout = timeout and math.max(timeout - total_parse_time, 0) + if not is_finished then + return self._trees, is_finished + end -- Need to run injections when we parsed something if no_regions_parsed > 0 then self._injections_processed = false @@ -457,10 +576,17 @@ function LanguageTree:parse(range) }) for _, child in pairs(self._children) do - child:parse(range) + if timeout == 0 then + return self._trees, false + end + local ctime, _, child_finished = tcall(child._parse, child, range, timeout) + timeout = timeout and math.max(timeout - ctime, 0) + if not child_finished then + return self._trees, child_finished + end end - return self._trees + return self._trees, true end --- Invokes the callback for each |LanguageTree| recursively. @@ -907,6 +1033,7 @@ function LanguageTree:_edit( ) end + self._parser:reset() self._regions = nil local changed_range = { diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index b0b0fecd38..66ab0d52f0 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -913,8 +913,8 @@ end ---@param start? integer Starting line for the search. Defaults to `node:start()`. ---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`. --- ----@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch): ---- capture id, capture node, metadata, match +---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree): +--- capture id, capture node, metadata, match, tree --- ---@note Captures are only returned if the query pattern of a specific capture contained predicates. function Query:iter_captures(node, source, start, stop) @@ -924,6 +924,8 @@ function Query:iter_captures(node, source, start, stop) start, stop = value_or_node_range(start, stop, node) + -- Copy the tree to ensure it is valid during the entire lifetime of the iterator + local tree = node:tree():copy() local cursor = vim._create_ts_querycursor(node, self.query, start, stop, { match_limit = 256 }) -- For faster checks that a match is not in the cache. @@ -970,7 +972,7 @@ function Query:iter_captures(node, source, start, stop) match_cache[match_id] = metadata end - return capture, captured_node, metadata, match + return capture, captured_node, metadata, match, tree end return iter end @@ -1011,7 +1013,7 @@ end --- (last) node instead of the full list of matching nodes. This option is only for backward --- compatibility and will be removed in a future release. --- ----@return (fun(): integer, table, vim.treesitter.query.TSMetadata): pattern id, match, metadata +---@return (fun(): integer, table, vim.treesitter.query.TSMetadata, TSTree): pattern id, match, metadata, tree function Query:iter_matches(node, source, start, stop, opts) opts = opts or {} opts.match_limit = opts.match_limit or 256 @@ -1022,6 +1024,8 @@ function Query:iter_matches(node, source, start, stop, opts) start, stop = value_or_node_range(start, stop, node) + -- Copy the tree to ensure it is valid during the entire lifetime of the iterator + local tree = node:tree():copy() local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts) local function iter() @@ -1059,7 +1063,7 @@ function Query:iter_matches(node, source, start, stop, opts) end -- TODO(lewis6991): create a new function that returns {match, metadata} - return pattern_i, captures, metadata + return pattern_i, captures, metadata, tree end return iter end -- cgit From bd4ca22d0334a3323313dfd6975a80218ec65e36 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Fri, 20 Dec 2024 16:23:52 -0800 Subject: feat(treesitter)!: don't parse tree in get_parser() or start() **Problem:** `vim.treesitter.get_parser()` and `vim.treesitter.start()` both parse the tree before returning it. This is problematic because if this is a sync parse, it will stall the editor on large files. If it is an async parse, the functions return stale trees. **Solution:** Remove this parsing side effect and leave it to the user to parse the returned trees, either synchronously or asynchronously. --- runtime/lua/vim/treesitter.lua | 2 -- runtime/lua/vim/treesitter/highlighter.lua | 2 -- 2 files changed, 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 9b7c8233d8..0269699dfd 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -61,8 +61,6 @@ function M._create_parser(bufnr, lang, opts) { on_bytes = bytes_cb, on_detach = detach_cb, on_reload = reload_cb, preview = true } ) - self:parse(nil, function() end) - return self end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 04e6ee8a9e..be138885d5 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -148,8 +148,6 @@ function TSHighlighter.new(tree, opts) vim.opt_local.spelloptions:append('noplainbuffer') end) - self.tree:parse(nil, function() end) - return self end -- cgit From 0631492f9c8044a378dc2a17ea257badfbda6d15 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 30 Dec 2024 16:01:00 +0100 Subject: feat: add vim.fs.relpath This is needed to replace the nvim-lspconfig function is_descendant that some lspconfg configurations still use. --- runtime/lua/vim/fs.lua | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 04a6e43db1..91e06688b3 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -741,4 +741,37 @@ function M.abspath(path) return M.joinpath(cwd, path) end +--- Gets `target` path relative to `base`, or `nil` if `base` is not an ancestor. +--- +--- Example: +--- +--- ```lua +--- vim.fs.relpath('/var', '/var/lib') -- 'lib' +--- vim.fs.relpath('/var', '/usr/bin') -- nil +--- ``` +--- +--- @param base string +--- @param target string +--- @param opts table? Reserved for future use +--- @return string|nil +function M.relpath(base, target, opts) + vim.validate('base', base, 'string') + vim.validate('target', target, 'string') + vim.validate('opts', opts, 'table', true) + + base = vim.fs.normalize(vim.fs.abspath(base)) + target = vim.fs.normalize(vim.fs.abspath(target)) + if base == target then + return '.' + end + + local prefix = '' + if iswin then + prefix, base = split_windows_path(base) + end + base = prefix .. base .. (base ~= '/' and '/' or '') + + return vim.startswith(target, base) and target:sub(#base + 1) or nil +end + return M -- cgit From 34e2185022ab698827b72751d77e218a1b6b6afe Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 10 Jan 2025 10:20:43 +0000 Subject: fix(options): better handling of empty values Problem: Whether an option is allowed to be empty isn't well defined and isn't properly checked. Solution: - For non-list string options, explicitly check the option value if it is empty. - Annotate non-list string options that can accept an empty value. - Adjust command completion to ignore the empty value. - Render values in Lua meta files --- runtime/lua/vim/_meta/options.lua | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index c9871c8660..14f252516a 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -52,7 +52,7 @@ vim.go.ari = vim.go.allowrevins --- set to one of CJK locales. See Unicode Standard Annex #11 --- (https://www.unicode.org/reports/tr11). --- ---- @type string +--- @type 'single'|'double' vim.o.ambiwidth = "single" vim.o.ambw = vim.o.ambiwidth vim.go.ambiwidth = vim.o.ambiwidth @@ -208,7 +208,7 @@ vim.go.awa = vim.go.autowriteall --- will change. To use other settings, place ":highlight" commands AFTER --- the setting of the 'background' option. --- ---- @type string +--- @type 'light'|'dark' vim.o.background = "dark" vim.o.bg = vim.o.background vim.go.background = vim.o.background @@ -595,7 +595,7 @@ vim.wo.briopt = vim.wo.breakindentopt --- This option is used together with 'buftype' and 'swapfile' to specify --- special kinds of buffers. See `special-buffers`. --- ---- @type string +--- @type ''|'hide'|'unload'|'delete'|'wipe' vim.o.bufhidden = "" vim.o.bh = vim.o.bufhidden vim.bo.bufhidden = vim.o.bufhidden @@ -658,7 +658,7 @@ vim.bo.bl = vim.bo.buflisted --- without saving. For writing there must be matching `BufWriteCmd|, --- |FileWriteCmd` or `FileAppendCmd` autocommands. --- ---- @type string +--- @type ''|'acwrite'|'help'|'nofile'|'nowrite'|'quickfix'|'terminal'|'prompt' vim.o.buftype = "" vim.o.bt = vim.o.buftype vim.bo.buftype = vim.o.buftype @@ -1118,7 +1118,7 @@ vim.go.cot = vim.go.completeopt --- For Insert mode completion the buffer-local value is used. For --- command line completion the global value is used. --- ---- @type string +--- @type ''|'slash'|'backslash' vim.o.completeslash = "" vim.o.csl = vim.o.completeslash vim.bo.completeslash = vim.o.completeslash @@ -1824,7 +1824,7 @@ vim.go.dy = vim.go.display --- hor horizontally, height of windows is not affected --- both width and height of windows is affected --- ---- @type string +--- @type 'both'|'ver'|'hor' vim.o.eadirection = "both" vim.o.ead = vim.o.eadirection vim.go.eadirection = vim.o.eadirection @@ -2126,7 +2126,7 @@ vim.go.fencs = vim.go.fileencodings --- option is set, because the file would be different when written. --- This option cannot be changed when 'modifiable' is off. --- ---- @type string +--- @type 'unix'|'dos'|'mac' vim.o.fileformat = "unix" vim.o.ff = vim.o.fileformat vim.bo.fileformat = vim.o.fileformat @@ -2382,7 +2382,7 @@ vim.go.fcl = vim.go.foldclose --- "[1-9]": to display a fixed number of columns --- See `folding`. --- ---- @type string +--- @type 'auto'|'auto:1'|'auto:2'|'auto:3'|'auto:4'|'auto:5'|'auto:6'|'auto:7'|'auto:8'|'auto:9'|'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' vim.o.foldcolumn = "0" vim.o.fdc = vim.o.foldcolumn vim.wo.foldcolumn = vim.o.foldcolumn @@ -2479,7 +2479,7 @@ vim.wo.fmr = vim.wo.foldmarker --- `fold-syntax` syntax Syntax highlighting items specify folds. --- `fold-diff` diff Fold text that is not changed. --- ---- @type string +--- @type 'manual'|'expr'|'marker'|'indent'|'syntax'|'diff' vim.o.foldmethod = "manual" vim.o.fdm = vim.o.foldmethod vim.wo.foldmethod = vim.o.foldmethod @@ -3144,7 +3144,7 @@ vim.bo.ims = vim.bo.imsearch --- 'redrawtime') then 'inccommand' is automatically disabled until --- `Command-line-mode` is done. --- ---- @type string +--- @type 'nosplit'|'split'|'' vim.o.inccommand = "nosplit" vim.o.icm = vim.o.inccommand vim.go.inccommand = vim.o.inccommand @@ -4354,7 +4354,7 @@ vim.go.mh = vim.go.mousehide --- "g" is " (jump to tag under mouse click) --- "g" is " ("CTRL-T") --- ---- @type string +--- @type 'extend'|'popup'|'popup_setpos' vim.o.mousemodel = "popup_setpos" vim.o.mousem = vim.o.mousemodel vim.go.mousemodel = vim.o.mousemodel @@ -4947,7 +4947,7 @@ vim.wo.rl = vim.wo.rightleft --- This is useful for languages such as Hebrew, Arabic and Farsi. --- The 'rightleft' option must be set for 'rightleftcmd' to take effect. --- ---- @type string +--- @type 'search' vim.o.rightleftcmd = "search" vim.o.rlc = vim.o.rightleftcmd vim.wo.rightleftcmd = vim.o.rightleftcmd @@ -5222,7 +5222,7 @@ vim.go.sect = vim.go.sections --- backwards, you cannot include the last character of a line, when --- starting in Normal mode and 'virtualedit' empty. --- ---- @type string +--- @type 'inclusive'|'exclusive'|'old' vim.o.selection = "inclusive" vim.o.sel = vim.o.selection vim.go.selection = vim.o.selection @@ -5788,7 +5788,7 @@ vim.go.sc = vim.go.showcmd --- place the text. Without a custom 'statusline' or 'tabline' it will be --- displayed in a convenient location. --- ---- @type string +--- @type 'last'|'statusline'|'tabline' vim.o.showcmdloc = "last" vim.o.sloc = vim.o.showcmdloc vim.go.showcmdloc = vim.o.showcmdloc @@ -5920,7 +5920,7 @@ vim.go.siso = vim.go.sidescrolloff --- "number" display signs in the 'number' column. If the number --- column is not present, then behaves like "auto". --- ---- @type string +--- @type 'yes'|'no'|'auto'|'auto:1'|'auto:2'|'auto:3'|'auto:4'|'auto:5'|'auto:6'|'auto:7'|'auto:8'|'auto:9'|'yes:1'|'yes:2'|'yes:3'|'yes:4'|'yes:5'|'yes:6'|'yes:7'|'yes:8'|'yes:9'|'number' vim.o.signcolumn = "auto" vim.o.scl = vim.o.signcolumn vim.wo.signcolumn = vim.o.signcolumn @@ -6228,7 +6228,7 @@ vim.go.sb = vim.go.splitbelow --- with the previous cursor position. For "screen", the text cannot always --- be kept on the same screen line when 'wrap' is enabled. --- ---- @type string +--- @type 'cursor'|'screen'|'topline' vim.o.splitkeep = "cursor" vim.o.spk = vim.o.splitkeep vim.go.splitkeep = vim.o.splitkeep @@ -6876,7 +6876,7 @@ vim.go.tbs = vim.go.tagbsearch --- match Match case --- smart Ignore case unless an upper case letter is used --- ---- @type string +--- @type 'followic'|'ignore'|'match'|'followscs'|'smart' vim.o.tagcase = "followic" vim.o.tc = vim.o.tagcase vim.bo.tagcase = vim.o.tagcase @@ -7758,7 +7758,7 @@ vim.go.wop = vim.go.wildoptions --- key is never used for the menu. --- This option is not used for ; on Win32. --- ---- @type string +--- @type 'yes'|'menu'|'no' vim.o.winaltkeys = "menu" vim.o.wak = vim.o.winaltkeys vim.go.winaltkeys = vim.o.winaltkeys -- cgit From a4f575abd85e734340ee303daace1a63e5ca9782 Mon Sep 17 00:00:00 2001 From: Xuyuan Pang Date: Tue, 14 Jan 2025 07:17:23 +0800 Subject: fix(lsp): minimum height for floating popup #31990 Problem: The floating window for hover and signature help always cuts off a few lines, because the `_make_floating_popup_size` function counts empty lines as having zero height. Solution: Ensure the height is at least 1. --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ccd68f0fdf..14633adf0c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1432,7 +1432,7 @@ function M._make_floating_popup_size(contents, opts) if vim.tbl_isempty(line_widths) then for _, line in ipairs(contents) do local line_width = vim.fn.strdisplaywidth(line:gsub('%z', '\n')) - height = height + math.ceil(line_width / wrap_at) + height = height + math.max(1, math.ceil(line_width / wrap_at)) end else for i = 1, #contents do -- cgit From b192d58284a791c55f5ae000250fc948e9098d47 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Mon, 13 Jan 2025 09:42:39 -0800 Subject: perf(treesitter): calculate folds asynchronously **Problem:** The treesitter `foldexpr` runs synchronous parses to calculate fold levels, which eliminates async parsing performance in the highlighter. **Solution:** Migrate the `foldexpr` to also calculate and apply fold levels asynchronously. --- runtime/lua/vim/treesitter/_fold.lua | 203 +++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 94 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index 7f1d1b14d5..2777241e9f 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -69,7 +69,8 @@ end ---@param info TS.FoldInfo ---@param srow integer? ---@param erow integer? 0-indexed, exclusive -local function compute_folds_levels(bufnr, info, srow, erow) +---@param callback function? +local function compute_folds_levels(bufnr, info, srow, erow, callback) srow = srow or 0 erow = erow or api.nvim_buf_line_count(bufnr) @@ -78,104 +79,112 @@ local function compute_folds_levels(bufnr, info, srow, erow) return end - parser:parse() - - local enter_counts = {} ---@type table - local leave_counts = {} ---@type table - local prev_start = -1 - local prev_stop = -1 - - parser:for_each_tree(function(tree, ltree) - local query = ts.query.get(ltree:lang(), 'folds') - if not query then + parser:parse(nil, function(_, trees) + if not trees then return end - -- Collect folds starting from srow - 1, because we should first subtract the folds that end at - -- srow - 1 from the level of srow - 1 to get accurate level of srow. - for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do - for id, nodes in pairs(match) do - if query.captures[id] == 'fold' then - local range = ts.get_range(nodes[1], bufnr, metadata[id]) - local start, _, stop, stop_col = Range.unpack4(range) - - if #nodes > 1 then - -- assumes nodes are ordered by range - local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id]) - local _, _, end_stop, end_stop_col = Range.unpack4(end_range) - stop = end_stop - stop_col = end_stop_col - end + local enter_counts = {} ---@type table + local leave_counts = {} ---@type table + local prev_start = -1 + local prev_stop = -1 - if stop_col == 0 then - stop = stop - 1 - end + parser:for_each_tree(function(tree, ltree) + local query = ts.query.get(ltree:lang(), 'folds') + if not query then + return + end - local fold_length = stop - start + 1 - - -- Fold only multiline nodes that are not exactly the same as previously met folds - -- Checking against just the previously found fold is sufficient if nodes - -- are returned in preorder or postorder when traversing tree - if - fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop) - then - enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1 - leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1 - prev_start = start - prev_stop = stop + -- Collect folds starting from srow - 1, because we should first subtract the folds that end at + -- srow - 1 from the level of srow - 1 to get accurate level of srow. + for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do + for id, nodes in pairs(match) do + if query.captures[id] == 'fold' then + local range = ts.get_range(nodes[1], bufnr, metadata[id]) + local start, _, stop, stop_col = Range.unpack4(range) + + if #nodes > 1 then + -- assumes nodes are ordered by range + local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id]) + local _, _, end_stop, end_stop_col = Range.unpack4(end_range) + stop = end_stop + stop_col = end_stop_col + end + + if stop_col == 0 then + stop = stop - 1 + end + + local fold_length = stop - start + 1 + + -- Fold only multiline nodes that are not exactly the same as previously met folds + -- Checking against just the previously found fold is sufficient if nodes + -- are returned in preorder or postorder when traversing tree + if + fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop) + then + enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1 + leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1 + prev_start = start + prev_stop = stop + end end end end - end - end) + end) - local nestmax = vim.wo.foldnestmax - local level0_prev = info.levels0[srow] or 0 - local leave_prev = leave_counts[srow] or 0 - - -- We now have the list of fold opening and closing, fill the gaps and mark where fold start - for lnum = srow + 1, erow do - local enter_line = enter_counts[lnum] or 0 - local leave_line = leave_counts[lnum] or 0 - local level0 = level0_prev - leave_prev + enter_line - - -- Determine if it's the start/end of a fold - -- NB: vim's fold-expr interface does not have a mechanism to indicate that - -- two (or more) folds start at this line, so it cannot distinguish between - -- ( \n ( \n )) \n (( \n ) \n ) - -- versus - -- ( \n ( \n ) \n ( \n ) \n ) - -- Both are represented by ['>1', '>2', '2', '>2', '2', '1'], and - -- vim interprets as the second case. - -- If it did have such a mechanism, (clamped - clamped_prev) - -- would be the correct number of starts to pass on. - local adjusted = level0 ---@type integer - local prefix = '' - if enter_line > 0 then - prefix = '>' - if leave_line > 0 then - -- If this line ends a fold f1 and starts a fold f2, then move f1's end to the previous line - -- so that f2 gets the correct level on this line. This may reduce the size of f1 below - -- foldminlines, but we don't handle it for simplicity. - adjusted = level0 - leave_line - leave_line = 0 + local nestmax = vim.wo.foldnestmax + local level0_prev = info.levels0[srow] or 0 + local leave_prev = leave_counts[srow] or 0 + + -- We now have the list of fold opening and closing, fill the gaps and mark where fold start + for lnum = srow + 1, erow do + local enter_line = enter_counts[lnum] or 0 + local leave_line = leave_counts[lnum] or 0 + local level0 = level0_prev - leave_prev + enter_line + + -- Determine if it's the start/end of a fold + -- NB: vim's fold-expr interface does not have a mechanism to indicate that + -- two (or more) folds start at this line, so it cannot distinguish between + -- ( \n ( \n )) \n (( \n ) \n ) + -- versus + -- ( \n ( \n ) \n ( \n ) \n ) + -- Both are represented by ['>1', '>2', '2', '>2', '2', '1'], and + -- vim interprets as the second case. + -- If it did have such a mechanism, (clamped - clamped_prev) + -- would be the correct number of starts to pass on. + local adjusted = level0 ---@type integer + local prefix = '' + if enter_line > 0 then + prefix = '>' + if leave_line > 0 then + -- If this line ends a fold f1 and starts a fold f2, then move f1's end to the previous line + -- so that f2 gets the correct level on this line. This may reduce the size of f1 below + -- foldminlines, but we don't handle it for simplicity. + adjusted = level0 - leave_line + leave_line = 0 + end end - end - -- Clamp at foldnestmax. - local clamped = adjusted - if adjusted > nestmax then - prefix = '' - clamped = nestmax - end + -- Clamp at foldnestmax. + local clamped = adjusted + if adjusted > nestmax then + prefix = '' + clamped = nestmax + end - -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels(). - info.levels0[lnum] = adjusted - info.levels[lnum] = prefix .. tostring(clamped) + -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels(). + info.levels0[lnum] = adjusted + info.levels[lnum] = prefix .. tostring(clamped) - leave_prev = leave_line - level0_prev = adjusted - end + leave_prev = leave_line + level0_prev = adjusted + end + + if callback then + callback() + end + end) end local M = {} @@ -266,6 +275,8 @@ local function on_changedtree(bufnr, foldinfo, tree_changes) schedule_if_loaded(bufnr, function() local srow_upd, erow_upd ---@type integer?, integer? local max_erow = api.nvim_buf_line_count(bufnr) + -- TODO(ribru17): Replace this with a proper .all() awaiter once #19624 is resolved + local iterations = 0 for _, change in ipairs(tree_changes) do local srow, _, erow, ecol = Range.unpack4(change) -- If a parser doesn't have any ranges explicitly set, treesitter will @@ -279,12 +290,14 @@ local function on_changedtree(bufnr, foldinfo, tree_changes) end -- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit. srow = math.max(srow - vim.wo.foldminlines, 0) - compute_folds_levels(bufnr, foldinfo, srow, erow) srow_upd = srow_upd and math.min(srow_upd, srow) or srow erow_upd = erow_upd and math.max(erow_upd, erow) or erow - end - if #tree_changes > 0 then - foldinfo:foldupdate(bufnr, srow_upd, erow_upd) + compute_folds_levels(bufnr, foldinfo, srow, erow, function() + iterations = iterations + 1 + if iterations == #tree_changes then + foldinfo:foldupdate(bufnr, srow_upd, erow_upd) + end + end) end end) end @@ -342,8 +355,9 @@ local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, foldinfo.on_bytes_range = nil -- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit. srow = math.max(srow - vim.wo.foldminlines, 0) - compute_folds_levels(bufnr, foldinfo, srow, erow) - foldinfo:foldupdate(bufnr, srow, erow) + compute_folds_levels(bufnr, foldinfo, srow, erow, function() + foldinfo:foldupdate(bufnr, srow, erow) + end) end) end end @@ -400,9 +414,10 @@ api.nvim_create_autocmd('OptionSet', { for _, bufnr in ipairs(bufs) do foldinfos[bufnr] = FoldInfo.new(bufnr) api.nvim_buf_call(bufnr, function() - compute_folds_levels(bufnr, foldinfos[bufnr]) + compute_folds_levels(bufnr, foldinfos[bufnr], nil, nil, function() + foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr)) + end) end) - foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr)) end end, }) -- cgit From 69ad6b12ec473a54f09a11596da724178185eb7a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 14 Jan 2025 18:55:06 +0800 Subject: vim-patch:9.1.1010: filetype: VisualCode setting file not recognized (#32003) Problem: filetype: VisualCode setting file not recognized Solution: detect json files in VSCode config directory as jsonc filetype (Konfekt) closes: vim/vim#16400 https://github.com/vim/vim/commit/c200f53cbb03fa11e489a27791d5b9dfc34a6564 Co-authored-by: Konfekt --- runtime/lua/vim/filetype.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index bc866db399..1960bca52b 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2374,6 +2374,8 @@ local pattern = { ['%.html%.m4$'] = 'htmlm4', ['^JAM.*%.'] = starsetf('jam'), ['^Prl.*%.'] = starsetf('jam'), + ['^${HOME}/.*/Code/User/.*%.json$'] = 'jsonc', + ['^${HOME}/.*/VSCodium/User/.*%.json$'] = 'jsonc', ['%.properties_..$'] = 'jproperties', ['%.properties_.._..$'] = 'jproperties', ['%.properties_.._.._'] = starsetf('jproperties'), -- cgit From 25d8c3a5ad7e9c5668841e66540ebe34ceda73a7 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Tue, 14 Jan 2025 14:02:46 +0100 Subject: feat(api): nvim_open_win() relative to tabline and laststatus #32006 Problem: Anchoring a floating window to the tabline and laststatus is cumbersome; requiring autocommands and looping over all windows/tabpages. Solution: Add new "tabline" and "laststatus" options to the `relative` field of nvim_open_win() to place a window relative to. --- runtime/lua/vim/_meta/api.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 8930f131f6..670e867c1e 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1754,10 +1754,12 @@ function vim.api.nvim_open_term(buffer, opts) end --- @param config vim.api.keyset.win_config Map defining the window configuration. Keys: --- - relative: Sets the window layout to "floating", placed at (row,col) --- coordinates relative to: ---- - "editor" The global editor grid ---- - "win" Window given by the `win` field, or current window. ---- - "cursor" Cursor position in current window. ---- - "mouse" Mouse position +--- - "cursor" Cursor position in current window. +--- - "editor" The global editor grid. +--- - "laststatus" 'laststatus' if present, or last row. +--- - "mouse" Mouse position. +--- - "tabline" Tabline if present, or first row. +--- - "win" Window given by the `win` field, or current window. --- - win: `window-ID` window to split, or relative window when creating a --- float (relative="win"). --- - anchor: Decides which corner of the float to place at (row,col): -- cgit From e8a6c1b02122852da83dc52184e78369598d8240 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 14 Jan 2025 08:19:54 -0600 Subject: fix(lsp): schedule call to vim.lsp.start for async root_dir (#31998) When `root_dir` is a function it can (and often will) call the provided callback function in a fast API context (e.g. in the `on_exit` handler of `vim.system`). When the callback function is executed we should ensure that it runs vim.lsp.start on the main event loop. --- runtime/lua/vim/lsp.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 23f4e104d0..7812f31db1 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -513,7 +513,9 @@ local function lsp_enable_callback(bufnr) ---@param root_dir string config.root_dir(function(root_dir) config.root_dir = root_dir - start(config) + vim.schedule(function() + start(config) + end) end) else start(config) -- cgit From 611ef354919f1c6564efd2ff8074545941458ccc Mon Sep 17 00:00:00 2001 From: Mike <4576770+mike325@users.noreply.github.com> Date: Wed, 15 Jan 2025 01:39:17 +0100 Subject: feat(vim.fs): find(), dir() can "follow" symlinks #31551 Problem: vim.fs.dir(), vim.fs.find() do not follow symlinks. Solution: - Add "follow" flag. - Enable it by default. --- runtime/lua/vim/fs.lua | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 91e06688b3..5940fa4386 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -136,6 +136,7 @@ end --- - skip: (fun(dir_name: string): boolean)|nil Predicate --- to control traversal. Return false to stop searching the current directory. --- Only useful when depth > 1 +--- - follow: boolean|nil Follow symbolic links. (default: true) --- ---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type". --- "name" is the basename of the item relative to {path}. @@ -147,6 +148,7 @@ function M.dir(path, opts) vim.validate('path', path, 'string') vim.validate('depth', opts.depth, 'number', true) vim.validate('skip', opts.skip, 'function', true) + vim.validate('follow', opts.follow, 'boolean', true) path = M.normalize(path) if not opts.depth or opts.depth == 1 then @@ -177,7 +179,9 @@ function M.dir(path, opts) if opts.depth and level < opts.depth - and t == 'directory' + and (t == 'directory' or (t == 'link' and opts.follow ~= false and (vim.uv.fs_stat( + M.joinpath(path, f) + ) or {}).type == 'directory')) and (not opts.skip or opts.skip(f) ~= false) then dirs[#dirs + 1] = { f, level + 1 } @@ -211,6 +215,10 @@ end --- Use `math.huge` to place no limit on the number of matches. --- (default: `1`) --- @field limit? number +--- +--- Follow symbolic links. +--- (default: `true`) +--- @field follow? boolean --- Find files or directories (or other items as specified by `opts.type`) in the given path. --- @@ -234,7 +242,7 @@ end --- --- -- get all files ending with .cpp or .hpp inside lib/ --- local cpp_hpp = vim.fs.find(function(name, path) ---- return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$') +--- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$') --- end, {limit = math.huge, type = 'file'}) --- ``` --- @@ -244,6 +252,7 @@ end --- If {names} is a function, it is called for each traversed item with args: --- - name: base name of the current item --- - path: full path of the current item +--- --- The function should return `true` if the given item is considered a match. --- ---@param opts vim.fs.find.Opts Optional keyword arguments: @@ -256,6 +265,7 @@ function M.find(names, opts) vim.validate('stop', opts.stop, 'string', true) vim.validate('type', opts.type, 'string', true) vim.validate('limit', opts.limit, 'number', true) + vim.validate('follow', opts.follow, 'boolean', true) if type(names) == 'string' then names = { names } @@ -345,7 +355,14 @@ function M.find(names, opts) end end - if type_ == 'directory' then + if + type_ == 'directory' + or ( + type_ == 'link' + and opts.follow ~= false + and (vim.uv.fs_stat(f) or {}).type == 'directory' + ) + then dirs[#dirs + 1] = f end end -- cgit From 850084b519e18122820478a71bb4bfa4c15e528a Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Mon, 13 Jan 2025 19:39:03 -0800 Subject: refactor: use nvim.foo.bar format for namespaces --- runtime/lua/vim/diagnostic.lua | 6 +++--- runtime/lua/vim/hl.lua | 2 +- runtime/lua/vim/lsp/buf.lua | 4 ++-- runtime/lua/vim/lsp/codelens.lua | 2 +- runtime/lua/vim/lsp/handlers.lua | 2 +- runtime/lua/vim/lsp/inlay_hint.lua | 2 +- runtime/lua/vim/lsp/semantic_tokens.lua | 4 ++-- runtime/lua/vim/lsp/util.lua | 2 +- runtime/lua/vim/snippet.lua | 2 +- runtime/lua/vim/treesitter/_query_linter.lua | 2 +- runtime/lua/vim/treesitter/dev.lua | 6 +++--- runtime/lua/vim/treesitter/highlighter.lua | 2 +- runtime/lua/vim/vimhelp.lua | 2 +- 13 files changed, 19 insertions(+), 19 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 6466c7d6e8..5df4399b93 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1402,7 +1402,7 @@ M.handlers.signs = { local ns = M.get_namespace(namespace) if not ns.user_data.sign_ns then ns.user_data.sign_ns = - api.nvim_create_namespace(string.format('%s/diagnostic/signs', ns.name)) + api.nvim_create_namespace(string.format('nvim.%s.diagnostic.signs', ns.name)) end -- Handle legacy diagnostic sign definitions @@ -1500,7 +1500,7 @@ M.handlers.underline = { local ns = M.get_namespace(namespace) if not ns.user_data.underline_ns then ns.user_data.underline_ns = - api.nvim_create_namespace(string.format('%s/diagnostic/underline', ns.name)) + api.nvim_create_namespace(string.format('nvim.%s.diagnostic.underline', ns.name)) end local underline_ns = ns.user_data.underline_ns @@ -1572,7 +1572,7 @@ M.handlers.virtual_text = { local ns = M.get_namespace(namespace) if not ns.user_data.virt_text_ns then ns.user_data.virt_text_ns = - api.nvim_create_namespace(string.format('%s/diagnostic/virtual_text', ns.name)) + api.nvim_create_namespace(string.format('nvim.%s.diagnostic.virtual_text', ns.name)) end local virt_text_ns = ns.user_data.virt_text_ns diff --git a/runtime/lua/vim/hl.lua b/runtime/lua/vim/hl.lua index 099efa3c61..f5ace7fdc5 100644 --- a/runtime/lua/vim/hl.lua +++ b/runtime/lua/vim/hl.lua @@ -115,7 +115,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts) end end -local yank_ns = api.nvim_create_namespace('hlyank') +local yank_ns = api.nvim_create_namespace('nvim.hlyank') local yank_timer --- @type uv.uv_timer_t? local yank_cancel --- @type fun()? diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 69407bc6f8..0acbc50003 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -20,7 +20,7 @@ local function client_positional_params(params) end end -local hover_ns = api.nvim_create_namespace('vim_lsp_hover_range') +local hover_ns = api.nvim_create_namespace('nvim.lsp.hover_range') --- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts --- @field silent? boolean @@ -324,7 +324,7 @@ local function process_signature_help_results(results) return signatures end -local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') +local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help') --- @class vim.lsp.buf.signature_help.Opts : vim.lsp.util.open_floating_preview.Opts --- @field silent? boolean diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 3ccd165d0b..65f794229c 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -21,7 +21,7 @@ local lens_cache_by_buf = setmetatable({}, { ---client_id -> namespace local namespaces = setmetatable({}, { __index = function(t, key) - local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key) + local value = api.nvim_create_namespace('nvim.lsp.codelens:' .. key) rawset(t, key, value) return value end, diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 3779c342e8..425e3206aa 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -382,7 +382,7 @@ end --- @diagnostic disable-next-line: deprecated RCS[ms.textDocument_hover] = M.hover -local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') +local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help') --- @deprecated remove in 0.13 --- |lsp-handler| for the method "textDocument/signatureHelp". diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 50cf9f5f29..83bf276bff 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -29,7 +29,7 @@ local bufstates = vim.defaulttable(function(_) }) end) -local namespace = api.nvim_create_namespace('vim_lsp_inlayhint') +local namespace = api.nvim_create_namespace('nvim.lsp.inlayhint') local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {}) --- |lsp-handler| for the method `textDocument/inlayHint` diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 7cc3f95aed..f2a8125d1a 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -225,7 +225,7 @@ function STHighlighter:attach(client_id) local state = self.client_state[client_id] if not state then state = { - namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens:' .. client_id), + namespace = api.nvim_create_namespace('nvim.lsp.semantic_tokens:' .. client_id), active_request = {}, current_result = {}, } @@ -805,7 +805,7 @@ function M._refresh(err, _, ctx) return vim.NIL end -local namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens') +local namespace = api.nvim_create_namespace('nvim.lsp.semantic_tokens') api.nvim_set_decoration_provider(namespace, { on_win = function(_, _, bufnr, topline, botline) local highlighter = STHighlighter.active[bufnr] diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 14633adf0c..e9bf58a01b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1650,7 +1650,7 @@ function M.open_floating_preview(contents, syntax, opts) end do --[[ References ]] - local reference_ns = api.nvim_create_namespace('vim_lsp_references') + local reference_ns = api.nvim_create_namespace('nvim.lsp.references') --- Removes document highlights from a buffer. --- diff --git a/runtime/lua/vim/snippet.lua b/runtime/lua/vim/snippet.lua index af7e3c6d33..9d4409b48a 100644 --- a/runtime/lua/vim/snippet.lua +++ b/runtime/lua/vim/snippet.lua @@ -1,6 +1,6 @@ local G = vim.lsp._snippet_grammar local snippet_group = vim.api.nvim_create_augroup('vim/snippet', {}) -local snippet_ns = vim.api.nvim_create_namespace('vim/snippet') +local snippet_ns = vim.api.nvim_create_namespace('nvim.snippet') local hl_group = 'SnippetTabstop' local jump_forward_key = '' local jump_backward_key = '' diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua index a825505378..f6645beb28 100644 --- a/runtime/lua/vim/treesitter/_query_linter.lua +++ b/runtime/lua/vim/treesitter/_query_linter.lua @@ -1,6 +1,6 @@ local api = vim.api -local namespace = api.nvim_create_namespace('vim.treesitter.query_linter') +local namespace = api.nvim_create_namespace('nvim.treesitter.query_linter') local M = {} diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 26817cdba5..0e886d0e27 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -119,7 +119,7 @@ function TSTreeView:new(bufnr, lang) end local t = { - ns = api.nvim_create_namespace('treesitter/dev-inspect'), + ns = api.nvim_create_namespace('nvim.treesitter.dev_inspect'), nodes = nodes, named = named, ---@type vim.treesitter.dev.TSTreeViewOpts @@ -135,7 +135,7 @@ function TSTreeView:new(bufnr, lang) return t end -local decor_ns = api.nvim_create_namespace('ts.dev') +local decor_ns = api.nvim_create_namespace('nvim.treesitter.dev') ---@param range Range4 ---@return string @@ -547,7 +547,7 @@ function M.inspect_tree(opts) }) end -local edit_ns = api.nvim_create_namespace('treesitter/dev-edit') +local edit_ns = api.nvim_create_namespace('nvim.treesitter.dev_edit') ---@param query_win integer ---@param base_win integer diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index be138885d5..c11fa1999d 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -2,7 +2,7 @@ local api = vim.api local query = vim.treesitter.query local Range = require('vim.treesitter._range') -local ns = api.nvim_create_namespace('treesitter/highlighter') +local ns = api.nvim_create_namespace('nvim.treesitter.highlighter') ---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch diff --git a/runtime/lua/vim/vimhelp.lua b/runtime/lua/vim/vimhelp.lua index 5579cc0174..a494d311b1 100644 --- a/runtime/lua/vim/vimhelp.lua +++ b/runtime/lua/vim/vimhelp.lua @@ -7,7 +7,7 @@ local M = {} --- Note: {patterns} is assumed to be sorted by occurrence in the file. --- @param patterns {start:string,stop:string,match:string}[] function M.highlight_groups(patterns) - local ns = vim.api.nvim_create_namespace('vimhelp') + local ns = vim.api.nvim_create_namespace('nvim.vimhelp') vim.api.nvim_buf_clear_namespace(0, ns, 0, -1) local save_cursor = vim.fn.getcurpos() -- cgit From 09e01437c968be4c6e9f6bb3ac8811108c58008c Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Mon, 13 Jan 2025 19:45:11 -0800 Subject: refactor: use nvim.foo.bar format for autocommand groups --- runtime/lua/vim/_defaults.lua | 10 +++++----- runtime/lua/vim/diagnostic.lua | 2 +- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/_folding_range.lua | 2 +- runtime/lua/vim/lsp/codelens.lua | 2 +- runtime/lua/vim/lsp/completion.lua | 2 +- runtime/lua/vim/lsp/diagnostic.lua | 2 +- runtime/lua/vim/lsp/inlay_hint.lua | 2 +- runtime/lua/vim/lsp/semantic_tokens.lua | 2 +- runtime/lua/vim/lsp/util.lua | 4 ++-- runtime/lua/vim/snippet.lua | 2 +- runtime/lua/vim/treesitter/_fold.lua | 2 +- runtime/lua/vim/treesitter/dev.lua | 4 ++-- runtime/lua/vim/treesitter/query.lua | 2 +- 14 files changed, 20 insertions(+), 20 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index d71116117e..28f1542f64 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -412,7 +412,7 @@ do end end - local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim_popupmenu', {}) + local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim.popupmenu', {}) vim.api.nvim_create_autocmd('MenuPopup', { pattern = '*', group = nvim_popupmenu_augroup, @@ -429,7 +429,7 @@ end --- Default autocommands. See |default-autocmds| do - local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim_terminal', {}) + local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim.terminal', {}) vim.api.nvim_create_autocmd('BufReadCmd', { pattern = 'term://*', group = nvim_terminal_augroup, @@ -509,14 +509,14 @@ do vim.api.nvim_create_autocmd('CmdwinEnter', { pattern = '[:>]', desc = 'Limit syntax sync to maxlines=1 in the command window', - group = vim.api.nvim_create_augroup('nvim_cmdwin', {}), + group = vim.api.nvim_create_augroup('nvim.cmdwin', {}), command = 'syntax sync minlines=1 maxlines=1', }) vim.api.nvim_create_autocmd('SwapExists', { pattern = '*', desc = 'Skip the swapfile prompt when the swapfile is owned by a running Nvim process', - group = vim.api.nvim_create_augroup('nvim_swapfile', {}), + group = vim.api.nvim_create_augroup('nvim.swapfile', {}), callback = function() local info = vim.fn.swapinfo(vim.v.swapname) local user = vim.uv.os_get_passwd().username @@ -543,7 +543,7 @@ do end if tty then - local group = vim.api.nvim_create_augroup('nvim_tty', {}) + local group = vim.api.nvim_create_augroup('nvim.tty', {}) --- Set an option after startup (so that OptionSet is fired), but only if not --- already set by the user. diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 5df4399b93..0939ff591e 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -356,7 +356,7 @@ local bufnr_and_namespace_cacher_mt = { -- bufnr -> ns -> Diagnostic[] local diagnostic_cache = {} --- @type table> do - local group = api.nvim_create_augroup('DiagnosticBufWipeout', {}) + local group = api.nvim_create_augroup('nvim.diagnostic.buf_wipeout', {}) setmetatable(diagnostic_cache, { --- @param t table --- @param bufnr integer diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 23f4e104d0..40caf01e86 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -841,7 +841,7 @@ local function buf_attach(bufnr) attached_buffers[bufnr] = true local uri = vim.uri_from_bufnr(bufnr) - local augroup = ('lsp_b_%d_save'):format(bufnr) + local augroup = ('nvim.lsp.b_%d_save'):format(bufnr) local group = api.nvim_create_augroup(augroup, { clear = true }) api.nvim_create_autocmd('BufWritePre', { group = group, diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua index 2f1767aaf5..66eb81db6e 100644 --- a/runtime/lua/vim/lsp/_folding_range.lua +++ b/runtime/lua/vim/lsp/_folding_range.lua @@ -171,7 +171,7 @@ end -- 1. Implement clearing `bufstate` and event hooks -- when no clients in the buffer support the corresponding method. -- 2. Then generalize this state management to other LSP modules. -local augroup_setup = api.nvim_create_augroup('vim_lsp_folding_range/setup', {}) +local augroup_setup = api.nvim_create_augroup('nvim.lsp.folding_range.setup', {}) --- Initialize `bufstate` and event hooks, then request folding ranges. --- Manage their lifecycle within this function. diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 65f794229c..e36d8fee27 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -30,7 +30,7 @@ local namespaces = setmetatable({}, { ---@private M.__namespaces = namespaces -local augroup = api.nvim_create_augroup('vim_lsp_codelens', {}) +local augroup = api.nvim_create_augroup('nvim.lsp.codelens', {}) api.nvim_create_autocmd('LspDetach', { group = augroup, diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index dbf0a62eeb..edbc329939 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -630,7 +630,7 @@ local function enable_completions(client_id, bufnr, opts) -- Set up autocommands. local group = - api.nvim_create_augroup(string.format('vim/lsp/completion-%d', bufnr), { clear = true }) + api.nvim_create_augroup(string.format('nvim.lsp.completion_%d', bufnr), { clear = true }) api.nvim_create_autocmd('CompleteDone', { group = group, buffer = bufnr, diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 8c1f3f10d4..cf39338cc1 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -5,7 +5,7 @@ local api = vim.api local M = {} -local augroup = api.nvim_create_augroup('vim_lsp_diagnostic', {}) +local augroup = api.nvim_create_augroup('nvim.lsp.diagnostic', {}) local DEFAULT_CLIENT_ID = -1 diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 83bf276bff..37e1202d1d 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -30,7 +30,7 @@ local bufstates = vim.defaulttable(function(_) end) local namespace = api.nvim_create_namespace('nvim.lsp.inlayhint') -local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {}) +local augroup = api.nvim_create_augroup('nvim.lsp.inlayhint', {}) --- |lsp-handler| for the method `textDocument/inlayHint` --- Store hints for a specific buffer and client diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index f2a8125d1a..a31202553b 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -166,7 +166,7 @@ function STHighlighter.new(bufnr) local self = setmetatable({}, { __index = STHighlighter }) self.bufnr = bufnr - self.augroup = api.nvim_create_augroup('vim_lsp_semantic_tokens:' .. bufnr, { clear = true }) + self.augroup = api.nvim_create_augroup('nvim.lsp.semantic_tokens:' .. bufnr, { clear = true }) self.client_state = {} STHighlighter.active[bufnr] = self diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e9bf58a01b..4e0adf3366 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1357,7 +1357,7 @@ end ---@param bufnrs table list of buffers where the preview window will remain visible ---@see autocmd-events local function close_preview_autocmd(events, winnr, bufnrs) - local augroup = api.nvim_create_augroup('preview_window_' .. winnr, { + local augroup = api.nvim_create_augroup('nvim.preview_window_' .. winnr, { clear = true, }) @@ -1619,7 +1619,7 @@ function M.open_floating_preview(contents, syntax, opts) api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) end - local augroup_name = ('closing_floating_preview_%d'):format(floating_winnr) + local augroup_name = ('nvim.closing_floating_preview_%d'):format(floating_winnr) local ok = pcall(api.nvim_get_autocmds, { group = augroup_name, pattern = tostring(floating_winnr) }) if not ok then diff --git a/runtime/lua/vim/snippet.lua b/runtime/lua/vim/snippet.lua index 9d4409b48a..bfd439181e 100644 --- a/runtime/lua/vim/snippet.lua +++ b/runtime/lua/vim/snippet.lua @@ -1,5 +1,5 @@ local G = vim.lsp._snippet_grammar -local snippet_group = vim.api.nvim_create_augroup('vim/snippet', {}) +local snippet_group = vim.api.nvim_create_augroup('nvim.snippet', {}) local snippet_ns = vim.api.nvim_create_namespace('nvim.snippet') local hl_group = 'SnippetTabstop' local jump_forward_key = '' diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index 7f1d1b14d5..cf5c40cd1e 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -183,7 +183,7 @@ local M = {} ---@type table local foldinfos = {} -local group = api.nvim_create_augroup('treesitter/fold', {}) +local group = api.nvim_create_augroup('nvim.treesitter.fold', {}) --- Update the folds in the windows that contain the buffer and use expr foldmethod (assuming that --- the user doesn't use different foldexpr for the same buffer). diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 0e886d0e27..42c25dbdad 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -442,7 +442,7 @@ function M.inspect_tree(opts) end, }) - local group = api.nvim_create_augroup('treesitter/dev', {}) + local group = api.nvim_create_augroup('nvim.treesitter.dev', {}) api.nvim_create_autocmd('CursorMoved', { group = group, @@ -633,7 +633,7 @@ function M.edit_query(lang) -- can infer the language later. api.nvim_buf_set_name(query_buf, string.format('%s/query_editor.scm', lang)) - local group = api.nvim_create_augroup('treesitter/dev-edit', {}) + local group = api.nvim_create_augroup('nvim.treesitter.dev_edit', {}) api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, { group = group, buffer = query_buf, diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 66ab0d52f0..ad648f36cc 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -289,7 +289,7 @@ end, false) api.nvim_create_autocmd('OptionSet', { pattern = { 'runtimepath' }, - group = api.nvim_create_augroup('ts_query_cache_reset', { clear = true }), + group = api.nvim_create_augroup('nvim.treesitter.query_cache_reset', { clear = true }), callback = function() M.get:clear() end, -- cgit From 0a7e4e9e5f28f3b6b3c83040430d0a36fcd71fad Mon Sep 17 00:00:00 2001 From: Andrew Braxton <42975660+andrewbraxton@users.noreply.github.com> Date: Wed, 15 Jan 2025 04:58:36 -0500 Subject: fix(lsp): vim.lsp.enable(...,false) does not disable #32002 Problem: Per the documentation, passing `false` as the `enable` parameter of `vim.lsp.enable()` should disable the given LSP(s), but it does not work due to a logic error. Specifically, `enable == false and nil or {}` will always evaluate to `{}` because `nil` is falsy. Solution: Correct the conditional statement. --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 7812f31db1..147d29d6be 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -546,7 +546,7 @@ function lsp.enable(name, enable) if nm == '*' then error('Invalid name') end - lsp._enabled_configs[nm] = enable == false and nil or {} + lsp._enabled_configs[nm] = enable ~= false and {} or nil end if not next(lsp._enabled_configs) then -- cgit From 09bcb310681e3b87d5b8c5eb547b182554cff7b4 Mon Sep 17 00:00:00 2001 From: Evgeni Chasnovski Date: Wed, 15 Jan 2025 12:36:00 +0200 Subject: fix(docs): replace `yxx` mappings with `g==` #31947 Problem: `yx` uses "y" prefix, which shadows a builtin operator. Solution: Use `g=` (in the form of `g==` currently), drawing from precedent of CTRL-= and 'tpope/vim-scriptease'. --- runtime/lua/vim/treesitter/query.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index ad648f36cc..e43d0a8ad4 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -301,7 +301,7 @@ api.nvim_create_autocmd('OptionSet', { --- - `captures`: a list of unique capture names defined in the query (alias: `info.captures`). --- - `info.patterns`: information about predicates. --- ---- Example (to try it, use `yxx` or select the code then run `:'<,'>lua`): +--- Example (to try it, use `g==` or select the code then run `:'<,'>lua`): --- ```lua --- local query = vim.treesitter.query.parse('vimdoc', [[ --- ; query -- cgit From 524be56042335db589b9fe62dfdae39be3f69a15 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 16 Jan 2025 00:34:25 +0100 Subject: vim-patch:9.1.1019: filetype: fd ignore files are not recognized (#32042) Problem: filetype: fd ignore files are not recognized Solution: detect .fdignore files as gitignore filetype closes: vim/vim#16444 https://github.com/vim/vim/commit/3058087f6f04be788118e94e942e0f0c9fca25f0 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 1960bca52b..e5ba3b1211 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1576,6 +1576,7 @@ local filename = { ['.gitignore'] = 'gitignore', ['.ignore'] = 'gitignore', ['.dockerignore'] = 'gitignore', + ['.fdignore'] = 'gitignore', ['.npmignore'] = 'gitignore', ['.rgignore'] = 'gitignore', ['.vscodeignore'] = 'gitignore', @@ -2244,6 +2245,7 @@ local pattern = { ['^dictd.*%.conf$'] = 'dictdconf', ['/lxqt/.*%.conf$'] = 'dosini', ['/screengrab/.*%.conf$'] = 'dosini', + ['/%.config/fd/ignore$'] = 'gitignore', ['^${GNUPGHOME}/gpg%.conf$'] = 'gpg', ['/boot/grub/grub%.conf$'] = 'grub', ['/hypr/.*%.conf$'] = 'hyprlang', -- cgit From fb564ddff0b4ec9dad5afa7548777af1c3044273 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 16 Jan 2025 20:53:17 +0000 Subject: refactor(options): generic expand and did_set callbacks (#32011) * refactor(options): generic expand and did_set callbacks Problem: Many options have similar callbacks to check the values are valid. Solution: Generalize these callbacks into a single function that reads the option table. * refactor: gen_options.lua refactor: gen_options.lua - inline get_cond * refactor(options): use a simpler format for the common default --- runtime/lua/vim/_meta/options.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 14f252516a..107b1ffdfb 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -4947,7 +4947,7 @@ vim.wo.rl = vim.wo.rightleft --- This is useful for languages such as Hebrew, Arabic and Farsi. --- The 'rightleft' option must be set for 'rightleftcmd' to take effect. --- ---- @type 'search' +--- @type string vim.o.rightleftcmd = "search" vim.o.rlc = vim.o.rightleftcmd vim.wo.rightleftcmd = vim.o.rightleftcmd -- cgit From 0d3a8e8c1a7778c6c79658f26ba492a5f4a17d18 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 17 Jan 2025 08:38:58 +0800 Subject: vim-patch:9.1.1020: no way to get current selected item in a async context (#32056) Problem: no way to get current selected item in a async context Solution: add completed flag to show the entries of currently selected index item (glepnir) closes: vim/vim#16451 https://github.com/vim/vim/commit/037b028a2219d09bc97be04b300b2c0490c4268d Co-authored-by: glepnir --- runtime/lua/vim/_meta/vimfn.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 6316ab2bfc..4b5b276023 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -1037,7 +1037,8 @@ function vim.fn.complete_check() end --- typed text only, or the last completion after --- no item is selected when using the or --- keys) ---- inserted Inserted string. [NOT IMPLEMENTED YET] +--- completed Return a dictionary containing the entries of +--- the currently selected index item. --- preview_winid Info floating preview window id. --- preview_bufnr Info floating preview buffer id. --- -- cgit From 7ce27381fb49ac7d6ef1e115c3952f998e979b15 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 16 Jan 2025 08:47:29 +0100 Subject: fix(messages): lsp window/showMessage is not an error Ref https://github.com/neovim/neovim/discussions/32015 --- runtime/lua/vim/lsp/client.lua | 6 +++--- runtime/lua/vim/lsp/handlers.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index a99363d3d6..a082613bb0 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -702,14 +702,14 @@ local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'err --- --- @param ... string List to write to the buffer local function err_message(...) - local chunks = { { table.concat({ ... }) } } + local chunks = { { table.concat(vim.iter({ ... }):flatten():totable()) } } if vim.in_fast_event() then vim.schedule(function() - vim.api.nvim_echo(chunks, true, { err = true }) + api.nvim_echo(chunks, true, { err = true }) api.nvim_command('redraw') end) else - vim.api.nvim_echo(chunks, true, { err = true }) + api.nvim_echo(chunks, true, { err = true }) api.nvim_command('redraw') end end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 425e3206aa..5da4033f89 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -583,7 +583,7 @@ NSC['window/showMessage'] = function(_, params, ctx) err_message('LSP[', client_name, '] ', message) else message = ('LSP[%s][%s] %s\n'):format(client_name, protocol.MessageType[message_type], message) - api.nvim_echo({ { message } }, true, { err = true }) + api.nvim_echo({ { message } }, true, {}) end return params end -- cgit From 3530182ba491ba8663b40bdff0c044d74e89bb82 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 17 Jan 2025 13:46:58 +0100 Subject: vim-patch:9.1.1026: filetype: swc configuration files are not recognized Problem: filetype: swc configuration files are not recognized Solution: detect .swcrc files as json filetype (Marces Engel) References: https://swc.rs/docs/configuration/swcrc closes: vim/vim#16462 https://github.com/vim/vim/commit/3a738fccaaf6737c91641856ea00579dbe68bd4e Co-authored-by: Marces Engel --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index e5ba3b1211..efc41269f8 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1636,6 +1636,7 @@ local filename = { ['.lintstagedrc'] = 'json', ['deno.lock'] = 'json', ['flake.lock'] = 'json', + ['.swcrc'] = 'json', ['.babelrc'] = 'jsonc', ['.eslintrc'] = 'jsonc', ['.hintrc'] = 'jsonc', -- cgit From b9e6fa7ec81c463d77cc919392b52f6df2d8d304 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Fri, 17 Jan 2025 15:27:50 +0100 Subject: fix(lsp): use filterText as word if textEdit/label doesn't match Problem: With language servers like lemminx, completing xml tags like ` Date: Sat, 18 Jan 2025 07:43:21 -0600 Subject: fix(diagnostic)!: make virtual text handler opt-in (#32079) Making this opt-out (on by default) was the wrong choice from the beginning. It is too visually noisy to be enabled by default. BREAKING CHANGE: Users must opt-in to the diagnostic virtual text handler by adding vim.diagnostic.config({ virtual_text = true }) to their config. --- runtime/lua/vim/diagnostic.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 0939ff591e..ead75f7d51 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -70,7 +70,7 @@ end --- Use virtual text for diagnostics. If multiple diagnostics are set for a --- namespace, one prefix per diagnostic + the last diagnostic message are --- shown. ---- (default: `true`) +--- (default: `false`) --- @field virtual_text? boolean|vim.diagnostic.Opts.VirtualText|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualText --- --- Use signs for diagnostics |diagnostic-signs|. @@ -312,7 +312,7 @@ M.severity = { local global_diagnostic_options = { signs = true, underline = true, - virtual_text = true, + virtual_text = false, float = true, update_in_insert = false, severity_sort = false, -- cgit From 6696ea7f103814d3d5700107546280bf50a4004a Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Sun, 19 Jan 2025 00:07:47 +0900 Subject: fix(treesitter): clean up parsing queue --- runtime/lua/vim/treesitter/languagetree.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 945a2301a9..35a77f1afc 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -450,8 +450,8 @@ function LanguageTree:_run_async_callbacks(range, err, trees) for _, cb in ipairs(self._cb_queues[key]) do cb(err, trees) end - self._ranges_being_parsed[key] = false - self._cb_queues[key] = {} + self._ranges_being_parsed[key] = nil + self._cb_queues[key] = nil end --- Run an asynchronous parse, calling {on_parse} when complete. -- cgit From d56ba71af11c9048c9085e4f66a47947770bdb29 Mon Sep 17 00:00:00 2001 From: Yochem van Rosmalen Date: Sun, 19 Jan 2025 22:08:10 +0100 Subject: fix(lsp): document_symbol uses loclist by default #32070 Problem: Not able to open document symbols for different buffers Solution: Use the location list as default. To switch back to previous behavior (qflist): vim.lsp.buf.document_symbol({ loclist = false }) Fixes: #31832 --- runtime/lua/vim/lsp/buf.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 0acbc50003..8efc6996dd 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -252,13 +252,13 @@ end --- vim.lsp.buf.definition({ on_list = on_list }) --- vim.lsp.buf.references(nil, { on_list = on_list }) --- ``` +--- @field on_list? fun(t: vim.lsp.LocationOpts.OnList) --- ---- If you prefer loclist instead of qflist: +--- Whether to use the |location-list| or the |quickfix| list. --- ```lua --- vim.lsp.buf.definition({ loclist = true }) ---- vim.lsp.buf.references(nil, { loclist = true }) +--- vim.lsp.buf.references(nil, { loclist = false }) --- ``` ---- @field on_list? fun(t: vim.lsp.LocationOpts.OnList) --- @field loclist? boolean --- @class vim.lsp.LocationOpts.OnList @@ -796,9 +796,10 @@ function M.references(context, opts) end end ---- Lists all symbols in the current buffer in the quickfix window. +--- Lists all symbols in the current buffer in the |location-list|. --- @param opts? vim.lsp.ListOpts function M.document_symbol(opts) + opts = vim.tbl_deep_extend('keep', opts or {}, { loclist = true }) local params = { textDocument = util.make_text_document_params() } request_with_opts(ms.textDocument_documentSymbol, params, opts) end -- cgit From 5f527f24f0ea89e9071e065530cbed449507df5c Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Sun, 19 Jan 2025 21:49:02 +0100 Subject: fix(lsp): don't use completion filterText if prefix is empty Follow up to https://github.com/neovim/neovim/pull/32072 If there is no prefix (e.g. at the start of word boundary or a line), it always used the `filterText` because the `match` function always returned false. --- runtime/lua/vim/lsp/completion.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 3c7d1f1469..9902c52c33 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -231,6 +231,9 @@ end ---@param prefix string ---@return boolean local function match_item_by_value(value, prefix) + if prefix == '' then + return true + end if vim.o.completeopt:find('fuzzy') ~= nil then return next(vim.fn.matchfuzzy({ value }, prefix)) ~= nil end -- cgit From 5b025b499ec430f1733409f0fb5ba3f88ce25a88 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 18 Jan 2025 10:42:11 +0100 Subject: vim-patch:9.1.1030: filetype: setting bash filetype is backwards incompatible Problem: filetype: setting bash filetype is backwards incompatible Solution: revert patch v9.1.0965, detect bash scripts again as sh filetype This reverts commit b9b762c21f2b61e0e7d8fee43d4d3dc8ecffd721. related: vim/vim#16309 https://github.com/vim/vim/commit/727c567a0934643e2d6e1dd92d4e636b17d9067f Co-authored-by: Christian Brabandt vim-patch:9.1.1033: tests: shaderslang was removed from test_filetype erroneously Problem: tests: shaderslang was removed from test_filetype erroneously (Christian Clason, after v9.1.1030) Solution: restore the test https://github.com/vim/vim/commit/1d2867df0c5dfa3d2444229f9e4b23d6ff935956 Co-authored-by: Christian Brabandt --- runtime/lua/vim/filetype/detect.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 30a9951f6a..31c88c80bd 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1527,7 +1527,6 @@ local function sh(path, contents, name) vim.b[b].is_kornshell = nil vim.b[b].is_sh = nil end - return M.shell(path, contents, 'bash'), on_detect -- Ubuntu links sh to dash elseif matchregex(name, [[\<\(sh\|dash\)\>]]) then on_detect = function(b) -- cgit From 27c88069538bf64dace1ed39512d914e88615ac1 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Mon, 20 Jan 2025 21:17:36 +0900 Subject: docs(treesitter): expose LanguageTree:parent() #32108 Plugins may want to climb up the LanguageTree. Also add missing type annotations for other methods. --- runtime/lua/vim/treesitter/languagetree.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 35a77f1afc..3db7fe5c9e 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -267,6 +267,7 @@ function LanguageTree:trees() end --- Gets the language of this tree node. +--- @return string function LanguageTree:lang() return self._lang end @@ -307,11 +308,13 @@ function LanguageTree:is_valid(exclude_children) end --- Returns a map of language to child tree. +--- @return table function LanguageTree:children() return self._children end --- Returns the source content of the language tree (bufnr or string). +--- @return integer|string function LanguageTree:source() return self._source end @@ -630,7 +633,8 @@ function LanguageTree:add_child(lang) return self._children[lang] end ---- @package +---Returns the parent tree. `nil` for the root tree. +---@return vim.treesitter.LanguageTree? function LanguageTree:parent() return self._parent end -- cgit From 19b25f3feacfedc18a57eb2a1368a1ed07ac5faa Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 19 Jan 2025 13:30:11 +0100 Subject: feat(api): deprecate nvim_buf_add_highlight() This was kept for a while as it was a useful short hand and initially matched what highlights what actually properly implemented. But now |vim.hl.range()| is a better high-level shorthand with full support for native multi-line ranges. --- runtime/lua/vim/_meta/api.lua | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 670e867c1e..4168d5b857 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -163,35 +163,14 @@ function vim.api.nvim__stats() end --- @return any function vim.api.nvim__unpack(str) end ---- Adds a highlight to buffer. ---- ---- Useful for plugins that dynamically generate highlights to a buffer ---- (like a semantic highlighter or linter). The function adds a single ---- highlight to a buffer. Unlike `matchaddpos()` highlights follow changes to ---- line numbering (as lines are inserted/removed above the highlighted line), ---- like signs and marks do. ---- ---- Namespaces are used for batch deletion/updating of a set of highlights. To ---- create a namespace, use `nvim_create_namespace()` which returns a namespace ---- id. Pass it in to this function as `ns_id` to add highlights to the ---- namespace. All highlights in the same namespace can then be cleared with ---- single call to `nvim_buf_clear_namespace()`. If the highlight never will be ---- deleted by an API call, pass `ns_id = -1`. ---- ---- As a shorthand, `ns_id = 0` can be used to create a new namespace for the ---- highlight, the allocated id is then returned. If `hl_group` is the empty ---- string no highlight is added, but a new `ns_id` is still returned. This is ---- supported for backwards compatibility, new code should use ---- `nvim_create_namespace()` to create a new empty namespace. ---- ---- @param buffer integer Buffer handle, or 0 for current buffer ---- @param ns_id integer namespace to use or -1 for ungrouped highlight ---- @param hl_group string Name of the highlight group to use ---- @param line integer Line to highlight (zero-indexed) ---- @param col_start integer Start of (byte-indexed) column range to highlight ---- @param col_end integer End of (byte-indexed) column range to highlight, ---- or -1 to highlight to end of line ---- @return integer # The ns_id that was used +--- @deprecated +--- @param buffer integer +--- @param ns_id integer +--- @param hl_group string +--- @param line integer +--- @param col_start integer +--- @param col_end integer +--- @return integer function vim.api.nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) end --- Activates buffer-update events on a channel, or as Lua callbacks. @@ -987,7 +966,7 @@ function vim.api.nvim_create_buf(listed, scratch) end --- Creates a new namespace or gets an existing one. [namespace]() --- --- Namespaces are used for buffer highlights and virtual text, see ---- `nvim_buf_add_highlight()` and `nvim_buf_set_extmark()`. +--- `nvim_buf_set_extmark()`. --- --- Namespaces can be named or anonymous. If `name` matches an existing --- namespace, the associated id is returned. If `name` is an empty string -- cgit From 8a236c242a76825a6a9266feda45794c7328c807 Mon Sep 17 00:00:00 2001 From: Guilherme Soares <48023091+guilhas07@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:00:13 +0000 Subject: fix(lsp): set floating window filetype after setup #32112 Problem: The filetype for the floating window buffer is being set before its context is fully initialized. This results in `FileType` events not receiving the correct context. Solution: Set the filetype after the floating preview window and its buffer variables are fully configured to ensure proper context is provided. --- runtime/lua/vim/lsp/util.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4e0adf3366..b9b53d36a8 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1568,8 +1568,6 @@ function M.open_floating_preview(contents, syntax, opts) if do_stylize then local width = M._make_floating_popup_size(contents, opts) contents = M._normalize_markdown(contents, { width = width }) - vim.bo[floating_bufnr].filetype = 'markdown' - vim.treesitter.start(floating_bufnr) else -- Clean up input: trim empty lines contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) @@ -1635,9 +1633,6 @@ function M.open_floating_preview(contents, syntax, opts) }) end - if do_stylize then - vim.wo[floating_winnr].conceallevel = 2 - end vim.wo[floating_winnr].foldenable = false -- Disable folding. vim.wo[floating_winnr].wrap = opts.wrap -- Soft wrapping. vim.wo[floating_winnr].breakindent = true -- Slightly better list presentation. @@ -1646,6 +1641,12 @@ function M.open_floating_preview(contents, syntax, opts) vim.bo[floating_bufnr].modifiable = false vim.bo[floating_bufnr].bufhidden = 'wipe' + if do_stylize then + vim.wo[floating_winnr].conceallevel = 2 + vim.bo[floating_bufnr].filetype = 'markdown' + vim.treesitter.start(floating_bufnr) + end + return floating_bufnr, floating_winnr end -- cgit From ded15ca8c210965442d39ab214d4838b80a3fdc6 Mon Sep 17 00:00:00 2001 From: Konrad Malik Date: Mon, 20 Jan 2025 15:10:00 +0100 Subject: fix: completion.enable(false,...) deletes invalid augroup #32121 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: vim.lsp.completion.enable(true, client.id, bufnr) vim.lsp.completion.enable(false, client.id, bufnr) Error detected while processing LspDetach Autocommands for "*": Error executing lua callback: …/lsp/completion.lua:701: Vim:E367: No such group: "vim/lsp/completion-22" stack traceback: [C]: in function 'nvim_del_augroup_by_name' …/lsp/completion.lua:701: in function 'disable_completions' …/lsp/completion.lua:724: in function 'enable' Solution: Delete the correct augroup. --- runtime/lua/vim/lsp/completion.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 9902c52c33..bdf31d8514 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -615,6 +615,12 @@ local function on_complete_done() end end +---@param bufnr integer +---@return string +local function get_augroup(bufnr) + return string.format('nvim.lsp.completion_%d', bufnr) +end + --- @class vim.lsp.completion.BufferOpts --- @field autotrigger? boolean Default: false When true, completion triggers automatically based on the server's `triggerCharacters`. --- @field convert? fun(item: lsp.CompletionItem): table Transforms an LSP CompletionItem to |complete-items|. @@ -639,8 +645,7 @@ local function enable_completions(client_id, bufnr, opts) }) -- Set up autocommands. - local group = - api.nvim_create_augroup(string.format('nvim.lsp.completion_%d', bufnr), { clear = true }) + local group = api.nvim_create_augroup(get_augroup(bufnr), { clear = true }) api.nvim_create_autocmd('CompleteDone', { group = group, buffer = bufnr, @@ -708,7 +713,7 @@ local function disable_completions(client_id, bufnr) handle.clients[client_id] = nil if not next(handle.clients) then buf_handles[bufnr] = nil - api.nvim_del_augroup_by_name(string.format('vim/lsp/completion-%d', bufnr)) + api.nvim_del_augroup_by_name(get_augroup(bufnr)) else for char, clients in pairs(handle.triggers) do --- @param c vim.lsp.Client -- cgit From f50f86b9ff5dd2aab7838801d3c1cad898ea0c77 Mon Sep 17 00:00:00 2001 From: Konrad Malik Date: Mon, 20 Jan 2025 17:17:46 +0100 Subject: fix(treesitter): compute folds on_changedtree only if not nil --- runtime/lua/vim/treesitter/_fold.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index 4a571bbaf7..f8d18d8427 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -387,7 +387,9 @@ function M.foldexpr(lnum) parser:register_cbs({ on_changedtree = function(tree_changes) - on_changedtree(bufnr, foldinfos[bufnr], tree_changes) + if foldinfos[bufnr] then + on_changedtree(bufnr, foldinfos[bufnr], tree_changes) + end end, on_bytes = function(_, _, start_row, start_col, _, old_row, old_col, _, new_row, new_col, _) -- cgit From 4cced601c8cdfd6253266b035667dd0383a07ebe Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 17 Jan 2025 13:44:07 +0100 Subject: feat(extmark): stack multiple highlight groups in `hl_group` This has been possible in the "backend" for a while but API was missing. Followup: we will need a `details2=true` mode for `nvim_get_hl_id_by_name` to return information in a way forward compatible with even further enhancements. --- runtime/lua/vim/_meta/api.lua | 3 +++ runtime/lua/vim/_meta/api_keysets.lua | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 670e867c1e..3ffbc89b08 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -595,6 +595,9 @@ function vim.api.nvim_buf_line_count(buffer) end --- - 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()`. +--- +--- Multiple highlight groups can be stacked by passing an array (highest +--- priority last). --- - 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 diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 98e916115e..26c2c963de 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -241,7 +241,7 @@ error('Cannot require a meta file') --- @field end_line? integer --- @field end_row? integer --- @field end_col? integer ---- @field hl_group? integer|string +--- @field hl_group? any --- @field virt_text? any[] --- @field virt_text_pos? string --- @field virt_text_win_col? integer -- cgit From 22fd52325bf60cadaf24bca328a602764f53d6a9 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Wed, 22 Jan 2025 07:02:30 -0600 Subject: fix(inspector): update semantic token namespace (#32157) This updates the extmark namespace to search for when filtering out semantic tokens to match the new namespace style recently introduced. --- runtime/lua/vim/_inspector.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua index 15b0733671..b0eb1d663b 100644 --- a/runtime/lua/vim/_inspector.lua +++ b/runtime/lua/vim/_inspector.lua @@ -128,13 +128,13 @@ function vim.inspect_pos(bufnr, row, col, filter) if filter.semantic_tokens then results.semantic_tokens = vim.tbl_filter(function(extmark) - return extmark.ns:find('vim_lsp_semantic_tokens') == 1 + return extmark.ns:find('nvim.lsp.semantic_tokens') == 1 end, extmarks) end if filter.extmarks then results.extmarks = vim.tbl_filter(function(extmark) - return extmark.ns:find('vim_lsp_semantic_tokens') ~= 1 + return extmark.ns:find('nvim.lsp.semantic_tokens') ~= 1 and (filter.extmarks == 'all' or extmark.opts.hl_group) end, extmarks) end -- cgit From af0ef2ac9dd19b7c4005a3197334d3e7d554646c Mon Sep 17 00:00:00 2001 From: Siddhant Agarwal <68201519+siddhantdev@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:16:24 +0530 Subject: feat(lua): vim.hl.range() "timeout" #32012 Problem: `vim.hl.on_yank()` has a "timeout" behavior but this is not available for `vim.hl.range()`. Solution: Add `timeout` arg to `vim.hl.range()`. --- runtime/lua/vim/hl.lua | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/hl.lua b/runtime/lua/vim/hl.lua index f5ace7fdc5..070748d31e 100644 --- a/runtime/lua/vim/hl.lua +++ b/runtime/lua/vim/hl.lua @@ -17,6 +17,9 @@ M.priorities = { user = 200, } +local range_timer --- @type uv.uv_timer_t? +local range_hl_clear --- @type fun()? + --- @class vim.hl.range.Opts --- @inlinedoc --- @@ -31,6 +34,10 @@ M.priorities = { --- Highlight priority --- (default: `vim.hl.priorities.user`) --- @field priority? integer +--- +--- Time in ms before highlight is cleared +--- (default: -1 no timeout) +--- @field timeout? integer --- Apply highlight group to range of text. --- @@ -45,6 +52,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts) local regtype = opts.regtype or 'v' local inclusive = opts.inclusive or false local priority = opts.priority or M.priorities.user + local timeout = opts.timeout or -1 local v_maxcol = vim.v.maxcol @@ -100,6 +108,19 @@ function M.range(bufnr, ns, higroup, start, finish, opts) end end + if range_timer and not range_timer:is_closing() then + range_timer:close() + assert(range_hl_clear) + range_hl_clear() + end + + range_hl_clear = function() + range_timer = nil + range_hl_clear = nil + pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns, 0, -1) + pcall(vim.api.nvim__ns_set, { wins = {} }) + end + for _, res in ipairs(region) do local start_row = res[1][2] - 1 local start_col = res[1][3] - 1 @@ -113,11 +134,13 @@ function M.range(bufnr, ns, higroup, start, finish, opts) strict = false, }) end + + if timeout ~= -1 then + range_timer = vim.defer_fn(range_hl_clear, timeout) + end end local yank_ns = api.nvim_create_namespace('nvim.hlyank') -local yank_timer --- @type uv.uv_timer_t? -local yank_cancel --- @type fun()? --- Highlight the yanked text during a |TextYankPost| event. --- @@ -152,31 +175,17 @@ function M.on_yank(opts) end local higroup = opts.higroup or 'IncSearch' - local timeout = opts.timeout or 150 local bufnr = vim.api.nvim_get_current_buf() local winid = vim.api.nvim_get_current_win() - if yank_timer then - yank_timer:close() - assert(yank_cancel) - yank_cancel() - end vim.api.nvim__ns_set(yank_ns, { wins = { winid } }) M.range(bufnr, yank_ns, higroup, "'[", "']", { regtype = event.regtype, inclusive = event.inclusive, priority = opts.priority or M.priorities.user, + timeout = opts.timeout or 150, }) - - yank_cancel = function() - yank_timer = nil - yank_cancel = nil - pcall(vim.api.nvim_buf_clear_namespace, bufnr, yank_ns, 0, -1) - pcall(vim.api.nvim__ns_set, { wins = {} }) - end - - yank_timer = vim.defer_fn(yank_cancel, timeout) end return M -- cgit From 4c9f3689a1c0646c8d743a2958af286b05c04ac5 Mon Sep 17 00:00:00 2001 From: phanium <91544758+phanen@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:39:26 +0800 Subject: fix(checkhealth): failed if 'lua' in plugin name --- runtime/lua/vim/health.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index ee376f3a11..3268c82613 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -118,7 +118,7 @@ local function filepath_to_healthcheck(path) func = 'health#' .. name .. '#check' filetype = 'v' else - local subpath = path:gsub('.*lua/', '') + local subpath = path:gsub('.*/lua/', '') if vim.fs.basename(subpath) == 'health.lua' then -- */health.lua name = vim.fs.dirname(subpath) -- cgit From 28998e1f8a9cdca27ada7030757b7a47e99ce5b6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 Jan 2025 16:33:41 +0800 Subject: vim-patch:9.1.1045: filetype: N-Tripels and TriG files are not recognized (#32170) Problem: filetype: N-Tripels and TriG files are not recognized Solution: detect '*.nt' files as ntriples filetype and '*.trig' files as trig filetype (Gordian Dziwis) closes: vim/vim#16493 https://github.com/vim/vim/commit/c04334c33f543a6b84a4442cf235d84f5eaef6bb Co-authored-by: Gordian Dziwis --- runtime/lua/vim/filetype.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index efc41269f8..37e6227090 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -845,6 +845,7 @@ local extension = { tr = 'nroff', nsi = 'nsis', nsh = 'nsis', + nt = 'ntriples', nu = 'nu', obj = 'obj', objdump = 'objdump', @@ -1240,6 +1241,7 @@ local extension = { toml = 'toml', tpp = 'tpp', treetop = 'treetop', + trig = 'trig', slt = 'tsalt', tsscl = 'tsscl', tssgm = 'tssgm', -- cgit From 8634bd46b26f28fa26950128b0cc585560bd6a9a Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 20 Jan 2025 22:45:47 +0100 Subject: vim-patch:9.1.1042: filetype: just files are not recognized Problem: filetype: just files are not recognized Solution: adjust filetype detection pattern, detect just shebang line, include just ftplugin, indent and syntax plugin (Peter Benjamin) closes: vim/vim#16466 https://github.com/vim/vim/commit/72755b3c8e91ec90447969b736f080e0de36003d Co-authored-by: Peter Benjamin --- runtime/lua/vim/filetype.lua | 5 +++++ runtime/lua/vim/filetype/detect.lua | 1 + 2 files changed, 6 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 37e6227090..a8f3d18bfa 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -661,6 +661,8 @@ local extension = { jsp = 'jsp', jl = 'julia', just = 'just', + Just = 'just', + JUST = 'just', kl = 'karel', KL = 'karel', kdl = 'kdl', @@ -1650,8 +1652,11 @@ local filename = { ['.vsconfig'] = 'jsonc', ['bun.lock'] = 'jsonc', ['.justfile'] = 'just', + ['.Justfile'] = 'just', + ['.JUSTFILE'] = 'just', ['justfile'] = 'just', ['Justfile'] = 'just', + ['JUSTFILE'] = 'just', Kconfig = 'kconfig', ['Kconfig.debug'] = 'kconfig', ['Config.in'] = 'kconfig', diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 31c88c80bd..855893530e 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1877,6 +1877,7 @@ local patterns_hashbang = { ruby = 'ruby', ['node\\(js\\)\\=\\>\\|js\\>'] = { 'javascript', { vim_regex = true } }, ['rhino\\>'] = { 'javascript', { vim_regex = true } }, + just = 'just', -- BC calculator ['^bc\\>'] = { 'bc', { vim_regex = true } }, ['sed\\>'] = { 'sed', { vim_regex = true } }, -- cgit From 34d808b73cbcb0a43636d826282193ab1ca8c148 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 16 Jan 2025 18:10:22 +0100 Subject: feat(api): combined highlights in nvim_eval_statusline() Problem: Combined highlighting was not applied to nvim_eval_statusline(), and 'statuscolumn' sign segment/numhl highlights. Solution: Add an additional `groups` element to the return value of `nvim_eval_statusline()->highlights`. This is an array of stacked highlight groups (highest priority last). Also resolve combined highlights for the 'statuscolumn' sign segment/numhl highlights. Expose/synchronize some drawline.c logic that is now mimicked in three different places. --- runtime/lua/vim/_meta/api.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 2f9ab5b846..6d9a17ea2b 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1131,7 +1131,9 @@ function vim.api.nvim_eval(expr) end --- the "highlights" key in {opts} is true. Each element of the array is a --- |Dict| with these keys: --- - start: (number) Byte index (0-based) of first character that uses the highlight. ---- - group: (string) Name of highlight group. +--- - group: (string) Name of highlight group. May be removed in the future, use +--- `groups` instead. +--- - groups: (array) Names of stacked highlight groups (highest priority last). function vim.api.nvim_eval_statusline(str, opts) end --- @deprecated -- cgit From a450fda4ededdd93e3dc571d82a6737f6b9d50d9 Mon Sep 17 00:00:00 2001 From: Evgeni Chasnovski Date: Thu, 23 Jan 2025 10:42:00 +0200 Subject: fix(lsp): prefer `on_list` over `loclist` in default handler Problem: setting `loclist = true` makes `on_list` being ignored. This was not a problem before, but with `vim.lsp.buf.document_symbol` using `loclist = true` as default it is needed to explicitly pass `loclist = false` in order to use custom `on_list`. Solution: prefer `on_list` over `loclist` and document the latter as taking effect only in the default handler. --- runtime/lua/vim/lsp/buf.lua | 2 +- runtime/lua/vim/lsp/handlers.lua | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8efc6996dd..c57fdbee18 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -254,7 +254,7 @@ end --- ``` --- @field on_list? fun(t: vim.lsp.LocationOpts.OnList) --- ---- Whether to use the |location-list| or the |quickfix| list. +--- Whether to use the |location-list| or the |quickfix| list in the default handler. --- ```lua --- vim.lsp.buf.definition({ loclist = true }) --- vim.lsp.buf.references(nil, { loclist = false }) diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 5da4033f89..a86ea99413 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -247,12 +247,12 @@ local function response_to_list(map_result, entity, title_fn) local items = map_result(result, ctx.bufnr) local list = { title = title, items = items, context = ctx } - if config.loclist then - vim.fn.setloclist(0, {}, ' ', list) - vim.cmd.lopen() - elseif config.on_list then + if config.on_list then assert(vim.is_callable(config.on_list), 'on_list is not a function') config.on_list(list) + elseif config.loclist then + vim.fn.setloclist(0, {}, ' ', list) + vim.cmd.lopen() else vim.fn.setqflist({}, ' ', list) vim.cmd('botright copen') -- cgit From 931ee5591fa764a769946318e05062098baf7c21 Mon Sep 17 00:00:00 2001 From: georgev93 <39860568+georgev93@users.noreply.github.com> Date: Fri, 24 Jan 2025 22:57:45 -0500 Subject: feat(extmarks): virtual text can be right-aligned, truncated #31921 Problem: Right aligned virtual text can cover up buffer text if virtual text is too long Solution: An additional option for `virt_text_pos` called `eol_right_align` has been added to truncate virtual text if it would have otherwise covered up buffer text. This ensures the virtual text extends no further left than EOL. --- runtime/lua/vim/_meta/api.lua | 9 +++++++++ runtime/lua/vim/diagnostic.lua | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 6d9a17ea2b..50fb7e4f9d 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -589,6 +589,15 @@ function vim.api.nvim_buf_line_count(buffer) end --- (highest priority last). --- - virt_text_pos : position of virtual text. Possible values: --- - "eol": right after eol character (default). +--- - "eol_right_align": display right aligned in the window +--- unless the virtual text is longer than +--- the space available. If the virtual +--- text is too long, it is truncated to +--- fit in the window after the EOL +--- character. If the line is wrapped, the +--- virtual text is shown after the end of +--- the line rather than the previous +--- screen line. --- - "overlay": display over the specified column, without --- shifting the underlying text. --- - "right_align": display right aligned in the window. diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index ead75f7d51..04118999cf 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -220,7 +220,7 @@ end --- @field virt_text? [string,any][] --- --- See |nvim_buf_set_extmark()|. ---- @field virt_text_pos? 'eol'|'overlay'|'right_align'|'inline' +--- @field virt_text_pos? 'eol'|'eol_right_align'|'inline'|'overlay'|'right_align' --- --- See |nvim_buf_set_extmark()|. --- @field virt_text_win_col? integer -- cgit From 63aa167f944b147b9d4b8c417a37f4beb212d984 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 24 Jan 2025 06:44:23 +0800 Subject: vim-patch:9.1.1049: insert-completed items are always sorted Problem: insert-completed items are always sorted, although the LSP spec[1] standard defines sortText in the returned completionitem list. This means that the server has sorted the results. When fuzzy is enabled, this will break the server's sorting results. Solution: disable sorting of candidates when "nosort" is set in 'completeopt' [1] https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItem closes: vim/vim#16501 https://github.com/vim/vim/commit/f400a0cc41113eb75516bdd7f38aeaa15208ba2c Co-authored-by: glepnir --- runtime/lua/vim/_meta/options.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 107b1ffdfb..f0f0d1a768 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -1098,6 +1098,10 @@ vim.go.cia = vim.go.completeitemalign --- list of alternatives, but not how the candidates are --- collected (using different completion types). --- +--- nosort Disable sorting of completion candidates based on fuzzy +--- scores when "fuzzy" is enabled. Candidates will appear +--- in their original order. +--- --- @type string vim.o.completeopt = "menu,preview" vim.o.cot = vim.o.completeopt -- cgit From 974a3f7a438bb772bd681987b25214784c52c7da Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 25 Jan 2025 15:41:29 +0100 Subject: vim-patch:9918120: runtime(filetype): Improve Verilog detection by checking for modules definition While at it, also increase the maximum number of lines to check to 500. fixes: vim/vim#16513 https://github.com/vim/vim/commit/99181205c5f8284a30f839107a12932924168f17 Co-authored-by: Christian Brabandt --- runtime/lua/vim/filetype/detect.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 855893530e..705ce33872 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1763,7 +1763,7 @@ function M.v(_, bufnr) return vim.g.filetype_v end local in_comment = 0 - for _, line in ipairs(getlines(bufnr, 1, 200)) do + for _, line in ipairs(getlines(bufnr, 1, 500)) do if line:find('^%s*/%*') then in_comment = 1 end @@ -1777,7 +1777,7 @@ function M.v(_, bufnr) or line:find('%(%*') and not line:find('/[/*].*%(%*') then return 'coq' - elseif findany(line, { ';%s*$', ';%s*/[/*]' }) then + elseif findany(line, { ';%s*$', ';%s*/[/*]', '^%s*module' }) then return 'verilog' end end -- cgit From b8e947ed4ed04f9aeef471f579451bbf2bb2993d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 25 Jan 2025 16:26:13 +0100 Subject: vim-patch:fb49e3c: runtime(filetype): commit 99181205c5f8284a3 breaks V lang detection so make the regex more strict and have it check for a parenthesis. See: https://github.com/vlang/v/blob/master/examples/submodule/mymodules/submodule/sub_functions.v related: vim/vim#16513 https://github.com/vim/vim/commit/fb49e3cde79de4ce558c86d21a56eb9d60aeabd5 Co-authored-by: Christian Brabandt --- runtime/lua/vim/filetype/detect.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 705ce33872..8f66418733 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1777,7 +1777,7 @@ function M.v(_, bufnr) or line:find('%(%*') and not line:find('/[/*].*%(%*') then return 'coq' - elseif findany(line, { ';%s*$', ';%s*/[/*]', '^%s*module' }) then + elseif findany(line, { ';%s*$', ';%s*/[/*]', '^%s*module%s+%w+%s*%(' }) then return 'verilog' end end -- cgit From d84a95da7e11555376a0ce60a0d4b5fbe15892d3 Mon Sep 17 00:00:00 2001 From: glepnir Date: Mon, 27 Jan 2025 07:28:33 +0800 Subject: feat(api): nvim_get_autocmds filter by id#31549 Problem: nvim_get_autocmds cannot filter by id. Solution: Support it. --- runtime/lua/vim/_meta/api.lua | 33 +++++++++++++++++---------------- runtime/lua/vim/_meta/api_keysets.lua | 1 + 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 50fb7e4f9d..c7d5db60b1 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1246,27 +1246,28 @@ function vim.api.nvim_get_all_options_info() end --- match any combination of them. --- --- @param opts vim.api.keyset.get_autocmds Dict with at least one of the following: ---- - group (string|integer): the autocommand group name or id to match against. ---- - event (string|array): event or events to match against `autocmd-events`. ---- - pattern (string|array): pattern or patterns to match against `autocmd-pattern`. ---- Cannot be used with {buffer} ---- - buffer: Buffer number or list of buffer numbers for buffer local autocommands +--- - buffer: (integer) Buffer number or list of buffer numbers for buffer local autocommands --- `autocmd-buflocal`. Cannot be used with {pattern} +--- - event: (string|table) event or events to match against `autocmd-events`. +--- - id: (integer) Autocommand ID to match. +--- - group: (string|table) the autocommand group name or id to match against. +--- - pattern: (string|table) pattern or patterns to match against `autocmd-pattern`. +--- Cannot be used with {buffer} --- @return vim.api.keyset.get_autocmds.ret[] # Array of autocommands matching the criteria, with each item --- containing the following fields: ---- - id (number): the autocommand id (only when defined with the API). ---- - group (integer): the autocommand group id. ---- - group_name (string): the autocommand group name. ---- - desc (string): the autocommand description. ---- - event (string): the autocommand event. ---- - command (string): the autocommand command. Note: this will be empty if a callback is set. ---- - callback (function|string|nil): Lua function or name of a Vim script function +--- - buffer: (integer) the buffer number. +--- - buflocal: (boolean) true if the autocommand is buffer local. +--- - command: (string) the autocommand command. Note: this will be empty if a callback is set. +--- - callback: (function|string|nil): Lua function or name of a Vim script function --- which is executed when this autocommand is triggered. ---- - once (boolean): whether the autocommand is only run once. ---- - pattern (string): the autocommand pattern. +--- - desc: (string) the autocommand description. +--- - event: (string) the autocommand event. +--- - id: (integer) the autocommand id (only when defined with the API). +--- - group: (integer) the autocommand group id. +--- - group_name: (string) the autocommand group name. +--- - once: (boolean) whether the autocommand is only run once. +--- - pattern: (string) the autocommand pattern. --- If the autocommand is buffer local |autocmd-buffer-local|: ---- - buflocal (boolean): true if the autocommand is buffer local. ---- - buffer (number): the buffer number. function vim.api.nvim_get_autocmds(opts) end --- Gets information about a channel. diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 26c2c963de..4d0665872b 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -117,6 +117,7 @@ error('Cannot require a meta file') --- @field group? integer|string --- @field pattern? string|string[] --- @field buffer? integer|integer[] +--- @field id? integer --- @class vim.api.keyset.get_commands --- @field builtin? boolean -- cgit From 1759b7844a2d468310b6d0ceca899fec6f2d4b84 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sun, 26 Jan 2025 15:33:03 -0800 Subject: feat(diagnostic): virtual_lines #31959 --- runtime/lua/vim/diagnostic.lua | 276 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 04118999cf..2d86fbe38c 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -73,6 +73,10 @@ end --- (default: `false`) --- @field virtual_text? boolean|vim.diagnostic.Opts.VirtualText|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualText --- +--- Use virtual lines for diagnostics. +--- (default: `false`) +--- @field virtual_lines? boolean|vim.diagnostic.Opts.VirtualLines|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualLines +--- --- Use signs for diagnostics |diagnostic-signs|. --- (default: `true`) --- @field signs? boolean|vim.diagnostic.Opts.Signs|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Signs @@ -101,6 +105,7 @@ end --- @field update_in_insert boolean --- @field underline vim.diagnostic.Opts.Underline --- @field virtual_text vim.diagnostic.Opts.VirtualText +--- @field virtual_lines vim.diagnostic.Opts.VirtualLines --- @field signs vim.diagnostic.Opts.Signs --- @field severity_sort {reverse?:boolean} @@ -228,6 +233,16 @@ end --- See |nvim_buf_set_extmark()|. --- @field virt_text_hide? boolean +--- @class vim.diagnostic.Opts.VirtualLines +--- +--- Only show diagnostics for the current line. +--- (default: `false`) +--- @field current_line? boolean +--- +--- A function that takes a diagnostic as input and returns a string. +--- The return value is the text used to display the diagnostic. +--- @field format? fun(diagnostic:vim.Diagnostic): string + --- @class vim.diagnostic.Opts.Signs --- --- Only show virtual text for diagnostics matching the given @@ -313,6 +328,7 @@ local global_diagnostic_options = { signs = true, underline = true, virtual_text = false, + virtual_lines = false, float = true, update_in_insert = false, severity_sort = false, @@ -328,6 +344,7 @@ local global_diagnostic_options = { --- @class (private) vim.diagnostic.Handler --- @field show? fun(namespace: integer, bufnr: integer, diagnostics: vim.Diagnostic[], opts?: vim.diagnostic.OptsResolved) --- @field hide? fun(namespace:integer, bufnr:integer) +--- @field _augroup? integer --- @nodoc --- @type table @@ -581,6 +598,7 @@ end -- TODO(lewis6991): these highlight maps can only be indexed with an integer, however there usage -- implies they can be indexed with any vim.diagnostic.Severity local virtual_text_highlight_map = make_highlight_map('VirtualText') +local virtual_lines_highlight_map = make_highlight_map('VirtualLines') local underline_highlight_map = make_highlight_map('Underline') local floating_highlight_map = make_highlight_map('Floating') local sign_highlight_map = make_highlight_map('Sign') @@ -1603,6 +1621,264 @@ M.handlers.virtual_text = { end, } +--- Some characters (like tabs) take up more than one cell. Additionally, inline +--- virtual text can make the distance between 2 columns larger. +--- A diagnostic aligned under such characters needs to account for that and that +--- many spaces to its left. +--- @param bufnr integer +--- @param lnum integer +--- @param start_col integer +--- @param end_col integer +--- @return integer +local function distance_between_cols(bufnr, lnum, start_col, end_col) + return api.nvim_buf_call(bufnr, function() + local s = vim.fn.virtcol({ lnum + 1, start_col }) + local e = vim.fn.virtcol({ lnum + 1, end_col + 1 }) + return e - 1 - s + end) +end + +--- @param namespace integer +--- @param bufnr integer +--- @param diagnostics vim.Diagnostic[] +local function render_virtual_lines(namespace, bufnr, diagnostics) + table.sort(diagnostics, function(d1, d2) + if d1.lnum == d2.lnum then + return d1.col < d2.col + else + return d1.lnum < d2.lnum + end + end) + + api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) + + if not next(diagnostics) then + return + end + + -- This loop reads each line, putting them into stacks with some extra data since + -- rendering each line requires understanding what is beneath it. + local ElementType = { Space = 1, Diagnostic = 2, Overlap = 3, Blank = 4 } ---@enum ElementType + local line_stacks = {} ---@type table + local prev_lnum = -1 + local prev_col = 0 + for _, diag in ipairs(diagnostics) do + if not line_stacks[diag.lnum] then + line_stacks[diag.lnum] = {} + end + + local stack = line_stacks[diag.lnum] + + if diag.lnum ~= prev_lnum then + table.insert(stack, { + ElementType.Space, + string.rep(' ', distance_between_cols(bufnr, diag.lnum, 0, diag.col)), + }) + elseif diag.col ~= prev_col then + table.insert(stack, { + ElementType.Space, + string.rep( + ' ', + -- +1 because indexing starts at 0 in one API but at 1 in the other. + -- -1 for non-first lines, since the previous column was already drawn. + distance_between_cols(bufnr, diag.lnum, prev_col + 1, diag.col) - 1 + ), + }) + else + table.insert(stack, { ElementType.Overlap, diag.severity }) + end + + if diag.message:find('^%s*$') then + table.insert(stack, { ElementType.Blank, diag }) + else + table.insert(stack, { ElementType.Diagnostic, diag }) + end + + prev_lnum, prev_col = diag.lnum, diag.col + end + + local chars = { + cross = '┼', + horizontal = '─', + horizontal_up = '┴', + up_right = '└', + vertical = '│', + vertical_right = '├', + } + + for lnum, stack in pairs(line_stacks) do + local virt_lines = {} + + -- Note that we read in the order opposite to insertion. + for i = #stack, 1, -1 do + if stack[i][1] == ElementType.Diagnostic then + local diagnostic = stack[i][2] + local left = {} ---@type {[1]:string, [2]:string} + local overlap = false + local multi = false + + -- Iterate the stack for this line to find elements on the left. + for j = 1, i - 1 do + local type = stack[j][1] + local data = stack[j][2] + if type == ElementType.Space then + if multi then + ---@cast data string + table.insert(left, { + string.rep(chars.horizontal, data:len()), + virtual_lines_highlight_map[diagnostic.severity], + }) + else + table.insert(left, { data, '' }) + end + elseif type == ElementType.Diagnostic then + -- If an overlap follows this line, don't add an extra column. + if stack[j + 1][1] ~= ElementType.Overlap then + table.insert(left, { chars.vertical, virtual_lines_highlight_map[data.severity] }) + end + overlap = false + elseif type == ElementType.Blank then + if multi then + table.insert( + left, + { chars.horizontal_up, virtual_lines_highlight_map[data.severity] } + ) + else + table.insert(left, { chars.up_right, virtual_lines_highlight_map[data.severity] }) + end + multi = true + elseif type == ElementType.Overlap then + overlap = true + end + end + + local center_char ---@type string + if overlap and multi then + center_char = chars.cross + elseif overlap then + center_char = chars.vertical_right + elseif multi then + center_char = chars.horizontal_up + else + center_char = chars.up_right + end + local center = { + { + string.format('%s%s', center_char, string.rep(chars.horizontal, 4) .. ' '), + virtual_lines_highlight_map[diagnostic.severity], + }, + } + + -- We can draw on the left side if and only if: + -- a. Is the last one stacked this line. + -- b. Has enough space on the left. + -- c. Is just one line. + -- d. Is not an overlap. + local msg ---@type string + if diagnostic.code then + msg = string.format('%s: %s', diagnostic.code, diagnostic.message) + else + msg = diagnostic.message + end + for msg_line in msg:gmatch('([^\n]+)') do + local vline = {} + vim.list_extend(vline, left) + vim.list_extend(vline, center) + vim.list_extend(vline, { { msg_line, virtual_lines_highlight_map[diagnostic.severity] } }) + + table.insert(virt_lines, vline) + + -- Special-case for continuation lines: + if overlap then + center = { + { chars.vertical, virtual_lines_highlight_map[diagnostic.severity] }, + { ' ', '' }, + } + else + center = { { ' ', '' } } + end + end + end + end + + api.nvim_buf_set_extmark(bufnr, namespace, lnum, 0, { virt_lines = virt_lines }) + end +end + +--- @param diagnostics vim.Diagnostic[] +--- @param namespace integer +--- @param bufnr integer +local function render_virtual_lines_at_current_line(diagnostics, namespace, bufnr) + local line_diagnostics = {} + local lnum = api.nvim_win_get_cursor(0)[1] - 1 + + for _, diag in ipairs(diagnostics) do + if (lnum == diag.lnum) or (diag.end_lnum and lnum >= diag.lnum and lnum <= diag.end_lnum) then + table.insert(line_diagnostics, diag) + end + end + + render_virtual_lines(namespace, bufnr, line_diagnostics) +end + +M.handlers.virtual_lines = { + show = function(namespace, bufnr, diagnostics, opts) + vim.validate('namespace', namespace, 'number') + vim.validate('bufnr', bufnr, 'number') + vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') + vim.validate('opts', opts, 'table', true) + + bufnr = vim._resolve_bufnr(bufnr) + opts = opts or {} + + if not api.nvim_buf_is_loaded(bufnr) then + return + end + + local ns = M.get_namespace(namespace) + if not ns.user_data.virt_lines_ns then + ns.user_data.virt_lines_ns = + api.nvim_create_namespace(string.format('nvim.%s.diagnostic.virtual_lines', ns.name)) + end + if not M.handlers.virtual_lines._augroup then + M.handlers.virtual_lines._augroup = + api.nvim_create_augroup('nvim.lsp.diagnostic.virt_lines', { clear = true }) + end + + api.nvim_clear_autocmds({ group = M.handlers.virtual_lines._augroup }) + + if opts.virtual_lines.format then + diagnostics = reformat_diagnostics(opts.virtual_lines.format, diagnostics) + end + + if opts.virtual_lines.current_line == true then + api.nvim_create_autocmd('CursorMoved', { + buffer = bufnr, + group = M.handlers.virtual_lines._augroup, + callback = function() + render_virtual_lines_at_current_line(diagnostics, ns.user_data.virt_lines_ns, bufnr) + end, + }) + -- Also show diagnostics for the current line before the first CursorMoved event. + render_virtual_lines_at_current_line(diagnostics, ns.user_data.virt_lines_ns, bufnr) + else + render_virtual_lines(ns.user_data.virt_lines_ns, bufnr, diagnostics) + end + + save_extmarks(ns.user_data.virt_lines_ns, bufnr) + end, + hide = function(namespace, bufnr) + local ns = M.get_namespace(namespace) + if ns.user_data.virt_lines_ns then + diagnostic_cache_extmarks[bufnr][ns.user_data.virt_lines_ns] = {} + if api.nvim_buf_is_valid(bufnr) then + api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_lines_ns, 0, -1) + end + api.nvim_clear_autocmds({ group = M.handlers.virtual_lines._augroup }) + end + end, +} + --- Get virtual text chunks to display using |nvim_buf_set_extmark()|. --- --- Exported for backward compatibility with -- cgit From eb60cd74fb5caa997e6253bef6a1f0b58e1b6ec6 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 27 Jan 2025 16:16:06 +0100 Subject: build(deps)!: bump tree-sitter to HEAD, wasmtime to v29.0.1 (#32200) Breaking change: `ts_node_child_containing_descendant()` was removed Breaking change: tree-sitter 0.25 (HEAD) required --- runtime/lua/vim/treesitter/_meta/tsnode.lua | 6 ------ 1 file changed, 6 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_meta/tsnode.lua b/runtime/lua/vim/treesitter/_meta/tsnode.lua index d982b6a505..0c1b376fba 100644 --- a/runtime/lua/vim/treesitter/_meta/tsnode.lua +++ b/runtime/lua/vim/treesitter/_meta/tsnode.lua @@ -68,12 +68,6 @@ function TSNode:named_child_count() end --- @return TSNode? function TSNode:named_child(index) end ---- Get the node's child that contains {descendant}. ---- @param descendant TSNode ---- @return TSNode? ---- @deprecated -function TSNode:child_containing_descendant(descendant) end - --- Get the node's child that contains {descendant} (includes {descendant}). --- --- For example, with the following node hierarchy: -- cgit From 6aa42e8f92bd8bea49b7b2accfe4ab67a5344e41 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 24 Jan 2025 13:01:25 +0000 Subject: fix: resolve all remaining LuaLS diagnostics --- runtime/lua/vim/_editor.lua | 12 ++++++--- runtime/lua/vim/_inspector.lua | 3 +++ runtime/lua/vim/_meta/api.lua | 4 +++ runtime/lua/vim/_options.lua | 5 ++++ runtime/lua/vim/diagnostic.lua | 13 +++++++--- runtime/lua/vim/filetype.lua | 4 +-- runtime/lua/vim/filetype/detect.lua | 8 +++--- runtime/lua/vim/fs.lua | 4 +-- runtime/lua/vim/glob.lua | 1 + runtime/lua/vim/health.lua | 7 +++--- runtime/lua/vim/health/health.lua | 13 +++++++--- runtime/lua/vim/inspect.lua | 1 + runtime/lua/vim/lsp/_changetracking.lua | 2 +- runtime/lua/vim/lsp/_meta.lua | 4 +-- runtime/lua/vim/lsp/_snippet_grammar.lua | 1 + runtime/lua/vim/lsp/buf.lua | 37 +++++++++++++++++----------- runtime/lua/vim/lsp/client.lua | 12 +++++---- runtime/lua/vim/lsp/completion.lua | 2 +- runtime/lua/vim/lsp/diagnostic.lua | 3 ++- runtime/lua/vim/lsp/handlers.lua | 2 +- runtime/lua/vim/lsp/protocol.lua | 7 ------ runtime/lua/vim/lsp/semantic_tokens.lua | 2 +- runtime/lua/vim/lsp/util.lua | 6 ++++- runtime/lua/vim/provider/health.lua | 34 ++++++++++++++----------- runtime/lua/vim/re.lua | 1 + runtime/lua/vim/shared.lua | 3 +-- runtime/lua/vim/treesitter.lua | 9 ++++--- runtime/lua/vim/treesitter/_meta/misc.lua | 8 +++++- runtime/lua/vim/treesitter/_meta/tsnode.lua | 1 + runtime/lua/vim/treesitter/_query_linter.lua | 4 ++- runtime/lua/vim/treesitter/dev.lua | 10 +------- runtime/lua/vim/treesitter/language.lua | 2 +- runtime/lua/vim/treesitter/languagetree.lua | 10 +++++--- runtime/lua/vim/treesitter/query.lua | 2 ++ runtime/lua/vim/uri.lua | 5 ++-- 35 files changed, 149 insertions(+), 93 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 4b28b63746..a77ea9bb91 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -425,6 +425,7 @@ local VIM_CMD_ARG_MAX = 20 --- vim.cmd.colorscheme('blue') --- ``` --- +---@diagnostic disable-next-line: undefined-doc-param ---@param command string|table Command(s) to execute. --- If a string, executes multiple lines of Vimscript at once. In this --- case, it is an alias to |nvim_exec2()|, where `opts.output` is set @@ -441,10 +442,12 @@ vim.cmd = setmetatable({}, { return '' end end, + --- @param t table __index = function(t, command) t[command] = function(...) - local opts + local opts --- @type vim.api.keyset.cmd if select('#', ...) == 1 and type(select(1, ...)) == 'table' then + --- @type vim.api.keyset.cmd opts = select(1, ...) -- Move indexed positions in opts to opt.args @@ -455,6 +458,7 @@ vim.cmd = setmetatable({}, { break end opts.args[i] = opts[i] + --- @diagnostic disable-next-line: no-unknown opts[i] = nil end end @@ -529,7 +533,7 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) end if pos1[1] > pos2[1] or (pos1[1] == pos2[1] and pos1[2] > pos2[2]) then - pos1, pos2 = pos2, pos1 + pos1, pos2 = pos2, pos1 --- @type [integer, integer], [integer, integer] end -- getpos() may return {0,0,0,0} @@ -701,6 +705,7 @@ function vim._on_key(buf, typed_buf) local discard = false for k, v in pairs(on_key_cbs) do local fn = v[1] + --- @type boolean, any local ok, rv = xpcall(function() return fn(buf, typed_buf) end, debug.traceback) @@ -828,6 +833,7 @@ function vim.str_utfindex(s, encoding, index, strict_indexing) -- Return (multiple): ~ -- (`integer`) UTF-32 index -- (`integer`) UTF-16 index + --- @diagnostic disable-next-line: redundant-return-value return col32, col16 end @@ -1000,7 +1006,7 @@ function vim._expand_pat(pat, env) or vim.v == final_env and { 'v:', 'var' } or { nil, nil } ) - assert(prefix, "Can't resolve final_env") + assert(prefix and type, "Can't resolve final_env") local vars = vim.fn.getcompletion(prefix .. match_part, type) --- @type string[] insert_keys(vim .iter(vars) diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua index b0eb1d663b..35063dffca 100644 --- a/runtime/lua/vim/_inspector.lua +++ b/runtime/lua/vim/_inspector.lua @@ -1,3 +1,5 @@ +--- @diagnostic disable:no-unknown + --- @class vim._inspector.Filter --- @inlinedoc --- @@ -78,6 +80,7 @@ function vim.inspect_pos(bufnr, row, col, filter) -- treesitter if filter.treesitter then for _, capture in pairs(vim.treesitter.get_captures_at_pos(bufnr, row, col)) do + --- @diagnostic disable-next-line: inject-field capture.hl_group = '@' .. capture.capture .. '.' .. capture.lang results.treesitter[#results.treesitter + 1] = resolve_hl(capture) end diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index c7d5db60b1..3d10729d23 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -3,6 +3,10 @@ -- DO NOT EDIT error('Cannot require a meta file') +--- This file embeds vimdoc as the function descriptions +--- so ignore any doc related errors. +--- @diagnostic disable: undefined-doc-name,luadoc-miss-symbol + vim.api = {} --- @private diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua index 8338c5ead7..973ad87ee8 100644 --- a/runtime/lua/vim/_options.lua +++ b/runtime/lua/vim/_options.lua @@ -688,6 +688,7 @@ local function remove_value(info, current, new) end local function create_option_accessor(scope) + --- @diagnostic disable-next-line: no-unknown local option_mt local function make_option(name, value) @@ -696,6 +697,7 @@ local function create_option_accessor(scope) if type(value) == 'table' and getmetatable(value) == option_mt then assert(name == value._name, "must be the same value, otherwise that's weird.") + --- @diagnostic disable-next-line: no-unknown value = value._value end @@ -719,6 +721,7 @@ local function create_option_accessor(scope) end, append = function(self, right) + --- @diagnostic disable-next-line: no-unknown self._value = add_value(self._info, self._value, right) self:_set() end, @@ -728,6 +731,7 @@ local function create_option_accessor(scope) end, prepend = function(self, right) + --- @diagnostic disable-next-line: no-unknown self._value = prepend_value(self._info, self._value, right) self:_set() end, @@ -737,6 +741,7 @@ local function create_option_accessor(scope) end, remove = function(self, right) + --- @diagnostic disable-next-line: no-unknown self._value = remove_value(self._info, self._value, right) self:_set() end, diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 2d86fbe38c..2538cd3048 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -891,6 +891,7 @@ local function set_list(loclist, opts) if open then if not loclist then -- First navigate to the diagnostics quickfix list. + --- @type integer local nr = vim.fn.getqflist({ id = qf_id, nr = 0 }).nr api.nvim_command(('silent %dchistory'):format(nr)) @@ -2250,18 +2251,24 @@ function M.open_float(opts, ...) if not opts.focus_id then opts.focus_id = scope end + + --- @diagnostic disable-next-line: param-type-mismatch local float_bufnr, winnr = vim.lsp.util.open_floating_preview(lines, 'plaintext', opts) vim.bo[float_bufnr].path = vim.bo[bufnr].path + + --- @diagnostic disable-next-line: deprecated + local add_highlight = api.nvim_buf_add_highlight + for i, hl in ipairs(highlights) do local line = lines[i] local prefix_len = hl.prefix and hl.prefix.length or 0 local suffix_len = hl.suffix and hl.suffix.length or 0 if prefix_len > 0 then - api.nvim_buf_add_highlight(float_bufnr, -1, hl.prefix.hlname, i - 1, 0, prefix_len) + add_highlight(float_bufnr, -1, hl.prefix.hlname, i - 1, 0, prefix_len) end - api.nvim_buf_add_highlight(float_bufnr, -1, hl.hlname, i - 1, prefix_len, #line - suffix_len) + add_highlight(float_bufnr, -1, hl.hlname, i - 1, prefix_len, #line - suffix_len) if suffix_len > 0 then - api.nvim_buf_add_highlight(float_bufnr, -1, hl.suffix.hlname, i - 1, #line - suffix_len, -1) + add_highlight(float_bufnr, -1, hl.suffix.hlname, i - 1, #line - suffix_len, -1) end end diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index a8f3d18bfa..cc7358ee49 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -732,8 +732,8 @@ local extension = { mc = detect.mc, quake = 'm3quake', m4 = function(path, bufnr) - path = path:lower() - return not (path:find('html%.m4$') or path:find('fvwm2rc')) and 'm4' or nil + local pathl = path:lower() + return not (pathl:find('html%.m4$') or pathl:find('fvwm2rc')) and 'm4' or nil end, eml = 'mail', mk = detect.make, diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 8f66418733..91c0406dc0 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -881,7 +881,7 @@ end --- (refactor of filetype.vim since the patterns are case-insensitive) --- @type vim.filetype.mapfn function M.log(path, _) - path = path:lower() + path = path:lower() --- @type string LuaLS bug if findany( path, @@ -1167,7 +1167,7 @@ end --- @type vim.filetype.mapfn function M.perl(path, bufnr) local dir_name = vim.fs.dirname(path) - if fn.expand(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then + if fn.fnamemodify(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then return 'perl' end local first_line = getline(bufnr, 1) @@ -1375,7 +1375,7 @@ end local udev_rules_pattern = '^%s*udev_rules%s*=%s*"([%^"]+)/*".*' --- @type vim.filetype.mapfn function M.rules(path) - path = path:lower() + path = path:lower() --- @type string LuaLS bug if findany(path, { '/etc/udev/.*%.rules$', @@ -1398,7 +1398,7 @@ function M.rules(path) if not ok then return 'hog' end - local dir = fn.expand(path, ':h') + local dir = fn.fnamemodify(path, ':h') for _, line in ipairs(config_lines) do local match = line:match(udev_rules_pattern) if match then diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 5940fa4386..8b4242223a 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -546,7 +546,7 @@ local function expand_home(path, sep) home = home:sub(1, -2) end - path = home .. path:sub(2) + path = home .. path:sub(2) --- @type string end return path @@ -620,7 +620,7 @@ function M.normalize(path, opts) -- Expand environment variables if `opts.expand_env` isn't `false` if opts.expand_env == nil or opts.expand_env then - path = path:gsub('%$([%w_]+)', uv.os_getenv) + path = path:gsub('%$([%w_]+)', uv.os_getenv) --- @type string end if win then diff --git a/runtime/lua/vim/glob.lua b/runtime/lua/vim/glob.lua index 4f86d5e1ca..242c70d4b2 100644 --- a/runtime/lua/vim/glob.lua +++ b/runtime/lua/vim/glob.lua @@ -53,6 +53,7 @@ function M.to_lpeg(pattern) end -- luacheck: pop + --- @diagnostic disable-next-line: missing-fields local p = P({ 'Pattern', Pattern = V('Elem') ^ -1 * V('End'), diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 3268c82613..a265e2b901 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -126,7 +126,7 @@ local function filepath_to_healthcheck(path) -- */health/init.lua name = vim.fs.dirname(vim.fs.dirname(subpath)) end - name = name:gsub('/', '.') + name = assert(name:gsub('/', '.')) --- @type string func = 'require("' .. name .. '.health").check()' filetype = 'l' @@ -235,7 +235,7 @@ local function format_report_message(status, msg, ...) -- Report each suggestion for _, v in ipairs(varargs) do if v then - output = output .. '\n - ' .. indent_after_line1(v, 6) + output = output .. '\n - ' .. indent_after_line1(v, 6) --- @type string end end end @@ -445,8 +445,7 @@ function M._check(mods, plugin_names) -- Quit with 'q' inside healthcheck buffers. vim.keymap.set('n', 'q', function() - local ok, _ = pcall(vim.cmd.close) - if not ok then + if not pcall(vim.cmd.close) then vim.cmd.bdelete() end end, { buffer = bufnr, silent = true, noremap = true, nowait = true }) diff --git a/runtime/lua/vim/health/health.lua b/runtime/lua/vim/health/health.lua index d226f35f9a..dd6fe7f608 100644 --- a/runtime/lua/vim/health/health.lua +++ b/runtime/lua/vim/health/health.lua @@ -183,13 +183,16 @@ end local function check_rplugin_manifest() health.start('Remote Plugins') - local existing_rplugins = {} - for _, item in ipairs(vim.fn['remote#host#PluginsForHost']('python3')) do + local existing_rplugins = {} --- @type table + --- @type {path:string}[] + local items = vim.fn['remote#host#PluginsForHost']('python3') + for _, item in ipairs(items) do existing_rplugins[item.path] = 'python3' end local require_update = false local handle_path = function(path) + --- @type string[] local python_glob = vim.fn.glob(path .. '/rplugin/python*', true, true) if vim.tbl_isempty(python_glob) then return @@ -198,6 +201,7 @@ local function check_rplugin_manifest() local python_dir = python_glob[1] local python_version = vim.fs.basename(python_dir) + --- @type string[] local scripts = vim.fn.glob(python_dir .. '/*.py', true, true) vim.list_extend(scripts, vim.fn.glob(python_dir .. '/*/__init__.py', true, true)) @@ -227,7 +231,10 @@ local function check_rplugin_manifest() end end - for _, path in ipairs(vim.fn.map(vim.split(vim.o.runtimepath, ','), 'resolve(v:val)')) do + --- @type string[] + local paths = vim.fn.map(vim.split(vim.o.runtimepath, ','), 'resolve(v:val)') + + for _, path in ipairs(paths) do handle_path(path) end diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua index c232f69590..cdf34897d4 100644 --- a/runtime/lua/vim/inspect.lua +++ b/runtime/lua/vim/inspect.lua @@ -1,3 +1,4 @@ +--- @diagnostic disable: no-unknown local inspect = { _VERSION = 'inspect.lua 3.1.0', _URL = 'http://github.com/kikito/inspect.lua', diff --git a/runtime/lua/vim/lsp/_changetracking.lua b/runtime/lua/vim/lsp/_changetracking.lua index c2ff66b90e..265a74c8fa 100644 --- a/runtime/lua/vim/lsp/_changetracking.lua +++ b/runtime/lua/vim/lsp/_changetracking.lua @@ -64,7 +64,7 @@ local state_by_group = setmetatable({}, { ---@param client vim.lsp.Client ---@return vim.lsp.CTGroup local function get_group(client) - local allow_inc_sync = vim.F.if_nil(client.flags.allow_incremental_sync, true) --- @type boolean + local allow_inc_sync = vim.F.if_nil(client.flags.allow_incremental_sync, true) local change_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change') local sync_kind = change_capability or protocol.TextDocumentSyncKind.None if not allow_inc_sync and change_capability == protocol.TextDocumentSyncKind.Incremental then diff --git a/runtime/lua/vim/lsp/_meta.lua b/runtime/lua/vim/lsp/_meta.lua index bf693ccc57..589a49c003 100644 --- a/runtime/lua/vim/lsp/_meta.lua +++ b/runtime/lua/vim/lsp/_meta.lua @@ -1,8 +1,8 @@ ---@meta error('Cannot require a meta file') ----@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext): ...any ----@alias lsp.MultiHandler fun(results: table, context: lsp.HandlerContext): ...any +---@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext, config?: table): ...any +---@alias lsp.MultiHandler fun(results: table, context: lsp.HandlerContext, config?: table): ...any ---@class lsp.HandlerContext ---@field method string diff --git a/runtime/lua/vim/lsp/_snippet_grammar.lua b/runtime/lua/vim/lsp/_snippet_grammar.lua index 9318fefcbc..f06d6e9afd 100644 --- a/runtime/lua/vim/lsp/_snippet_grammar.lua +++ b/runtime/lua/vim/lsp/_snippet_grammar.lua @@ -127,6 +127,7 @@ local function node(type) end -- stylua: ignore +--- @diagnostic disable-next-line: missing-fields local G = P({ 'snippet'; snippet = Ct(Cg( diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index c57fdbee18..638a0d0f3f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -450,10 +450,10 @@ local function range_from_selection(bufnr, mode) -- A user can start visual selection at the end and move backwards -- Normalize the range to start < end if start_row == end_row and end_col < start_col then - end_col, start_col = start_col, end_col + end_col, start_col = start_col, end_col --- @type integer, integer elseif end_row < start_row then - start_row, end_row = end_row, start_row - start_col, end_col = end_col, start_col + start_row, end_row = end_row, start_row --- @type integer, integer + start_col, end_col = end_col, start_col --- @type integer, integer end if mode == 'V' then start_col = 1 @@ -553,25 +553,30 @@ function M.format(opts) --- @param client vim.lsp.Client --- @param params lsp.DocumentFormattingParams - --- @return lsp.DocumentFormattingParams + --- @return lsp.DocumentFormattingParams|lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams local function set_range(client, params) - local to_lsp_range = function(r) ---@return lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams + --- @param r {start:[integer,integer],end:[integer, integer]} + local function to_lsp_range(r) return util.make_given_range_params(r.start, r['end'], bufnr, client.offset_encoding).range end + local ret = params --[[@as lsp.DocumentFormattingParams|lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams]] if passed_multiple_ranges then - params.ranges = vim.tbl_map(to_lsp_range, range) + ret = params --[[@as lsp.DocumentRangesFormattingParams]] + --- @cast range {start:[integer,integer],end:[integer, integer]} + ret.ranges = vim.tbl_map(to_lsp_range, range) elseif range then - params.range = to_lsp_range(range) + ret = params --[[@as lsp.DocumentRangeFormattingParams]] + ret.range = to_lsp_range(range) end - return params + return ret end if opts.async then - --- @param idx integer - --- @param client vim.lsp.Client + --- @param idx? integer + --- @param client? vim.lsp.Client local function do_format(idx, client) - if not client then + if not idx or not client then return end local params = set_range(client, util.make_formatting_params(opts.formatting_options)) @@ -650,16 +655,16 @@ function M.rename(new_name, opts) )[1] end - --- @param idx integer + --- @param idx? integer --- @param client? vim.lsp.Client local function try_use_client(idx, client) - if not client then + if not idx or not client then return end --- @param name string local function rename(name) - local params = util.make_position_params(win, client.offset_encoding) + local params = util.make_position_params(win, client.offset_encoding) --[[@as lsp.RenameParams]] params.newName = name local handler = client.handlers[ms.textDocument_rename] or lsp.handlers[ms.textDocument_rename] @@ -1229,6 +1234,7 @@ function M.code_action(opts) for _, client in ipairs(clients) do ---@type lsp.CodeActionParams local params + if opts.range then assert(type(opts.range) == 'table', 'code_action range must be a table') local start = assert(opts.range.start, 'range must have a `start` property') @@ -1241,6 +1247,9 @@ function M.code_action(opts) else params = util.make_range_params(win, client.offset_encoding) end + + --- @cast params lsp.CodeActionParams + if context.diagnostics then params.context = context else diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index a082613bb0..253ccc48f4 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -904,18 +904,20 @@ end function Client:_get_registration(method, bufnr) bufnr = vim._resolve_bufnr(bufnr) for _, reg in ipairs(self.registrations[method] or {}) do - if not reg.registerOptions or not reg.registerOptions.documentSelector then + local regoptions = reg.registerOptions --[[@as {documentSelector:lsp.TextDocumentFilter[]}]] + if not regoptions or not regoptions.documentSelector then return reg end - local documentSelector = reg.registerOptions.documentSelector + local documentSelector = regoptions.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 + local flang, fscheme, fpat = filter.language, filter.scheme, filter.pattern 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)) + not (flang and language ~= flang) + and not (fscheme and not vim.startswith(uri, fscheme .. ':')) + and not (type(fpat) == 'string' and not vim.glob.to_lpeg(fpat):match(fname)) then return reg end diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index bdf31d8514..cf6d07745f 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -470,7 +470,7 @@ local function trigger(bufnr, clients) local server_start_boundary --- @type integer? for client_id, response in pairs(responses) do if response.err then - vim.notify_once(response.err.message, vim.log.levels.warn) + vim.notify_once(response.err.message, vim.log.levels.WARN) end local result = response.result diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index cf39338cc1..fe24928a69 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -20,7 +20,7 @@ end ---@return lsp.DiagnosticSeverity local function severity_vim_to_lsp(severity) if type(severity) == 'string' then - severity = vim.diagnostic.severity[severity] + severity = vim.diagnostic.severity[severity] --- @type integer end return severity end @@ -89,6 +89,7 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) string.format('Unsupported Markup message from LSP client %d', client_id), vim.lsp.log_levels.ERROR ) + --- @diagnostic disable-next-line: undefined-field,no-unknown message = diagnostic.message.value end local line = buf_lines and buf_lines[start.line + 1] or '' diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index a86ea99413..b35140dfad 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -47,7 +47,7 @@ RSC[ms.dollar_progress] = function(_, params, ctx) local value = params.value if type(value) == 'table' then - kind = value.kind + kind = value.kind --- @type string -- Carry over title of `begin` messages to `report` and `end` messages -- So that consumers always have it available, even if they consume a -- subset of the full sequence diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index cfd47d8f7c..fbfd0cd6b0 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -15,7 +15,6 @@ local sysname = vim.uv.os_uname().sysname --- @class vim.lsp.protocol.constants --- @nodoc local constants = { - --- @enum lsp.DiagnosticSeverity DiagnosticSeverity = { -- Reports an error. Error = 1, @@ -27,7 +26,6 @@ local constants = { Hint = 4, }, - --- @enum lsp.DiagnosticTag DiagnosticTag = { -- Unused or unnecessary code Unnecessary = 1, @@ -35,7 +33,6 @@ local constants = { Deprecated = 2, }, - ---@enum lsp.MessageType MessageType = { -- An error message. Error = 1, @@ -50,7 +47,6 @@ local constants = { }, -- The file event type. - ---@enum lsp.FileChangeType FileChangeType = { -- The file got created. Created = 1, @@ -149,7 +145,6 @@ local constants = { }, -- Represents reasons why a text document is saved. - ---@enum lsp.TextDocumentSaveReason TextDocumentSaveReason = { -- Manually triggered, e.g. by the user pressing save, by starting debugging, -- or by an API call. @@ -246,7 +241,6 @@ local constants = { -- Defines whether the insert text in a completion item should be interpreted as -- plain text or a snippet. - --- @enum lsp.InsertTextFormat InsertTextFormat = { -- The primary text to be inserted is treated as a plain string. PlainText = 1, @@ -305,7 +299,6 @@ local constants = { SourceOrganizeImports = 'source.organizeImports', }, -- The reason why code actions were requested. - ---@enum lsp.CodeActionTriggerKind CodeActionTriggerKind = { -- Code actions were explicitly requested by the user or by an extension. Invoked = 1, diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index a31202553b..dd8b654856 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -139,7 +139,7 @@ local function tokens_to_ranges(data, bufnr, client, request) if token_type then local modifiers = modifiers_from_number(data[i + 4], token_modifiers) - local end_char = start_char + data[i + 2] + local end_char = start_char + data[i + 2] --- @type integer LuaLS bug local buf_line = lines and lines[line + 1] or '' local start_col = vim.str_byteindex(buf_line, encoding, start_char, false) local end_col = vim.str_byteindex(buf_line, encoding, end_char, false) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b9b53d36a8..e16a905c44 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -49,7 +49,8 @@ local function get_border_size(opts) if not border_size[border] then border_error(border) end - return unpack(border_size[border]) + local r = border_size[border] + return r[1], r[2] end if 8 % #border ~= 0 then @@ -1897,6 +1898,7 @@ function M.make_position_params(window, position_encoding) 'position_encoding param is required in vim.lsp.util.make_position_params. Defaulting to position encoding of the first client.', vim.log.levels.WARN ) + --- @diagnostic disable-next-line: deprecated position_encoding = M._get_offset_encoding(buf) end return { @@ -1953,6 +1955,7 @@ function M.make_range_params(window, position_encoding) 'position_encoding param is required in vim.lsp.util.make_range_params. Defaulting to position encoding of the first client.', vim.log.levels.WARN ) + --- @diagnostic disable-next-line: deprecated position_encoding = M._get_offset_encoding(buf) end local position = make_position_param(window, position_encoding) @@ -1982,6 +1985,7 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, position_encoding) 'position_encoding param is required in vim.lsp.util.make_given_range_params. Defaulting to position encoding of the first client.', vim.log.levels.WARN ) + --- @diagnostic disable-next-line: deprecated position_encoding = M._get_offset_encoding(bufnr) end --- @type [integer, integer] diff --git a/runtime/lua/vim/provider/health.lua b/runtime/lua/vim/provider/health.lua index 5ecb00f49b..fa01951b02 100644 --- a/runtime/lua/vim/provider/health.lua +++ b/runtime/lua/vim/provider/health.lua @@ -10,22 +10,20 @@ end -- Attempts to construct a shell command from an args list. -- Only for display, to help users debug a failed command. +--- @param cmd string|string[] local function shellify(cmd) if type(cmd) ~= 'table' then return cmd end - local escaped = {} + local escaped = {} --- @type string[] for i, v in ipairs(cmd) do - if v:match('[^A-Za-z_/.-]') then - escaped[i] = vim.fn.shellescape(v) - else - escaped[i] = v - end + escaped[i] = v:match('[^A-Za-z_/.-]') and vim.fn.shellescape(v) or v end return table.concat(escaped, ' ') end -- Handler for s:system() function. +--- @param self {output: string, stderr: string, add_stderr_to_output: boolean} local function system_handler(self, _, data, event) if event == 'stderr' then if self.add_stderr_to_output then @@ -38,7 +36,7 @@ local function system_handler(self, _, data, event) end end ---- @param cmd table List of command arguments to execute +--- @param cmd string|string[] List of command arguments to execute --- @param args? table Optional arguments: --- - stdin (string): Data to write to the job's stdin --- - stderr (boolean): Append stderr to stdout @@ -47,8 +45,8 @@ end local function system(cmd, args) args = args or {} local stdin = args.stdin or '' - local stderr = vim.F.if_nil(args.stderr, false) - local ignore_error = vim.F.if_nil(args.ignore_error, false) + local stderr = args.stderr or false + local ignore_error = args.ignore_error or false local shell_error_code = 0 local opts = { @@ -530,13 +528,14 @@ local function version_info(python) if rc ~= 0 or nvim_version == '' then nvim_version = 'unable to find pynvim module version' local base = vim.fs.basename(nvim_path) - local metas = vim.fn.glob(base .. '-*/METADATA', true, 1) - vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', true, 1)) - vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', true, 1)) + local metas = vim.fn.glob(base .. '-*/METADATA', true, true) + vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', true, true)) + vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', true, true)) metas = table.sort(metas, compare) if metas and next(metas) ~= nil then for line in io.lines(metas[1]) do + --- @cast line string local version = line:match('^Version: (%S+)') if version then nvim_version = version @@ -762,6 +761,7 @@ local function python() -- subshells launched from Nvim. local bin_dir = iswin and 'Scripts' or 'bin' local venv_bins = vim.fn.glob(string.format('%s/%s/python*', virtual_env, bin_dir), true, true) + --- @param v string venv_bins = vim.tbl_filter(function(v) -- XXX: Remove irrelevant executables found in bin/. return not v:match('python.*%-config') @@ -809,6 +809,7 @@ local function python() msg, bin_dir, table.concat( + --- @param v string vim.tbl_map(function(v) return vim.fs.basename(v) end, venv_bins), @@ -817,12 +818,15 @@ local function python() ) end local conj = '\nBut ' + local msgs = {} --- @type string[] for _, err in ipairs(errors) do - msg = msg .. conj .. err + msgs[#msgs + 1] = msg + msgs[#msgs + 1] = conj + msgs[#msgs + 1] = err conj = '\nAnd ' end - msg = msg .. '\nSo invoking Python may lead to unexpected results.' - health.warn(msg, vim.tbl_keys(hints)) + msgs[#msgs + 1] = '\nSo invoking Python may lead to unexpected results.' + health.warn(table.concat(msgs), vim.tbl_keys(hints)) else health.info(msg) health.info( diff --git a/runtime/lua/vim/re.lua b/runtime/lua/vim/re.lua index 114f74eb80..e0a36703e3 100644 --- a/runtime/lua/vim/re.lua +++ b/runtime/lua/vim/re.lua @@ -1,3 +1,4 @@ +--- @diagnostic disable: no-unknown -- -- Copyright 2007-2023, Lua.org & PUC-Rio (see 'lpeg.html' for license) -- written by Roberto Ierusalimschy diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 02b12490af..f19533f474 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -7,8 +7,7 @@ -- so this wouldn't be a separate case to consider) ---@nodoc ----@diagnostic disable-next-line: lowercase-global -vim = vim or {} +_G.vim = _G.vim or {} ---@generic T ---@param orig T diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 0269699dfd..10638e10d8 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -149,7 +149,7 @@ end --- Returns the node's range or an unpacked range table --- ----@param node_or_range (TSNode | table) Node or table of positions +---@param node_or_range TSNode|Range4 Node or table of positions --- ---@return integer start_row ---@return integer start_col @@ -157,7 +157,8 @@ end ---@return integer end_col function M.get_node_range(node_or_range) if type(node_or_range) == 'table' then - return unpack(node_or_range) + --- @cast node_or_range -TSNode LuaLS bug + return M._range.unpack4(node_or_range) else return node_or_range:range(false) end @@ -238,7 +239,9 @@ function M.node_contains(node, range) -- allow a table so nodes can be mocked vim.validate('node', node, { 'userdata', 'table' }) vim.validate('range', range, M._range.validate, 'integer list with 4 or 6 elements') - return M._range.contains({ node:range() }, range) + --- @diagnostic disable-next-line: missing-fields LuaLS bug + local nrange = { node:range() } --- @type Range4 + return M._range.contains(nrange, range) end --- Returns a list of highlight captures at the given position diff --git a/runtime/lua/vim/treesitter/_meta/misc.lua b/runtime/lua/vim/treesitter/_meta/misc.lua index 33701ef254..c532257f49 100644 --- a/runtime/lua/vim/treesitter/_meta/misc.lua +++ b/runtime/lua/vim/treesitter/_meta/misc.lua @@ -20,9 +20,15 @@ error('Cannot require a meta file') ---@class (exact) TSQueryInfo ---@field captures string[] ---@field patterns table +--- +---@class TSLangInfo +---@field fields string[] +---@field symbols table +---@field _wasm boolean +---@field _abi_version integer --- @param lang string ---- @return table +--- @return TSLangInfo vim._ts_inspect_language = function(lang) end ---@return integer diff --git a/runtime/lua/vim/treesitter/_meta/tsnode.lua b/runtime/lua/vim/treesitter/_meta/tsnode.lua index 0c1b376fba..b261b87253 100644 --- a/runtime/lua/vim/treesitter/_meta/tsnode.lua +++ b/runtime/lua/vim/treesitter/_meta/tsnode.lua @@ -104,6 +104,7 @@ function TSNode:end_() end --- - end column --- - end byte (if {include_bytes} is `true`) --- @param include_bytes boolean? +--- @return integer, integer, integer, integer function TSNode:range(include_bytes) end --- @nodoc diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua index f6645beb28..3dfc6b0cfe 100644 --- a/runtime/lua/vim/treesitter/_query_linter.lua +++ b/runtime/lua/vim/treesitter/_query_linter.lua @@ -138,7 +138,9 @@ local function lint_match(buf, match, query, lang_context, diagnostics) -- perform language-independent checks only for first lang if lang_context.is_first_lang and cap_id == 'error' then local node_text = vim.treesitter.get_node_text(node, buf):gsub('\n', ' ') - add_lint_for_node(diagnostics, { node:range() }, 'Syntax error: ' .. node_text) + ---@diagnostic disable-next-line: missing-fields LuaLS varargs bug + local range = { node:range() } --- @type Range4 + add_lint_for_node(diagnostics, range, 'Syntax error: ' .. node_text) end -- other checks rely on Neovim parser introspection diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 42c25dbdad..ab08e1a527 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -137,14 +137,6 @@ end local decor_ns = api.nvim_create_namespace('nvim.treesitter.dev') ----@param range Range4 ----@return string -local function range_to_string(range) - ---@type integer, integer, integer, integer - local row, col, end_row, end_col = unpack(range) - return string.format('[%d, %d] - [%d, %d]', row, col, end_row, end_col) -end - ---@param w integer ---@return boolean closed Whether the window was closed. local function close_win(w) @@ -227,7 +219,7 @@ function TSTreeView:draw(bufnr) local lang_hl_marks = {} ---@type table[] for i, item in self:iter() do - local range_str = range_to_string({ item.node:range() }) + local range_str = ('[%d, %d] - [%d, %d]'):format(item.node:range()) local lang_str = self.opts.lang and string.format(' %s', item.lang) or '' local text ---@type string diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 238a078703..16d19bfc5a 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -175,7 +175,7 @@ end --- (`"`). --- ---@param lang string Language ----@return table +---@return TSLangInfo function M.inspect(lang) M.add(lang) return vim._ts_inspect_language(lang) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 3db7fe5c9e..ecace67419 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -123,7 +123,7 @@ function LanguageTree.new(source, lang, opts) local injections = opts.injections or {} - --- @type vim.treesitter.LanguageTree + --- @class vim.treesitter.LanguageTree local self = { _source = source, _lang = lang, @@ -190,7 +190,7 @@ end ---Measure execution time of a function ---@generic R1, R2, R3 ----@param f fun(): R1, R2, R2 +---@param f fun(): R1, R2, R3 ---@return number, R1, R2, R3 local function tcall(f, ...) local start = vim.uv.hrtime() @@ -198,6 +198,7 @@ local function tcall(f, ...) local r = { f(...) } --- @type number local duration = (vim.uv.hrtime() - start) / 1000000 + --- @diagnostic disable-next-line: redundant-return-value return duration, unpack(r) end @@ -550,14 +551,14 @@ function LanguageTree:_parse(range, timeout) local no_regions_parsed = 0 local query_time = 0 local total_parse_time = 0 - local is_finished --- @type boolean -- At least 1 region is invalid if not self:is_valid(true) then + local is_finished changes, no_regions_parsed, total_parse_time, is_finished = self:_parse_regions(range, timeout) timeout = timeout and math.max(timeout - total_parse_time, 0) if not is_finished then - return self._trees, is_finished + return self._trees, false end -- Need to run injections when we parsed something if no_regions_parsed > 0 then @@ -740,6 +741,7 @@ function LanguageTree:set_included_regions(new_regions) if type(range) == 'table' and #range == 4 then region[i] = Range.add_bytes(self._source, range --[[@as Range4]]) elseif type(range) == 'userdata' then + --- @diagnostic disable-next-line: missing-fields LuaLS varargs bug region[i] = { range:range(true) } end end diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index e43d0a8ad4..8055270a7f 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -262,6 +262,7 @@ local explicit_queries = setmetatable({}, { ---@param query_name string Name of the query (e.g., "highlights") ---@param text string Query text (unparsed). function M.set(lang, query_name, text) + --- @diagnostic disable-next-line: undefined-field LuaLS bad at generics M.get:clear(lang, query_name) explicit_queries[lang][query_name] = M.parse(lang, text) end @@ -291,6 +292,7 @@ api.nvim_create_autocmd('OptionSet', { pattern = { 'runtimepath' }, group = api.nvim_create_augroup('nvim.treesitter.query_cache_reset', { clear = true }), callback = function() + --- @diagnostic disable-next-line: undefined-field LuaLS bad at generics M.get:clear() end, }) diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index 6323f61256..8b6f1a61ee 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -60,9 +60,10 @@ end ---@param path string Path to file ---@return string URI function M.uri_from_fname(path) - local volume_path, fname = path:match('^([a-zA-Z]:)(.*)') ---@type string? + local volume_path, fname = path:match('^([a-zA-Z]:)(.*)') ---@type string?, string? local is_windows = volume_path ~= nil if is_windows then + assert(fname) path = volume_path .. M.uri_encode(fname:gsub('\\', '/')) else path = M.uri_encode(path) @@ -111,7 +112,7 @@ function M.uri_to_fname(uri) uri = M.uri_decode(uri) --TODO improve this. if is_windows_file_uri(uri) then - uri = uri:gsub('^file:/+', ''):gsub('/', '\\') + uri = uri:gsub('^file:/+', ''):gsub('/', '\\') --- @type string else uri = uri:gsub('^file:/+', '/') ---@type string end -- cgit From c47496791a80f8b6b9e37866010305482de4c8ca Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Mon, 27 Jan 2025 14:25:06 -0800 Subject: docs(treesitter): fix TSNode:range() type signature #32224 Uses an overload to properly show the different return type based on the input parameter. --- runtime/lua/vim/treesitter/_meta/tsnode.lua | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_meta/tsnode.lua b/runtime/lua/vim/treesitter/_meta/tsnode.lua index b261b87253..552905c3f0 100644 --- a/runtime/lua/vim/treesitter/_meta/tsnode.lua +++ b/runtime/lua/vim/treesitter/_meta/tsnode.lua @@ -103,18 +103,9 @@ function TSNode:end_() end --- - end row --- - end column --- - end byte (if {include_bytes} is `true`) ---- @param include_bytes boolean? ---- @return integer, integer, integer, integer -function TSNode:range(include_bytes) end - ---- @nodoc --- @param include_bytes false? --- @return integer, integer, integer, integer -function TSNode:range(include_bytes) end - ---- @nodoc ---- @param include_bytes true ---- @return integer, integer, integer, integer, integer, integer +--- @overload fun(self: TSNode, include_bytes: true): integer, integer, integer, integer, integer, integer function TSNode:range(include_bytes) end --- Get the node's type as a string. -- cgit From a119dab40f939121d5e5a0c622f19911c9c9ce03 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Tue, 28 Jan 2025 17:34:07 +0100 Subject: fix(treesitter): avoid computing foldlevels for reloaded buffer #32233 --- runtime/lua/vim/treesitter/_fold.lua | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index f8d18d8427..ad4110b83d 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -269,10 +269,15 @@ local function schedule_if_loaded(bufnr, fn) end ---@param bufnr integer ----@param foldinfo TS.FoldInfo ---@param tree_changes Range4[] -local function on_changedtree(bufnr, foldinfo, tree_changes) +local function on_changedtree(bufnr, tree_changes) schedule_if_loaded(bufnr, function() + -- Buffer reload clears `foldinfos[bufnr]`, which may still be nil when callback is invoked. + local foldinfo = foldinfos[bufnr] + if not foldinfo then + return + end + local srow_upd, erow_upd ---@type integer?, integer? local max_erow = api.nvim_buf_line_count(bufnr) -- TODO(ribru17): Replace this with a proper .all() awaiter once #19624 is resolved @@ -303,13 +308,18 @@ local function on_changedtree(bufnr, foldinfo, tree_changes) end ---@param bufnr integer ----@param foldinfo TS.FoldInfo ---@param start_row integer ---@param old_row integer ---@param old_col integer ---@param new_row integer ---@param new_col integer -local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, new_row, new_col) +local function on_bytes(bufnr, start_row, start_col, old_row, old_col, new_row, new_col) + -- Buffer reload clears `foldinfos[bufnr]`, which may still be nil when callback is invoked. + local foldinfo = foldinfos[bufnr] + if not foldinfo then + return + end + -- extend the end to fully include the range local end_row_old = start_row + old_row + 1 local end_row_new = start_row + new_row + 1 @@ -348,7 +358,7 @@ local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, -- is invoked. For example, `J` with non-zero count triggers multiple on_bytes before executing -- the scheduled callback. So we accumulate the edited ranges in `on_bytes_range`. schedule_if_loaded(bufnr, function() - if not foldinfo.on_bytes_range then + if not (foldinfo.on_bytes_range and foldinfos[bufnr]) then return end local srow, erow = foldinfo.on_bytes_range[1], foldinfo.on_bytes_range[2] @@ -387,13 +397,11 @@ function M.foldexpr(lnum) parser:register_cbs({ on_changedtree = function(tree_changes) - if foldinfos[bufnr] then - on_changedtree(bufnr, foldinfos[bufnr], tree_changes) - end + on_changedtree(bufnr, tree_changes) end, on_bytes = function(_, _, start_row, start_col, _, old_row, old_col, _, new_row, new_col, _) - on_bytes(bufnr, foldinfos[bufnr], start_row, start_col, old_row, old_col, new_row, new_col) + on_bytes(bufnr, start_row, start_col, old_row, old_col, new_row, new_col) end, on_detach = function() -- cgit From b88874d33c15bb0fd7a421230f8bf819056d7665 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Tue, 28 Jan 2025 09:59:04 -0800 Subject: fix(treesitter): empty queries can disable injections (#31748) **Problem:** Currently, if users want to efficiently disable injections, they have to delete the injection query files at their runtime path. This is because we only check for existence of the files before running the query over the entire buffer. **Solution:** Check for existence of query files, *and* that those files actually have captures. This will allow users to just comment out existing queries (or better yet, just add their own injection query to `~/.config/nvim` which contains only comments) to disable running the query over the entire buffer (a potentially slow operation) --- runtime/lua/vim/treesitter/languagetree.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index ecace67419..5e1156fa68 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -953,7 +953,7 @@ end --- @private --- @return table function LanguageTree:_get_injections() - if not self._injection_query then + if not self._injection_query or #self._injection_query.captures == 0 then return {} end -- cgit From 6711fa27ca6e822bfd2394ec513671617cc53efd Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Tue, 28 Jan 2025 12:22:25 -0800 Subject: fix(treesitter): recalculate folds on VimEnter #32240 **Problem:** In the case where the user sets the treesitter foldexpr upon startup in their `init.lua`, the fold info will be calculated before the parser has been loaded in, meaning folds will be properly calculated until edits or `:e`. **Solution:** Refresh fold information upon `VimEnter` as a sanity check to ensure that a parser really doesn't exist before always returning `'0'` in the foldexpr. --- runtime/lua/vim/treesitter/_fold.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index ad4110b83d..38318347a7 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -380,7 +380,7 @@ function M.foldexpr(lnum) if not foldinfos[bufnr] then foldinfos[bufnr] = FoldInfo.new(bufnr) - api.nvim_create_autocmd('BufUnload', { + api.nvim_create_autocmd({ 'BufUnload', 'VimEnter' }, { buffer = bufnr, once = true, callback = function() -- cgit From da0ae953490098c28bad4791e08e2cc4c2e385e2 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Tue, 28 Jan 2025 23:59:28 -0800 Subject: feat(treesitter): support modelines in `query.set()` (#30257) --- runtime/lua/vim/treesitter/query.lua | 66 +++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 8055270a7f..10fb82e533 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -5,6 +5,9 @@ local api = vim.api local language = require('vim.treesitter.language') local memoize = vim.func._memoize +local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$' +local EXTENDS_FORMAT = '^;+%s*extends%s*$' + local M = {} local function is_directive(name) @@ -167,9 +170,6 @@ function M.get_files(lang, query_name, is_included) -- ;+ inherits: ({language},)*{language} -- -- {language} ::= {lang} | ({lang}) - local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$' - local EXTENDS_FORMAT = '^;+%s*extends%s*$' - for _, filename in ipairs(lang_files) do local file, err = io.open(filename, 'r') if not file then @@ -242,8 +242,8 @@ local function read_query_files(filenames) return table.concat(contents, '') end --- The explicitly set queries from |vim.treesitter.query.set()| ----@type table> +-- The explicitly set query strings from |vim.treesitter.query.set()| +---@type table> local explicit_queries = setmetatable({}, { __index = function(t, k) local lang_queries = {} @@ -255,16 +255,27 @@ local explicit_queries = setmetatable({}, { --- Sets the runtime query named {query_name} for {lang} --- ---- This allows users to override any runtime files and/or configuration +--- This allows users to override or extend any runtime files and/or configuration --- set by plugins. --- +--- For example, you could enable spellchecking of `C` identifiers with the +--- following code: +--- ```lua +--- vim.treesitter.query.set( +--- 'c', +--- 'highlights', +--- [[;inherits c +--- (identifier) @spell]]) +--- ]]) +--- ``` +--- ---@param lang string Language to use for the query ---@param query_name string Name of the query (e.g., "highlights") ---@param text string Query text (unparsed). function M.set(lang, query_name, text) --- @diagnostic disable-next-line: undefined-field LuaLS bad at generics M.get:clear(lang, query_name) - explicit_queries[lang][query_name] = M.parse(lang, text) + explicit_queries[lang][query_name] = text end --- Returns the runtime query {query_name} for {lang}. @@ -274,12 +285,43 @@ end --- ---@return vim.treesitter.Query? : Parsed query. `nil` if no query files are found. M.get = memoize('concat-2', function(lang, query_name) + local query_string ---@type string + if explicit_queries[lang][query_name] then - return explicit_queries[lang][query_name] - end + local query_files = {} + local base_langs = {} ---@type string[] + + for line in explicit_queries[lang][query_name]:gmatch('([^\n]*)\n?') do + if not vim.startswith(line, ';') then + break + end + + local lang_list = line:match(MODELINE_FORMAT) + if lang_list then + for _, incl_lang in ipairs(vim.split(lang_list, ',')) do + local is_optional = incl_lang:match('%(.*%)') - local query_files = M.get_files(lang, query_name) - local query_string = read_query_files(query_files) + if is_optional then + add_included_lang(base_langs, lang, incl_lang:sub(2, #incl_lang - 1)) + else + add_included_lang(base_langs, lang, incl_lang) + end + end + elseif line:match(EXTENDS_FORMAT) then + table.insert(base_langs, lang) + end + end + + for _, base_lang in ipairs(base_langs) do + local base_files = M.get_files(base_lang, query_name, true) + vim.list_extend(query_files, base_files) + end + + query_string = read_query_files(query_files) .. explicit_queries[lang][query_name] + else + local query_files = M.get_files(lang, query_name) + query_string = read_query_files(query_files) + end if #query_string == 0 then return nil @@ -303,7 +345,7 @@ api.nvim_create_autocmd('OptionSet', { --- - `captures`: a list of unique capture names defined in the query (alias: `info.captures`). --- - `info.patterns`: information about predicates. --- ---- Example (to try it, use `g==` or select the code then run `:'<,'>lua`): +--- Example: --- ```lua --- local query = vim.treesitter.query.parse('vimdoc', [[ --- ; query -- cgit From 19f00bf32cebfa66a17e0f5945d62d7da1859623 Mon Sep 17 00:00:00 2001 From: Daniel Petrovic Date: Wed, 29 Jan 2025 09:02:49 +0100 Subject: fix(treesitter) Set modeline=false in TSHighlighter:destroy (#32234) Problem: `TSHighlighter:destroy()` causes double-processing of the modeline and failure of `b:undo_ftplugin`. Solution: Disable modeline in `TSHighlighter:destroy()` by setting `modeline=false` if executing `syntaxset` autocommands for the `FileType` event. Co-authored-by: Daniel Petrovic --- runtime/lua/vim/treesitter/highlighter.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index c11fa1999d..6dd47811bd 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -160,7 +160,10 @@ function TSHighlighter:destroy() vim.bo[self.bufnr].spelloptions = self.orig_spelloptions vim.b[self.bufnr].ts_highlight = nil if vim.g.syntax_on == 1 then - api.nvim_exec_autocmds('FileType', { group = 'syntaxset', buffer = self.bufnr }) + api.nvim_exec_autocmds( + 'FileType', + { group = 'syntaxset', buffer = self.bufnr, modeline = false } + ) end end end -- cgit From e7ebc5c13d2d1658005a7fb477bc92718044746f Mon Sep 17 00:00:00 2001 From: notomo Date: Sat, 25 Jan 2025 21:15:01 +0900 Subject: fix(treesitter): stop async parsing if buffer is invalid Problem: Error occurs if delete buffer in the middle of parsing. Solution: Check if buffer is valid in parsing. --- runtime/lua/vim/treesitter/languagetree.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 5e1156fa68..8ea1c44cdc 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -475,13 +475,18 @@ function LanguageTree:_async_parse(range, on_parse) return end - local buf = vim.b[self._source] + local source = self._source + local buf = vim.b[source] local ct = buf.changedtick local total_parse_time = 0 local redrawtime = vim.o.redrawtime local timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ms or nil local function step() + if type(source) == 'number' and not vim.api.nvim_buf_is_valid(source) then + return nil + end + -- If buffer was changed in the middle of parsing, reset parse state if buf.changedtick ~= ct then ct = buf.changedtick -- cgit From efa664c7ed21b63f2cf0a8caa53161fe7e32b2bb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Jan 2025 14:39:13 +0800 Subject: vim-patch:9.1.1056: Vim doesn't highlight to be inserted text when completing (#32251) Problem: Vim doesn't highlight to be inserted text when completing Solution: Add support for the "preinsert" 'completeopt' value (glepnir) Support automatically inserting the currently selected candidate word that does not belong to the latter part of the leader. fixes: vim/vim#3433 closes: vim/vim#16403 https://github.com/vim/vim/commit/edd4ac3e895ce16034c7e098f1d68e0155d97886 Co-authored-by: glepnir --- runtime/lua/vim/_meta/options.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index f0f0d1a768..452959970d 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -1102,6 +1102,12 @@ vim.go.cia = vim.go.completeitemalign --- scores when "fuzzy" is enabled. Candidates will appear --- in their original order. --- +--- preinsert +--- Preinsert the portion of the first candidate word that is +--- not part of the current completion leader and using the +--- `hl-ComplMatchIns` highlight group. Does not work when +--- "fuzzy" is also included. +--- --- @type string vim.o.completeopt = "menu,preview" vim.o.cot = vim.o.completeopt -- cgit From e71d2c817d1a2475551f58a98e411f6b39a5be3f Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 13 Jan 2025 15:48:02 +0100 Subject: docs: misc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dustin S. Co-authored-by: Ferenc Fejes Co-authored-by: Maria José Solano Co-authored-by: Yochem van Rosmalen Co-authored-by: brianhuster Co-authored-by: zeertzjq --- runtime/lua/vim/_defaults.lua | 2 +- runtime/lua/vim/filetype/detect.lua | 2 +- runtime/lua/vim/lsp.lua | 5 +++-- runtime/lua/vim/lsp/buf.lua | 2 +- runtime/lua/vim/treesitter/languagetree.lua | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 28f1542f64..69204e3fe6 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -652,7 +652,7 @@ do -- This autocommand updates the value of 'background' anytime we receive -- an OSC 11 response from the terminal emulator. If the user has set - -- 'background' explictly then we will delete this autocommand, + -- 'background' explicitly then we will delete this autocommand, -- effectively disabling automatic background setting. local force = false local id = vim.api.nvim_create_autocmd('TermResponse', { diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 91c0406dc0..fc0b45ecd8 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -34,7 +34,7 @@ local matchregex = vim.filetype._matchregex -- can be detected from the first five lines of the file. --- @type vim.filetype.mapfn function M.asm(path, bufnr) - -- tiasm uses `* commment` + -- tiasm uses `* comment` local lines = table.concat(getlines(bufnr, 1, 10), '\n') if findany(lines, { '^%*', '\n%*', 'Texas Instruments Incorporated' }) then return 'tiasm' diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5226c8ae37..a45f9adeb6 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1395,8 +1395,9 @@ end --- callback = function(args) --- local client = vim.lsp.get_client_by_id(args.data.client_id) --- if client:supports_method('textDocument/foldingRange') then ---- vim.wo.foldmethod = 'expr' ---- vim.wo.foldexpr = 'v:lua.vim.lsp.foldexpr()' +--- local win = vim.api.nvim_get_current_win() +--- vim.wo[win][0].foldmethod = 'expr' +--- vim.wo[win][0].foldexpr = 'v:lua.vim.lsp.foldexpr()' --- end --- end, --- }) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 638a0d0f3f..48aa809ebd 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -423,7 +423,7 @@ end --- ---@see vim.lsp.protocol.CompletionTriggerKind function M.completion(context) - vim.depends('vim.lsp.buf.completion', 'vim.lsp.commpletion.trigger', '0.12') + vim.depends('vim.lsp.buf.completion', 'vim.lsp.completion.trigger', '0.12') return lsp.buf_request( 0, ms.textDocument_completion, diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 8ea1c44cdc..1f7872715f 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -523,7 +523,7 @@ end --- only the root tree without injections). --- @param on_parse fun(err?: string, trees?: table)? Function invoked when parsing completes. --- When provided and `vim.g._ts_force_sync_parsing` is not set, parsing will run ---- asynchronously. The first argument to the function is a string respresenting the error type, +--- asynchronously. The first argument to the function is a string representing the error type, --- in case of a failure (currently only possible for timeouts). The second argument is the list --- of trees returned by the parse (upon success), or `nil` if the parse timed out (determined --- by 'redrawtime'). -- cgit From 096ae3bfd7075dce69c70182ccedcd6d33e66d31 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Thu, 30 Jan 2025 13:34:46 -0800 Subject: fix(treesitter): nil access when running string parser async --- runtime/lua/vim/treesitter/languagetree.lua | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 1f7872715f..4e4da5a5ec 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -476,21 +476,26 @@ function LanguageTree:_async_parse(range, on_parse) end local source = self._source - local buf = vim.b[source] - local ct = buf.changedtick + local is_buffer_parser = type(source) == 'number' + local buf = is_buffer_parser and vim.b[source] or nil + local ct = is_buffer_parser and buf.changedtick or nil local total_parse_time = 0 local redrawtime = vim.o.redrawtime local timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ms or nil local function step() - if type(source) == 'number' and not vim.api.nvim_buf_is_valid(source) then - return nil - end + if is_buffer_parser then + if + not vim.api.nvim_buf_is_valid(source --[[@as number]]) + then + return nil + end - -- If buffer was changed in the middle of parsing, reset parse state - if buf.changedtick ~= ct then - ct = buf.changedtick - total_parse_time = 0 + -- If buffer was changed in the middle of parsing, reset parse state + if buf.changedtick ~= ct then + ct = buf.changedtick + total_parse_time = 0 + end end local parse_time, trees, finished = tcall(self._parse, self, range, timeout) -- cgit From 4bdabf9b1ae52134f50a0b75dc2c73a40c0c252f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 2 Feb 2025 17:32:51 +0800 Subject: vim-patch:9.1.1068: getchar() can't distinguish between C-I and Tab (#32295) Problem: getchar() can't distinguish between C-I and Tab. Solution: Add {opts} to pass extra flags to getchar() and getcharstr(), with "number" and "simplify" keys. related: vim/vim#10603 closes: vim/vim#16554 https://github.com/vim/vim/commit/e0a2ab397fd13a71efec85b017d5d4d62baf7f63 Cherry-pick tv_dict_has_key() from patch 8.2.4683. --- runtime/lua/vim/_meta/vimfn.lua | 53 ++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 19 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 4b5b276023..c0be6b089c 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -2748,12 +2748,14 @@ function vim.fn.getcellwidths() end function vim.fn.getchangelist(buf) end --- Get a single character from the user or input stream. ---- If {expr} is omitted, wait until a character is available. +--- If {expr} is omitted or is -1, wait until a character is +--- available. --- If {expr} is 0, only get a character when one is available. --- Return zero otherwise. --- If {expr} is 1, only check if a character is available, it is --- not consumed. Return zero if no character available. ---- If you prefer always getting a string use |getcharstr()|. +--- If you prefer always getting a string use |getcharstr()|, or +--- specify |FALSE| as "number" in {opts}. --- --- Without {expr} and when {expr} is 0 a whole character or --- special key is returned. If it is a single character, the @@ -2763,7 +2765,8 @@ function vim.fn.getchangelist(buf) end --- starting with 0x80 (decimal: 128). This is the same value as --- the String "\", e.g., "\". The returned value is --- also a String when a modifier (shift, control, alt) was used ---- that is not included in the character. +--- that is not included in the character. |keytrans()| can also +--- be used to convert a returned String into a readable form. --- --- When {expr} is 0 and Esc is typed, there will be a short delay --- while Vim waits to see if this is the start of an escape @@ -2775,6 +2778,24 @@ function vim.fn.getchangelist(buf) end --- --- Use getcharmod() to obtain any additional modifiers. --- +--- The optional argument {opts} is a Dict and supports the +--- following items: +--- +--- number If |TRUE|, return a Number when getting +--- a single character. +--- If |FALSE|, the return value is always +--- converted to a String, and an empty +--- String (instead of 0) is returned when +--- no character is available. +--- (default: |TRUE|) +--- +--- simplify If |TRUE|, include modifiers in the +--- character if possible. E.g., return +--- the same value for CTRL-I and . +--- If |FALSE|, don't include modifiers in +--- the character. +--- (default: |TRUE|) +--- --- When the user clicks a mouse button, the mouse event will be --- returned. The position can then be found in |v:mouse_col|, --- |v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|. @@ -2811,9 +2832,10 @@ function vim.fn.getchangelist(buf) end --- endfunction --- < --- ---- @param expr? 0|1 ---- @return integer -function vim.fn.getchar(expr) end +--- @param expr? -1|0|1 +--- @param opts? table +--- @return integer|string +function vim.fn.getchar(expr, opts) end --- The result is a Number which is the state of the modifiers for --- the last obtained character with getchar() or in another way. @@ -2872,20 +2894,13 @@ function vim.fn.getcharpos(expr) end --- @return table function vim.fn.getcharsearch() end ---- Get a single character from the user or input stream as a ---- string. ---- If {expr} is omitted, wait until a character is available. ---- If {expr} is 0 or false, only get a character when one is ---- available. Return an empty string otherwise. ---- If {expr} is 1 or true, only check if a character is ---- available, it is not consumed. Return an empty string ---- if no character is available. ---- Otherwise this works like |getchar()|, except that a number ---- result is converted to a string. ---- ---- @param expr? 0|1 +--- The same as |getchar()|, except that this always returns a +--- String, and "number" isn't allowed in {opts}. +--- +--- @param expr? -1|0|1 +--- @param opts? table --- @return string -function vim.fn.getcharstr(expr) end +function vim.fn.getcharstr(expr, opts) end --- Return completion pattern of the current command-line. --- Only works when the command line is being edited, thus -- cgit From 5bc948c050ce321ed56ff48b0d41e189b1ea4c87 Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Sun, 2 Feb 2025 09:56:01 +0000 Subject: fix(diagnostic): improve current_line refresh logic #32275 Problem: The current implementation uses a global augroup for virtual lines in diagnostics, which can lead to conflicts and unintended behavior when multiple namespaces/buffers are involved. Solution: Refactor the code to use a namespace-specific augroup for virtual lines. This ensures that each namespace has its own augroup. Scope the clear commands to only the relevant buffer. --- runtime/lua/vim/diagnostic.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 2538cd3048..6016867046 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -344,7 +344,6 @@ local global_diagnostic_options = { --- @class (private) vim.diagnostic.Handler --- @field show? fun(namespace: integer, bufnr: integer, diagnostics: vim.Diagnostic[], opts?: vim.diagnostic.OptsResolved) --- @field hide? fun(namespace:integer, bufnr:integer) ---- @field _augroup? integer --- @nodoc --- @type table @@ -1841,12 +1840,14 @@ M.handlers.virtual_lines = { ns.user_data.virt_lines_ns = api.nvim_create_namespace(string.format('nvim.%s.diagnostic.virtual_lines', ns.name)) end - if not M.handlers.virtual_lines._augroup then - M.handlers.virtual_lines._augroup = - api.nvim_create_augroup('nvim.lsp.diagnostic.virt_lines', { clear = true }) + if not ns.user_data.virt_lines_augroup then + ns.user_data.virt_lines_augroup = api.nvim_create_augroup( + string.format('nvim.%s.diagnostic.virt_lines', ns.name), + { clear = true } + ) end - api.nvim_clear_autocmds({ group = M.handlers.virtual_lines._augroup }) + api.nvim_clear_autocmds({ group = ns.user_data.virt_lines_augroup, buffer = bufnr }) if opts.virtual_lines.format then diagnostics = reformat_diagnostics(opts.virtual_lines.format, diagnostics) @@ -1855,7 +1856,7 @@ M.handlers.virtual_lines = { if opts.virtual_lines.current_line == true then api.nvim_create_autocmd('CursorMoved', { buffer = bufnr, - group = M.handlers.virtual_lines._augroup, + group = ns.user_data.virt_lines_augroup, callback = function() render_virtual_lines_at_current_line(diagnostics, ns.user_data.virt_lines_ns, bufnr) end, @@ -1875,7 +1876,7 @@ M.handlers.virtual_lines = { if api.nvim_buf_is_valid(bufnr) then api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_lines_ns, 0, -1) end - api.nvim_clear_autocmds({ group = M.handlers.virtual_lines._augroup }) + api.nvim_clear_autocmds({ group = ns.user_data.virt_lines_augroup, buffer = bufnr }) end end, } -- cgit From 48e3ac60c63341761b0f7e21262722f09127d374 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sun, 2 Feb 2025 03:40:43 -0800 Subject: perf(diagnostics): cache line diagnostics when `current_line` is set #32288 Compute the diagnostics per line when `show` is called, allowing for O(1) access for the diagnostics to display when the cursor line or the list of diagnostics haven't changed. --- runtime/lua/vim/diagnostic.lua | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 6016867046..8044767cb0 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1805,20 +1805,26 @@ local function render_virtual_lines(namespace, bufnr, diagnostics) end end ---- @param diagnostics vim.Diagnostic[] +--- @param diagnostics table --- @param namespace integer --- @param bufnr integer local function render_virtual_lines_at_current_line(diagnostics, namespace, bufnr) - local line_diagnostics = {} local lnum = api.nvim_win_get_cursor(0)[1] - 1 + local cursor_diagnostics = {} - for _, diag in ipairs(diagnostics) do - if (lnum == diag.lnum) or (diag.end_lnum and lnum >= diag.lnum and lnum <= diag.end_lnum) then - table.insert(line_diagnostics, diag) + if diagnostics[lnum] ~= nil then + cursor_diagnostics = diagnostics[lnum] + else + for _, line_diags in pairs(diagnostics) do + for _, diag in ipairs(line_diags) do + if diag.end_lnum and lnum >= diag.lnum and lnum <= diag.end_lnum then + table.insert(cursor_diagnostics, diag) + end + end end end - render_virtual_lines(namespace, bufnr, line_diagnostics) + render_virtual_lines(namespace, bufnr, cursor_diagnostics) end M.handlers.virtual_lines = { @@ -1854,15 +1860,18 @@ M.handlers.virtual_lines = { end if opts.virtual_lines.current_line == true then + -- Create a mapping from line -> diagnostics so that we can quickly get the + -- diagnostics we need when the cursor line doesn't change. + local line_diagnostics = diagnostic_lines(diagnostics) api.nvim_create_autocmd('CursorMoved', { buffer = bufnr, group = ns.user_data.virt_lines_augroup, callback = function() - render_virtual_lines_at_current_line(diagnostics, ns.user_data.virt_lines_ns, bufnr) + render_virtual_lines_at_current_line(line_diagnostics, ns.user_data.virt_lines_ns, bufnr) end, }) -- Also show diagnostics for the current line before the first CursorMoved event. - render_virtual_lines_at_current_line(diagnostics, ns.user_data.virt_lines_ns, bufnr) + render_virtual_lines_at_current_line(line_diagnostics, ns.user_data.virt_lines_ns, bufnr) else render_virtual_lines(ns.user_data.virt_lines_ns, bufnr, diagnostics) end -- cgit From 02ea0e77a19b116006dc04848703aaeed3f50ded Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Sun, 2 Feb 2025 03:42:47 -0800 Subject: refactor(treesitter): drop `LanguageTree._has_regions` #32274 This simplifies some logic in `languagetree.lua`, removing the need for `_has_regions`, and removing side effects in `:included_regions()`. Before: - Edit is made which sets `_regions = nil` - Upon the next call to `included_regions()` (usually right after we marked `_regions` as `nil` due to an `_iter_regions()` call), if `_regions` is nil, we repopulate the table (as long as the tree actually has regions) After: - Edit is made which resets `_regions` if it exists - `included_regions()` no longer needs to perform this logic itself, and also no longer needs to read a `_has_regions` variable --- runtime/lua/vim/treesitter/languagetree.lua | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 4e4da5a5ec..9571a117b8 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -82,7 +82,6 @@ local TSCallbackNames = { ---@field private _ranges_being_parsed table ---Table of callback queues, keyed by each region for which the callbacks should be run ---@field private _cb_queues table)[]> ----@field private _has_regions boolean ---@field private _regions table? ---List of regions this tree should manage and parse. If nil then regions are ---taken from _trees. This is mostly a short-lived cache for included_regions() @@ -132,7 +131,6 @@ function LanguageTree.new(source, lang, opts) _opts = opts, _injection_query = injections[lang] and query.parse(lang, injections[lang]) or query.get(lang, 'injections'), - _has_regions = false, _injections_processed = false, _valid = false, _parser = vim._create_ts_parser(lang), @@ -743,8 +741,6 @@ end ---@private ---@param new_regions (Range4|Range6|TSNode)[][] List of regions this tree should manage and parse. function LanguageTree:set_included_regions(new_regions) - self._has_regions = true - -- Transform the tables from 4 element long to 6 element long (with byte offset) for _, region in ipairs(new_regions) do for i, range in ipairs(region) do @@ -788,18 +784,8 @@ function LanguageTree:included_regions() return self._regions end - if not self._has_regions then - -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} (the full range) - return { {} } - end - - local regions = {} ---@type Range6[][] - for i, _ in pairs(self._trees) do - regions[i] = self._trees[i]:included_ranges(true) - end - - self._regions = regions - return regions + -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} (the full range) + return { {} } end ---@param node TSNode @@ -1050,7 +1036,14 @@ function LanguageTree:_edit( end self._parser:reset() - self._regions = nil + + if self._regions then + local regions = {} ---@type table + for i, tree in pairs(self._trees) do + regions[i] = tree:included_ranges(true) + end + self._regions = regions + end local changed_range = { start_row, -- cgit From 77be44563acb64a481d48f45c8dbbfca2d7db415 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Sun, 2 Feb 2025 03:46:26 -0800 Subject: refactor(treesitter): always return valid range from parse() #32273 Problem: When running an initial parse, parse() returns an empty table rather than an actual range. In `languagetree.lua`, we manually check if a parse was incremental to determine the changed parse region. Solution: - Always return a range (in the C side) from parse(). - Simplify the language tree code a bit. - Logger no longer shows empty ranges on the initial parse. --- runtime/lua/vim/treesitter/languagetree.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 9571a117b8..725e95dfc9 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -378,10 +378,7 @@ function LanguageTree:_parse_regions(range, timeout) return changes, no_regions_parsed, total_parse_time, false end - -- Pass ranges if this is an initial parse - local cb_changes = self._trees[i] and tree_changes or tree:included_ranges(true) - - self:_do_callback('changedtree', cb_changes, tree) + self:_do_callback('changedtree', tree_changes, tree) self._trees[i] = tree vim.list_extend(changes, tree_changes) -- cgit From 9508d6a8146350ffc9f31f4263fa871bab9130bf Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Fri, 17 Jan 2025 14:35:05 -0800 Subject: refactor(treesitter): use coroutines for resuming _parse() logic This means that all work previously done by a `_parse()` iteration will be kept in future iterations. This prevents it from running indefinitely in some cases where the file is very large and there are 2+ injections. --- runtime/lua/vim/treesitter/languagetree.lua | 70 ++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 21 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 725e95dfc9..f876d9fe7d 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -60,6 +60,8 @@ local default_parse_timeout_ms = 3 ---| 'on_child_added' ---| 'on_child_removed' +---@alias ParserThreadState { timeout: integer? } + --- @type table local TSCallbackNames = { on_changedtree = 'changedtree', @@ -345,12 +347,12 @@ end --- @private --- @param range boolean|Range? ---- @param timeout integer? +--- @param thread_state ParserThreadState --- @return Range6[] changes --- @return integer no_regions_parsed --- @return number total_parse_time --- @return boolean finished whether async parsing still needs time -function LanguageTree:_parse_regions(range, timeout) +function LanguageTree:_parse_regions(range, thread_state) local changes = {} local no_regions_parsed = 0 local total_parse_time = 0 @@ -370,12 +372,18 @@ function LanguageTree:_parse_regions(range, timeout) ) then self._parser:set_included_ranges(ranges) - self._parser:set_timeout(timeout and timeout * 1000 or 0) -- ms -> micros + self._parser:set_timeout(thread_state.timeout and thread_state.timeout * 1000 or 0) -- ms -> micros + local parse_time, tree, tree_changes = tcall(self._parser.parse, self._parser, self._trees[i], self._source, true) + while true do + if tree then + break + end + coroutine.yield(changes, no_regions_parsed, total_parse_time, false) - if not tree then - return changes, no_regions_parsed, total_parse_time, false + parse_time, tree, tree_changes = + tcall(self._parser.parse, self._parser, self._trees[i], self._source, true) end self:_do_callback('changedtree', tree_changes, tree) @@ -476,7 +484,11 @@ function LanguageTree:_async_parse(range, on_parse) local ct = is_buffer_parser and buf.changedtick or nil local total_parse_time = 0 local redrawtime = vim.o.redrawtime - local timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ms or nil + + local thread_state = {} ---@type ParserThreadState + + ---@type fun(): table, boolean + local parse = coroutine.wrap(self._parse) local function step() if is_buffer_parser then @@ -490,10 +502,12 @@ function LanguageTree:_async_parse(range, on_parse) if buf.changedtick ~= ct then ct = buf.changedtick total_parse_time = 0 + parse = coroutine.wrap(self._parse) end end - local parse_time, trees, finished = tcall(self._parse, self, range, timeout) + thread_state.timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ms or nil + local parse_time, trees, finished = tcall(parse, self, range, thread_state) total_parse_time = total_parse_time + parse_time if finished then @@ -535,16 +549,16 @@ function LanguageTree:parse(range, on_parse) if on_parse then return self:_async_parse(range, on_parse) end - local trees, _ = self:_parse(range) + local trees, _ = self:_parse(range, {}) return trees end --- @private --- @param range boolean|Range|nil ---- @param timeout integer? +--- @param thread_state ParserThreadState --- @return table trees --- @return boolean finished -function LanguageTree:_parse(range, timeout) +function LanguageTree:_parse(range, thread_state) if self:is_valid() then self:_log('valid') return self._trees, true @@ -559,11 +573,18 @@ function LanguageTree:_parse(range, timeout) -- At least 1 region is invalid if not self:is_valid(true) then - local is_finished - changes, no_regions_parsed, total_parse_time, is_finished = self:_parse_regions(range, timeout) - timeout = timeout and math.max(timeout - total_parse_time, 0) - if not is_finished then - return self._trees, false + ---@type fun(self: vim.treesitter.LanguageTree, range: boolean|Range?, thread_state: ParserThreadState): Range6[], integer, number, boolean + local parse_regions = coroutine.wrap(self._parse_regions) + while true do + local is_finished + changes, no_regions_parsed, total_parse_time, is_finished = + parse_regions(self, range, thread_state) + thread_state.timeout = thread_state.timeout + and math.max(thread_state.timeout - total_parse_time, 0) + if is_finished then + break + end + coroutine.yield(self._trees, false) end -- Need to run injections when we parsed something if no_regions_parsed > 0 then @@ -585,13 +606,20 @@ function LanguageTree:_parse(range, timeout) }) for _, child in pairs(self._children) do - if timeout == 0 then - return self._trees, false + if thread_state.timeout == 0 then + coroutine.yield(self._trees, false) end - local ctime, _, child_finished = tcall(child._parse, child, range, timeout) - timeout = timeout and math.max(timeout - ctime, 0) - if not child_finished then - return self._trees, child_finished + + ---@type fun(): table, boolean + local parse = coroutine.wrap(child._parse) + + while true do + local ctime, _, child_finished = tcall(parse, child, range, thread_state) + if child_finished then + thread_state.timeout = thread_state.timeout and math.max(thread_state.timeout - ctime, 0) + break + end + coroutine.yield(self._trees, child_finished) end end -- cgit From 8543aa406c4ae88cc928372b2f8105005cdd0a80 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Wed, 29 Jan 2025 15:53:34 -0800 Subject: feat(treesitter): allow LanguageTree:is_valid() to accept a range When given, only that range will be checked for validity rather than the entire tree. This is used in the highlighter to save CPU cycles since we only need to parse a certain region at a time anyway. --- runtime/lua/vim/treesitter/languagetree.lua | 129 +++++++++++++++------------- 1 file changed, 67 insertions(+), 62 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index f876d9fe7d..ea745c4deb 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -85,6 +85,8 @@ local TSCallbackNames = { ---Table of callback queues, keyed by each region for which the callbacks should be run ---@field private _cb_queues table)[]> ---@field private _regions table? +---The total number of regions. Since _regions can have holes, we cannot simply read this value from #_regions. +---@field private _num_regions integer ---List of regions this tree should manage and parse. If nil then regions are ---taken from _trees. This is mostly a short-lived cache for included_regions() ---@field private _lang string Language name @@ -92,7 +94,8 @@ local TSCallbackNames = { ---@field private _source (integer|string) Buffer or string to parse ---@field private _trees table Reference to parsed tree (one for each language). ---Each key is the index of region, which is synced with _regions and _valid. ----@field private _valid boolean|table If the parsed tree is valid +---@field private _valid_regions table Set of valid region IDs. +---@field private _is_entirely_valid boolean Whether the entire tree (excluding children) is valid. ---@field private _logger? fun(logtype: string, msg: string) ---@field private _logfile? file* local LanguageTree = {} @@ -134,7 +137,9 @@ function LanguageTree.new(source, lang, opts) _injection_query = injections[lang] and query.parse(lang, injections[lang]) or query.get(lang, 'injections'), _injections_processed = false, - _valid = false, + _valid_regions = {}, + _num_regions = 1, + _is_entirely_valid = false, _parser = vim._create_ts_parser(lang), _ranges_being_parsed = {}, _cb_queues = {}, @@ -240,7 +245,8 @@ end --- tree in treesitter. Doesn't clear filesystem cache. Called often, so needs to be fast. ---@param reload boolean|nil function LanguageTree:invalidate(reload) - self._valid = false + self._valid_regions = {} + self._is_entirely_valid = false self._parser:reset() -- buffer was reloaded, reparse all trees @@ -273,16 +279,46 @@ function LanguageTree:lang() return self._lang end +--- @param region Range6[] +--- @param range? boolean|Range +--- @return boolean +local function intercepts_region(region, range) + if #region == 0 then + return true + end + + if range == nil then + return false + end + + if type(range) == 'boolean' then + return range + end + + for _, r in ipairs(region) do + if Range.intercepts(r, range) then + return true + end + end + + return false +end + --- Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()| reflects the latest --- state of the source. If invalid, user should call |LanguageTree:parse()|. ----@param exclude_children boolean|nil whether to ignore the validity of children (default `false`) +---@param exclude_children boolean? whether to ignore the validity of children (default `false`) +---@param range Range? range to check for validity ---@return boolean -function LanguageTree:is_valid(exclude_children) - local valid = self._valid +function LanguageTree:is_valid(exclude_children, range) + local valid_regions = self._valid_regions - if type(valid) == 'table' then - for i, _ in pairs(self:included_regions()) do - if not valid[i] then + if not self._is_entirely_valid then + if not range then + return false + end + -- TODO: Efficiently search for possibly intersecting regions using a binary search + for i, region in pairs(self:included_regions()) do + if not valid_regions[i] and intercepts_region(region, range) then return false end end @@ -294,17 +330,12 @@ function LanguageTree:is_valid(exclude_children) end for _, child in pairs(self._children) do - if not child:is_valid(exclude_children) then + if not child:is_valid(exclude_children, range) then return false end end end - if type(valid) == 'boolean' then - return valid - end - - self._valid = true return true end @@ -320,31 +351,6 @@ function LanguageTree:source() return self._source end ---- @param region Range6[] ---- @param range? boolean|Range ---- @return boolean -local function intercepts_region(region, range) - if #region == 0 then - return true - end - - if range == nil then - return false - end - - if type(range) == 'boolean' then - return range - end - - for _, r in ipairs(region) do - if Range.intercepts(r, range) then - return true - end - end - - return false -end - --- @private --- @param range boolean|Range? --- @param thread_state ParserThreadState @@ -357,15 +363,11 @@ function LanguageTree:_parse_regions(range, thread_state) local no_regions_parsed = 0 local total_parse_time = 0 - if type(self._valid) ~= 'table' then - self._valid = {} - end - -- If there are no ranges, set to an empty list -- so the included ranges in the parser are cleared. for i, ranges in pairs(self:included_regions()) do if - not self._valid[i] + not self._valid_regions[i] and ( intercepts_region(ranges, range) or (self._trees[i] and intercepts_region(self._trees[i]:included_ranges(false), range)) @@ -392,7 +394,13 @@ function LanguageTree:_parse_regions(range, thread_state) total_parse_time = total_parse_time + parse_time no_regions_parsed = no_regions_parsed + 1 - self._valid[i] = true + self._valid_regions[i] = true + + -- _valid_regions can have holes, but that is okay because this equality is only true when it + -- has no holes (meaning all regions are valid) + if #self._valid_regions == self._num_regions then + self._is_entirely_valid = true + end end end @@ -559,7 +567,7 @@ end --- @return table trees --- @return boolean finished function LanguageTree:_parse(range, thread_state) - if self:is_valid() then + if self:is_valid(nil, type(range) == 'table' and range or nil) then self:_log('valid') return self._trees, true end @@ -572,7 +580,7 @@ function LanguageTree:_parse(range, thread_state) local total_parse_time = 0 -- At least 1 region is invalid - if not self:is_valid(true) then + if not self:is_valid(true, type(range) == 'table' and range or nil) then ---@type fun(self: vim.treesitter.LanguageTree, range: boolean|Range?, thread_state: ParserThreadState): Range6[], integer, number, boolean local parse_regions = coroutine.wrap(self._parse_regions) while true do @@ -715,38 +723,34 @@ end ---region is valid or not. ---@param fn fun(index: integer, region: Range6[]): boolean function LanguageTree:_iter_regions(fn) - if not self._valid then + if vim.deep_equal(self._valid_regions, {}) then return end - local was_valid = type(self._valid) ~= 'table' - - if was_valid then - self:_log('was valid', self._valid) - self._valid = {} + if self._is_entirely_valid then + self:_log('was valid') end local all_valid = true for i, region in pairs(self:included_regions()) do - if was_valid or self._valid[i] then - self._valid[i] = fn(i, region) - if not self._valid[i] then + if self._valid_regions[i] then + -- Setting this to nil rather than false allows us to determine if all regions were parsed + -- just by checking the length of _valid_regions. + self._valid_regions[i] = fn(i, region) and true or nil + if not self._valid_regions[i] then self:_log(function() return 'invalidating region', i, region_tostr(region) end) end end - if not self._valid[i] then + if not self._valid_regions[i] then all_valid = false end end - -- Compress the valid value to 'true' if there are no invalid regions - if all_valid then - self._valid = all_valid - end + self._is_entirely_valid = all_valid end --- Sets the included regions that should be parsed by this |LanguageTree|. @@ -796,6 +800,7 @@ function LanguageTree:set_included_regions(new_regions) end self._regions = new_regions + self._num_regions = #new_regions end ---Gets the set of included regions managed by this LanguageTree. This can be different from the -- cgit From af069c5c05ad99623345071007ad23da51973601 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 3 Feb 2025 08:09:03 +0800 Subject: vim-patch:9.1.1070: Cannot control cursor positioning of getchar() (#32303) Problem: Cannot control cursor positioning of getchar(). Solution: Add "cursor" flag to {opts}, with possible values "hide", "keep" and "msg". related: vim/vim#10603 closes: vim/vim#16569 https://github.com/vim/vim/commit/edf0f7db28f87611368e158210e58ed30f673098 --- runtime/lua/vim/_meta/vimfn.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index c0be6b089c..098c0e907a 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -2781,6 +2781,14 @@ function vim.fn.getchangelist(buf) end --- The optional argument {opts} is a Dict and supports the --- following items: --- +--- cursor A String specifying cursor behavior +--- when waiting for a character. +--- "hide": hide the cursor. +--- "keep": keep current cursor unchanged. +--- "msg": move cursor to message area. +--- (default: automagically decide +--- between "keep" and "msg") +--- --- number If |TRUE|, return a Number when getting --- a single character. --- If |FALSE|, the return value is always -- cgit From 445ecca398401ab9cdada163865db6dee374dde3 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Mon, 3 Feb 2025 00:54:31 -0800 Subject: feat(diagnostic): format() can filter diagnostics by returning nil #32302 --- runtime/lua/vim/diagnostic.lua | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 8044767cb0..972a5d1fa6 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -150,10 +150,11 @@ end --- Overrides the setting from |vim.diagnostic.config()|. --- @field source? boolean|'if_many' --- ---- A function that takes a diagnostic as input and returns a string. ---- The return value is the text used to display the diagnostic. +--- A function that takes a diagnostic as input and returns a string or nil. +--- If the return value is nil, the diagnostic is not displayed by the handler. +--- Else the output text is used to display the diagnostic. --- Overrides the setting from |vim.diagnostic.config()|. ---- @field format? fun(diagnostic:vim.Diagnostic): string +--- @field format? fun(diagnostic:vim.Diagnostic): string? --- --- Prefix each diagnostic in the floating window: --- - If a `function`, {i} is the index of the diagnostic being evaluated and @@ -207,7 +208,7 @@ end --- This can be used to render an LSP diagnostic error code. --- @field suffix? string|(fun(diagnostic:vim.Diagnostic): string) --- ---- The return value is the text used to display the diagnostic. Example: +--- If not nil, the return value is the text used to display the diagnostic. Example: --- ```lua --- function(diagnostic) --- if diagnostic.severity == vim.diagnostic.severity.ERROR then @@ -216,7 +217,8 @@ end --- return diagnostic.message --- end --- ``` ---- @field format? fun(diagnostic:vim.Diagnostic): string +--- If the return value is nil, the diagnostic is not displayed by the handler. +--- @field format? fun(diagnostic:vim.Diagnostic): string? --- --- See |nvim_buf_set_extmark()|. --- @field hl_mode? 'replace'|'combine'|'blend' @@ -239,9 +241,10 @@ end --- (default: `false`) --- @field current_line? boolean --- ---- A function that takes a diagnostic as input and returns a string. ---- The return value is the text used to display the diagnostic. ---- @field format? fun(diagnostic:vim.Diagnostic): string +--- A function that takes a diagnostic as input and returns a string or nil. +--- If the return value is nil, the diagnostic is not displayed by the handler. +--- Else the output text is used to display the diagnostic. +--- @field format? fun(diagnostic:vim.Diagnostic): string? --- @class vim.diagnostic.Opts.Signs --- @@ -503,15 +506,21 @@ local function prefix_source(diagnostics) end, diagnostics) end +--- @param format fun(vim.Diagnostic): string? --- @param diagnostics vim.Diagnostic[] --- @return vim.Diagnostic[] local function reformat_diagnostics(format, diagnostics) vim.validate('format', format, 'function') vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') - local formatted = vim.deepcopy(diagnostics, true) - for _, diagnostic in ipairs(formatted) do - diagnostic.message = format(diagnostic) + local formatted = {} + for _, diagnostic in ipairs(diagnostics) do + local message = format(diagnostic) + if message ~= nil then + local formatted_diagnostic = vim.deepcopy(diagnostic, true) + formatted_diagnostic.message = message + table.insert(formatted, formatted_diagnostic) + end end return formatted end -- cgit From 290bb4c64bdcc475c29b857dc8626f5c51aa2b8d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 3 Feb 2025 21:52:53 +0800 Subject: vim-patch:9.1.1009: diff feature can be improved Problem: diff feature can be improved Solution: include the linematch diff alignment algorithm (Jonathon) closes: vim/vim#9661 https://github.com/vim/vim/commit/7c7a4e6d1ad50d5b25b42aa2d5a33a8d04a4cc8a Co-authored-by: Jonathon --- runtime/lua/vim/_meta/options.lua | 102 +++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 51 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 452959970d..52c556867f 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -1631,11 +1631,20 @@ vim.go.dex = vim.go.diffexpr --- Option settings for diff mode. It can consist of the following items. --- All are optional. Items must be separated by a comma. --- ---- filler Show filler lines, to keep the text ---- synchronized with a window that has inserted ---- lines at the same position. Mostly useful ---- when windows are side-by-side and 'scrollbind' ---- is set. +--- algorithm:{text} Use the specified diff algorithm with the +--- internal diff engine. Currently supported +--- algorithms are: +--- myers the default algorithm +--- minimal spend extra time to generate the +--- smallest possible diff +--- patience patience diff algorithm +--- histogram histogram diff algorithm +--- +--- closeoff When a window is closed where 'diff' is set +--- and there is only one window remaining in the +--- same tab page with 'diff' set, execute +--- `:diffoff` in that window. This undoes a +--- `:diffsplit` command. --- --- context:{n} Use a context of {n} lines between a change --- and a fold that contains unchanged lines. @@ -1646,6 +1655,23 @@ vim.go.dex = vim.go.diffexpr --- value (999999) to disable folding completely. --- See `fold-diff`. --- +--- filler Show filler lines, to keep the text +--- synchronized with a window that has inserted +--- lines at the same position. Mostly useful +--- when windows are side-by-side and 'scrollbind' +--- is set. +--- +--- foldcolumn:{n} Set the 'foldcolumn' option to {n} when +--- starting diff mode. Without this 2 is used. +--- +--- followwrap Follow the 'wrap' option and leave as it is. +--- +--- horizontal Start diff mode with horizontal splits (unless +--- explicitly specified otherwise). +--- +--- hiddenoff Do not use diff mode for a buffer when it +--- becomes hidden. +--- --- iblank Ignore changes where lines are all blank. Adds --- the "-B" flag to the "diff" command if --- 'diffexpr' is empty. Check the documentation @@ -1659,6 +1685,17 @@ vim.go.dex = vim.go.diffexpr --- are considered the same. Adds the "-i" flag --- to the "diff" command if 'diffexpr' is empty. --- +--- indent-heuristic +--- Use the indent heuristic for the internal +--- diff library. +--- +--- internal Use the internal diff library. This is +--- ignored when 'diffexpr' is set. *E960* +--- When running out of memory when writing a +--- buffer this item will be ignored for diffs +--- involving that buffer. Set the 'verbose' +--- option to see when this happens. +--- --- iwhite Ignore changes in amount of white space. Adds --- the "-b" flag to the "diff" command if --- 'diffexpr' is empty. Check the documentation @@ -1678,56 +1715,19 @@ vim.go.dex = vim.go.diffexpr --- of the "diff" command for what this does --- exactly. --- ---- horizontal Start diff mode with horizontal splits (unless ---- explicitly specified otherwise). +--- linematch:{n} Align and mark changes between the most +--- similar lines between the buffers. When the +--- total number of lines in the diff hunk exceeds +--- {n}, the lines will not be aligned because for +--- very large diff hunks there will be a +--- noticeable lag. A reasonable setting is +--- "linematch:60", as this will enable alignment +--- for a 2 buffer diff hunk of 30 lines each, +--- or a 3 buffer diff hunk of 20 lines each. --- --- vertical Start diff mode with vertical splits (unless --- explicitly specified otherwise). --- ---- closeoff When a window is closed where 'diff' is set ---- and there is only one window remaining in the ---- same tab page with 'diff' set, execute ---- `:diffoff` in that window. This undoes a ---- `:diffsplit` command. ---- ---- hiddenoff Do not use diff mode for a buffer when it ---- becomes hidden. ---- ---- foldcolumn:{n} Set the 'foldcolumn' option to {n} when ---- starting diff mode. Without this 2 is used. ---- ---- followwrap Follow the 'wrap' option and leave as it is. ---- ---- internal Use the internal diff library. This is ---- ignored when 'diffexpr' is set. *E960* ---- When running out of memory when writing a ---- buffer this item will be ignored for diffs ---- involving that buffer. Set the 'verbose' ---- option to see when this happens. ---- ---- indent-heuristic ---- Use the indent heuristic for the internal ---- diff library. ---- ---- linematch:{n} Enable a second stage diff on each generated ---- hunk in order to align lines. When the total ---- number of lines in a hunk exceeds {n}, the ---- second stage diff will not be performed as ---- very large hunks can cause noticeable lag. A ---- recommended setting is "linematch:60", as this ---- will enable alignment for a 2 buffer diff with ---- hunks of up to 30 lines each, or a 3 buffer ---- diff with hunks of up to 20 lines each. ---- ---- algorithm:{text} Use the specified diff algorithm with the ---- internal diff engine. Currently supported ---- algorithms are: ---- myers the default algorithm ---- minimal spend extra time to generate the ---- smallest possible diff ---- patience patience diff algorithm ---- histogram histogram diff algorithm ---- --- Examples: --- --- ```vim -- cgit From 09f9f0a94625002f4c70efbdf858fe6918cbc9c6 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Tue, 4 Feb 2025 09:25:03 -0800 Subject: feat(treesitter): show which nodes are missing in InspectTree Now `:InspectTree` will show missing nodes as e.g. `(MISSING identifier)` or `(MISSING ";")` rather than just `(identifier)` or `";"`. This is doable because the `MISSING` keyword is now valid query syntax. Co-authored-by: Christian Clason --- runtime/lua/vim/treesitter/dev.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index ab08e1a527..24dd8243db 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -224,9 +224,12 @@ function TSTreeView:draw(bufnr) local text ---@type string if item.node:named() then - text = string.format('(%s', item.node:type()) + text = string.format('(%s%s', item.node:missing() and 'MISSING ' or '', item.node:type()) else text = string.format('%q', item.node:type()):gsub('\n', 'n') + if item.node:missing() then + text = string.format('(MISSING %s)', text) + end end if item.field then text = string.format('%s: %s', item.field, text) -- cgit From 38a52caec09eb15c9ff8b4db6f0cdb7e2a28eb98 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sun, 2 Feb 2025 14:06:05 -0800 Subject: feat(diagnostic): add `current_line` option for `virtual_text` handler --- runtime/lua/vim/diagnostic.lua | 130 +++++++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 38 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 972a5d1fa6..621945aedd 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -190,6 +190,10 @@ end --- severity |diagnostic-severity| --- @field severity? vim.diagnostic.SeverityFilter --- +--- Only show diagnostics for the current line. +--- (default `false`) +--- @field current_line? boolean +--- --- Include the diagnostic source in virtual text. Use `'if_many'` to only --- show sources if there is more than one diagnostic source in the buffer. --- Otherwise, any truthy value means to always show the diagnostic source. @@ -630,6 +634,26 @@ local function diagnostic_lines(diagnostics) return diagnostics_by_line end +--- @param diagnostics table +--- @return vim.Diagnostic[] +local function diagnostics_at_cursor(diagnostics) + local lnum = api.nvim_win_get_cursor(0)[1] - 1 + + if diagnostics[lnum] ~= nil then + return diagnostics[lnum] + end + + local cursor_diagnostics = {} + for _, line_diags in pairs(diagnostics) do + for _, diag in ipairs(line_diags) do + if diag.end_lnum and lnum >= diag.lnum and lnum <= diag.end_lnum then + table.insert(cursor_diagnostics, diag) + end + end + end + return cursor_diagnostics +end + --- @param namespace integer --- @param bufnr integer --- @param diagnostics vim.Diagnostic[] @@ -1570,6 +1594,28 @@ M.handlers.underline = { end, } +--- @param namespace integer +--- @param bufnr integer +--- @param diagnostics table +--- @param opts vim.diagnostic.Opts.VirtualText +local function render_virtual_text(namespace, bufnr, diagnostics, opts) + api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) + + for line, line_diagnostics in pairs(diagnostics) do + local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts) + + if virt_texts then + api.nvim_buf_set_extmark(bufnr, namespace, line, 0, { + hl_mode = opts.hl_mode or 'combine', + virt_text = virt_texts, + virt_text_pos = opts.virt_text_pos, + virt_text_hide = opts.virt_text_hide, + virt_text_win_col = opts.virt_text_win_col, + }) + end + end +end + M.handlers.virtual_text = { show = function(namespace, bufnr, diagnostics, opts) vim.validate('namespace', namespace, 'number') @@ -1601,23 +1647,44 @@ M.handlers.virtual_text = { ns.user_data.virt_text_ns = api.nvim_create_namespace(string.format('nvim.%s.diagnostic.virtual_text', ns.name)) end + if not ns.user_data.virt_text_augroup then + ns.user_data.virt_text_augroup = api.nvim_create_augroup( + string.format('nvim.%s.diagnostic.virt_text', ns.name), + { clear = true } + ) + end - local virt_text_ns = ns.user_data.virt_text_ns - local buffer_line_diagnostics = diagnostic_lines(diagnostics) - for line, line_diagnostics in pairs(buffer_line_diagnostics) do - local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text) + api.nvim_clear_autocmds({ group = ns.user_data.virt_text_augroup, buffer = bufnr }) - if virt_texts then - api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, { - hl_mode = opts.virtual_text.hl_mode or 'combine', - virt_text = virt_texts, - virt_text_pos = opts.virtual_text.virt_text_pos, - virt_text_hide = opts.virtual_text.virt_text_hide, - virt_text_win_col = opts.virtual_text.virt_text_win_col, - }) - end + local line_diagnostics = diagnostic_lines(diagnostics) + + if opts.virtual_text.current_line == true then + api.nvim_create_autocmd('CursorMoved', { + buffer = bufnr, + group = ns.user_data.virt_text_augroup, + callback = function() + local lnum = api.nvim_win_get_cursor(0)[1] - 1 + render_virtual_text( + ns.user_data.virt_text_ns, + bufnr, + { [lnum] = diagnostics_at_cursor(line_diagnostics) }, + opts.virtual_text + ) + end, + }) + -- Also show diagnostics for the current line before the first CursorMoved event. + local lnum = api.nvim_win_get_cursor(0)[1] - 1 + render_virtual_text( + ns.user_data.virt_text_ns, + bufnr, + { [lnum] = diagnostics_at_cursor(line_diagnostics) }, + opts.virtual_text + ) + else + render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, opts.virtual_text) end - save_extmarks(virt_text_ns, bufnr) + + save_extmarks(ns.user_data.virt_text_ns, bufnr) end, hide = function(namespace, bufnr) local ns = M.get_namespace(namespace) @@ -1626,6 +1693,7 @@ M.handlers.virtual_text = { if api.nvim_buf_is_valid(bufnr) then api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1) end + api.nvim_clear_autocmds({ group = ns.user_data.virt_text_augroup, buffer = bufnr }) end end, } @@ -1814,28 +1882,6 @@ local function render_virtual_lines(namespace, bufnr, diagnostics) end end ---- @param diagnostics table ---- @param namespace integer ---- @param bufnr integer -local function render_virtual_lines_at_current_line(diagnostics, namespace, bufnr) - local lnum = api.nvim_win_get_cursor(0)[1] - 1 - local cursor_diagnostics = {} - - if diagnostics[lnum] ~= nil then - cursor_diagnostics = diagnostics[lnum] - else - for _, line_diags in pairs(diagnostics) do - for _, diag in ipairs(line_diags) do - if diag.end_lnum and lnum >= diag.lnum and lnum <= diag.end_lnum then - table.insert(cursor_diagnostics, diag) - end - end - end - end - - render_virtual_lines(namespace, bufnr, cursor_diagnostics) -end - M.handlers.virtual_lines = { show = function(namespace, bufnr, diagnostics, opts) vim.validate('namespace', namespace, 'number') @@ -1876,11 +1922,19 @@ M.handlers.virtual_lines = { buffer = bufnr, group = ns.user_data.virt_lines_augroup, callback = function() - render_virtual_lines_at_current_line(line_diagnostics, ns.user_data.virt_lines_ns, bufnr) + render_virtual_lines( + ns.user_data.virt_lines_ns, + bufnr, + diagnostics_at_cursor(line_diagnostics) + ) end, }) -- Also show diagnostics for the current line before the first CursorMoved event. - render_virtual_lines_at_current_line(line_diagnostics, ns.user_data.virt_lines_ns, bufnr) + render_virtual_lines( + ns.user_data.virt_lines_ns, + bufnr, + diagnostics_at_cursor(line_diagnostics) + ) else render_virtual_lines(ns.user_data.virt_lines_ns, bufnr, diagnostics) end -- cgit