From 3c1d70f20b5d5bad3bec121e589187d15f325a9b Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Mon, 25 Jul 2022 12:23:04 +0200 Subject: feat(treesitter): allow customizing language symbol name --- runtime/lua/vim/treesitter/language.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index dfb6f5be84..d14b825603 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -6,10 +6,11 @@ local M = {} --- --- Parsers are searched in the `parser` runtime directory. --- ----@param lang The language the parser should parse ----@param path Optional path the parser is located at ----@param silent Don't throw an error if language not found -function M.require_language(lang, path, silent) +---@param lang string The language the parser should parse +---@param path string|nil Optional path the parser is located at +---@param silent boolean|nil Don't throw an error if language not found +---@param symbol_name string|nil Internal symbol name for the language to load +function M.require_language(lang, path, silent, symbol_name) if vim._ts_has_language(lang) then return true end @@ -21,7 +22,6 @@ function M.require_language(lang, path, silent) return false end - -- TODO(bfredl): help tag? error("no parser for '" .. lang .. "' language, see :help treesitter-parsers") end path = paths[1] @@ -29,10 +29,10 @@ function M.require_language(lang, path, silent) if silent then return pcall(function() - vim._ts_add_language(path, lang) + vim._ts_add_language(path, lang, symbol_name) end) else - vim._ts_add_language(path, lang) + vim._ts_add_language(path, lang, symbol_name) end return true -- cgit From e892b7b3830f44bc8ab62e993bf07f7bf03d0029 Mon Sep 17 00:00:00 2001 From: Simon Wachter Date: Tue, 23 Aug 2022 13:02:55 +0200 Subject: fix(inspect): escape identifiers that are lua keywords (#19898) A lua keyword is not a valid table identifier --- runtime/lua/vim/inspect.lua | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua index 0a53fb203b..c232f69590 100644 --- a/runtime/lua/vim/inspect.lua +++ b/runtime/lua/vim/inspect.lua @@ -89,8 +89,38 @@ local function escape(str) ) end +-- List of lua keywords +local luaKeywords = { + ['and'] = true, + ['break'] = true, + ['do'] = true, + ['else'] = true, + ['elseif'] = true, + ['end'] = true, + ['false'] = true, + ['for'] = true, + ['function'] = true, + ['goto'] = true, + ['if'] = true, + ['in'] = true, + ['local'] = true, + ['nil'] = true, + ['not'] = true, + ['or'] = true, + ['repeat'] = true, + ['return'] = true, + ['then'] = true, + ['true'] = true, + ['until'] = true, + ['while'] = true, +} + local function isIdentifier(str) - return type(str) == 'string' and not not str:match('^[_%a][_%a%d]*$') + return type(str) == 'string' + -- identifier must start with a letter and underscore, and be followed by letters, numbers, and underscores + and not not str:match('^[_%a][_%a%d]*$') + -- lua keywords are not valid identifiers + and not luaKeywords[str] end local flr = math.floor -- cgit From 3aba4ba37859e4407eff2bb3f4d99c44b108ed79 Mon Sep 17 00:00:00 2001 From: Quentin Rasmont Date: Fri, 22 Apr 2022 21:50:52 +0200 Subject: feat(treesitter): upstream is_parent() Util from the nvim-treesitter project. Renamed is_parent to is_ancestor for clarity. --- runtime/lua/vim/treesitter.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 70f2c425ed..37ab59b259 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -118,4 +118,27 @@ function M.get_string_parser(str, lang, opts) return LanguageTree.new(str, lang, opts) end +--- Determines whether a node is the ancestor of another +--- +---@param dest table the possible ancestor +---@param source table the possible descendant node +--- +---@returns (boolean) True if dest is an ancestor of source +function M.is_ancestor(dest, source) + if not (dest and source) then + return false + end + + local current = source + while current ~= nil do + if current == dest then + return true + end + + current = current:parent() + end + + return false +end + return M -- cgit From 733b2e12b86a34c00aa07e0491762f88582792a5 Mon Sep 17 00:00:00 2001 From: Quentin Rasmont Date: Tue, 26 Apr 2022 22:42:48 +0200 Subject: feat(treesitter): add opts.concat to query.get_text_node As part of the upstream of utility functions from nvim-treesitter, this option when set to false allows to return a table (downstream behavior). Effectively making the switch from the downstream to the upstream function much easier. --- runtime/lua/vim/treesitter/query.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 103e85abfd..697e2e7691 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -181,9 +181,14 @@ end --- Gets the text corresponding to a given node --- ----@param node the node ----@param source The buffer or string from which the node is extracted -function M.get_node_text(node, source) +---@param node table The node +---@param source table The buffer or string from which the node is extracted +---@param opts table Optional parameters. +--- - concat: (boolean default true) Concatenate result in a string +function M.get_node_text(node, source, opts) + opts = opts or {} + local concat = vim.F.if_nil(opts.concat, true) + local start_row, start_col, start_byte = node:start() local end_row, end_col, end_byte = node:end_() @@ -210,7 +215,7 @@ function M.get_node_text(node, source) end end - return table.concat(lines, '\n') + return concat and table.concat(lines, '\n') or lines elseif type(source) == 'string' then return source:sub(start_byte + 1, end_byte) end -- cgit From 6b2d42eb0352d01923e4bf2e3ce0824c662b7be4 Mon Sep 17 00:00:00 2001 From: Quentin Rasmont Date: Sat, 30 Apr 2022 10:43:26 +0200 Subject: feat(treesitter): add ability to retreive a tree/node given a range --- runtime/lua/vim/treesitter/languagetree.lua | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 4d3b0631a2..f87a66ddab 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -549,6 +549,44 @@ function LanguageTree:contains(range) return false end +--- Gets the tree that contains {range} +--- +---@param range table A text range +---@param opts table Options table +---@param opts.ignore_injections boolean (default true) Ignore injected languages. +function LanguageTree:tree_for_range(range, opts) + opts = opts or {} + local ignore = vim.F.if_nil(opts.ignore_injections, true) + + if not ignore then + for _, child in pairs(self._children) do + for _, tree in pairs(child:trees()) do + if tree_contains(tree, range) then + return tree + end + end + end + end + + for _, tree in pairs(self._trees) do + if tree_contains(tree, range) then + return tree + end + end + + return nil +end + +--- Gets the smallest named node that contains {range} +--- +---@param range table A text range +---@param opts table Options table +---@param opts.ignore_injections boolean (default true) Ignore injected languages. +function LanguageTree:named_node_for_range(range, opts) + local tree = self:tree_for_range(range, opts) + return tree:root():named_descendant_for_range(unpack(range)) +end + --- Gets the appropriate language that contains {range} --- ---@param range A text range, see |LanguageTree:contains| -- cgit From 133ff6e11ea862c7425d9c6a2827b20c97cf297f Mon Sep 17 00:00:00 2001 From: Quentin Rasmont Date: Thu, 2 Jun 2022 18:13:05 +0200 Subject: feat(treesitter): upstream node_contains() Util from the nvim-treesitter project. --- runtime/lua/vim/treesitter.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 37ab59b259..0936d62296 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -141,4 +141,17 @@ function M.is_ancestor(dest, source) return false end +---Determines if a node contains a range +---@param node table The node +---@param range table The range +--- +---@returns (boolean) True if the node contains the range +function M.node_contains(node, range) + local start_row, start_col, end_row, end_col = node:range() + local start_fits = start_row < range[1] or (start_row == range[1] and start_col <= range[2]) + local end_fits = end_row > range[3] or (end_row == range[3] and end_col >= range[4]) + + return start_fits and end_fits +end + return M -- cgit From 244a115e494bce8e8205c04a6e5f3ab74ec4ed65 Mon Sep 17 00:00:00 2001 From: Quentin Rasmont Date: Sun, 24 Jul 2022 20:49:33 +0200 Subject: feat(treesitter): clarify similar 'get_node_range' functions The private 'get_node_range' function from the languagetree module has been renamed and remains private as it serve a purpose that is only relevant inside the languagetree module. The 'get_node_range' upstreamed from nvim-treesitter in the treesitter module has been made public as it is in itself a utlity function. --- runtime/lua/vim/treesitter.lua | 13 +++++++++++++ runtime/lua/vim/treesitter/languagetree.lua | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 0936d62296..82d41070ee 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -141,6 +141,19 @@ function M.is_ancestor(dest, source) return false end +--- Get the node's range or unpack a range table +--- +---@param node_or_range table +--- +---@returns start_row, start_col, end_row, end_col +function M.get_node_range(node_or_range) + if type(node_or_range) == 'table' then + return unpack(node_or_range) + else + return node_or_range:range() + end +end + ---Determines if a node contains a range ---@param node table The node ---@param range table The range diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index f87a66ddab..70317a9f94 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -299,7 +299,7 @@ function LanguageTree:included_regions() end ---@private -local function get_node_range(node, id, metadata) +local function get_range_from_metadata(node, id, metadata) if metadata[id] and metadata[id].range then return metadata[id].range end @@ -362,7 +362,7 @@ function LanguageTree:_get_injections() elseif name == 'combined' then combined = true elseif name == 'content' and #ranges == 0 then - table.insert(ranges, get_node_range(node, id, metadata)) + table.insert(ranges, get_range_from_metadata(node, id, metadata)) -- Ignore any tags that start with "_" -- Allows for other tags to be used in matches elseif string.sub(name, 1, 1) ~= '_' then @@ -371,7 +371,7 @@ function LanguageTree:_get_injections() end if #ranges == 0 then - table.insert(ranges, get_node_range(node, id, metadata)) + table.insert(ranges, get_range_from_metadata(node, id, metadata)) end end end -- cgit From 030b422d1e9517ed1b1c70fd8002b74881c80650 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 24 Aug 2022 23:48:52 +0200 Subject: feat(treesitter)!: use @foo.bar style highlight groups This removes the support for defining links via vim.treesitter.highlighter.hl_map (never documented, but plugins did anyway), or the uppercase-only `@FooGroup.Bar` to `FooGroup` rule. The fallback is now strictly `@foo.bar.lang` to `@foo.bar` to `@foo`, and casing is irrelevant (as it already was outside of treesitter) For compatibility, define default links to builting syntax groups as defined by pre-existing color schemes --- runtime/lua/vim/treesitter/highlighter.lua | 109 ++--------------------------- 1 file changed, 4 insertions(+), 105 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index e27a5fa9c3..1f242b0fdd 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -12,105 +12,18 @@ TSHighlighterQuery.__index = TSHighlighterQuery local ns = a.nvim_create_namespace('treesitter/highlighter') -local _default_highlights = {} -local _link_default_highlight_once = function(from, to) - if not _default_highlights[from] then - _default_highlights[from] = true - a.nvim_set_hl(0, from, { link = to, default = true }) - end - - return from -end - --- If @definition.special does not exist use @definition instead -local subcapture_fallback = { - __index = function(self, capture) - local rtn - local shortened = capture - while not rtn and shortened do - shortened = shortened:match('(.*)%.') - rtn = shortened and rawget(self, shortened) - end - rawset(self, capture, rtn or '__notfound') - return rtn - end, -} - -TSHighlighter.hl_map = setmetatable({ - ['error'] = 'Error', - ['text.underline'] = 'Underlined', - ['todo'] = 'Todo', - ['debug'] = 'Debug', - - -- Miscs - ['comment'] = 'Comment', - ['punctuation.delimiter'] = 'Delimiter', - ['punctuation.bracket'] = 'Delimiter', - ['punctuation.special'] = 'Delimiter', - - -- Constants - ['constant'] = 'Constant', - ['constant.builtin'] = 'Special', - ['constant.macro'] = 'Define', - ['define'] = 'Define', - ['macro'] = 'Macro', - ['string'] = 'String', - ['string.regex'] = 'String', - ['string.escape'] = 'SpecialChar', - ['character'] = 'Character', - ['character.special'] = 'SpecialChar', - ['number'] = 'Number', - ['boolean'] = 'Boolean', - ['float'] = 'Float', - - -- Functions - ['function'] = 'Function', - ['function.special'] = 'Function', - ['function.builtin'] = 'Special', - ['function.macro'] = 'Macro', - ['parameter'] = 'Identifier', - ['method'] = 'Function', - ['field'] = 'Identifier', - ['property'] = 'Identifier', - ['constructor'] = 'Special', - - -- Keywords - ['conditional'] = 'Conditional', - ['repeat'] = 'Repeat', - ['label'] = 'Label', - ['operator'] = 'Operator', - ['keyword'] = 'Keyword', - ['exception'] = 'Exception', - - ['type'] = 'Type', - ['type.builtin'] = 'Type', - ['type.qualifier'] = 'Type', - ['type.definition'] = 'Typedef', - ['storageclass'] = 'StorageClass', - ['structure'] = 'Structure', - ['include'] = 'Include', - ['preproc'] = 'PreProc', -}, subcapture_fallback) - ----@private -local function is_highlight_name(capture_name) - local firstc = string.sub(capture_name, 1, 1) - return firstc ~= string.lower(firstc) -end - ---@private function TSHighlighterQuery.new(lang, query_string) local self = setmetatable({}, { __index = TSHighlighterQuery }) self.hl_cache = setmetatable({}, { __index = function(table, capture) - local hl, is_vim_highlight = self:_get_hl_from_capture(capture) - if not is_vim_highlight then - hl = _link_default_highlight_once(lang .. hl, hl) + local name = self._query.captures[capture] + local id = 0 + if not vim.startswith(name, '_') then + id = a.nvim_get_hl_id_by_name('@' .. name .. '.' .. lang) end - local id = a.nvim_get_hl_id_by_name(hl) - rawset(table, capture, id) return id end, @@ -130,20 +43,6 @@ function TSHighlighterQuery:query() return self._query end ----@private ---- Get the hl from capture. ---- Returns a tuple { highlight_name: string, is_builtin: bool } -function TSHighlighterQuery:_get_hl_from_capture(capture) - local name = self._query.captures[capture] - - if is_highlight_name(name) then - -- From "Normal.left" only keep "Normal" - return vim.split(name, '.', true)[1], true - else - return TSHighlighter.hl_map[name] or 0, false - end -end - --- Creates a new highlighter using @param tree --- ---@param tree The language tree to use for highlighting -- cgit From 064ecb9ec581ec68f1376cde5ad7f960419b0324 Mon Sep 17 00:00:00 2001 From: Quentin Rasmont Date: Thu, 2 Jun 2022 20:07:08 +0200 Subject: feat(treesitter): upstream get_hl_groups_at_position() Util from the nvim-treesitter project. --- runtime/lua/vim/treesitter.lua | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 82d41070ee..8ba4525003 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -2,6 +2,7 @@ local a = vim.api local query = require('vim.treesitter.query') local language = require('vim.treesitter.language') local LanguageTree = require('vim.treesitter.languagetree') +local highlighter = require('vim.treesitter.highlighter') -- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. -- Consider use weak references to release parser if all plugins are done with @@ -167,4 +168,60 @@ function M.node_contains(node, range) return start_fits and end_fits end +---Gets a list of highlight group for a given cursor position +---@param bufnr number The buffer number +---@param row number The position row +---@param col number The position column +--- +---@returns (table) A table of highlight groups +function M.get_hl_groups_at_position(bufnr, row, col) + local buf_highlighter = highlighter.active[bufnr] + + if not buf_highlighter then + return {} + end + + local matches = {} + + buf_highlighter.tree:for_each_tree(function(tstree, tree) + if not tstree then + return + end + + local root = tstree:root() + local root_start_row, _, root_end_row, _ = root:range() + + -- Only worry about trees within the line range + if root_start_row > row or root_end_row < row then + return + end + + local q = buf_highlighter:get_query(tree:lang()) + + -- Some injected languages may not have highlight queries. + if not q:query() then + return + end + + local iter = q:query():iter_captures(root, buf_highlighter.bufnr, row, row + 1) + + for capture, node, metadata in iter do + local hl = q.hl_cache[capture] + + if hl and M.is_in_node_range(node, row, col) then + local c = q._query.captures[capture] -- name of the capture in the query + if c ~= nil then + local general_hl, is_vim_hl = q:_get_hl_from_capture(capture) + local local_hl = not is_vim_hl and (tree:lang() .. general_hl) + table.insert( + matches, + { capture = c, specific = local_hl, general = general_hl, priority = metadata.priority } + ) + end + end + end + end, true) + return matches +end + return M -- cgit From b04ef7f6b966c44b12dbc65b17a761ae9313d6c4 Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 25 Aug 2022 21:41:52 +0200 Subject: fix(treesitter): make it get_captures_at_position --- runtime/lua/vim/treesitter.lua | 45 +++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 8ba4525003..6431162799 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -2,7 +2,6 @@ local a = vim.api local query = require('vim.treesitter.query') local language = require('vim.treesitter.language') local LanguageTree = require('vim.treesitter.languagetree') -local highlighter = require('vim.treesitter.highlighter') -- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. -- Consider use weak references to release parser if all plugins are done with @@ -155,6 +154,28 @@ function M.get_node_range(node_or_range) end end +---Determines whether (line, col) position is in node range +--- +---@param node Node defining the range +---@param line A line (0-based) +---@param col A column (0-based) +function M.is_in_node_range(node, line, col) + local start_line, start_col, end_line, end_col = M.get_node_range(node) + if line >= start_line and line <= end_line then + if line == start_line and line == end_line then + return col >= start_col and col < end_col + elseif line == start_line then + return col >= start_col + elseif line == end_line then + return col < end_col + else + return true + end + else + return false + end +end + ---Determines if a node contains a range ---@param node table The node ---@param range table The range @@ -168,14 +189,17 @@ function M.node_contains(node, range) return start_fits and end_fits end ----Gets a list of highlight group for a given cursor position +---Gets a list of captures for a given cursor position ---@param bufnr number The buffer number ---@param row number The position row ---@param col number The position column --- ----@returns (table) A table of highlight groups -function M.get_hl_groups_at_position(bufnr, row, col) - local buf_highlighter = highlighter.active[bufnr] +---@returns (table) A table of captures +function M.get_captures_at_position(bufnr, row, col) + if bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + local buf_highlighter = M.highlighter.active[bufnr] if not buf_highlighter then return {} @@ -206,17 +230,10 @@ function M.get_hl_groups_at_position(bufnr, row, col) local iter = q:query():iter_captures(root, buf_highlighter.bufnr, row, row + 1) for capture, node, metadata in iter do - local hl = q.hl_cache[capture] - - if hl and M.is_in_node_range(node, row, col) then + if M.is_in_node_range(node, row, col) then local c = q._query.captures[capture] -- name of the capture in the query if c ~= nil then - local general_hl, is_vim_hl = q:_get_hl_from_capture(capture) - local local_hl = not is_vim_hl and (tree:lang() .. general_hl) - table.insert( - matches, - { capture = c, specific = local_hl, general = general_hl, priority = metadata.priority } - ) + table.insert(matches, { capture = c, priority = metadata.priority }) end end end -- cgit From f9641d1ac6bae58a42572ce3bfa34d62d5f22619 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Tue, 23 Aug 2022 21:05:46 +0200 Subject: refactor(lsp): factor out read_loop function --- runtime/lua/vim/lsp/rpc.lua | 57 +++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 25 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 0926912066..f96321c845 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -241,6 +241,35 @@ function default_dispatchers.on_error(code, err) local _ = log.error() and log.error('client_error:', client_errors[code], err) end +---@private +local function create_read_loop(handle_body, on_no_chunk, on_error) + local parse_chunk = coroutine.wrap(request_parser_loop) + parse_chunk() + return function(err, chunk) + if err then + on_error(err) + return + end + + if not chunk then + if on_no_chunk then + on_no_chunk() + end + return + end + + while true do + local headers, body = parse_chunk(chunk) + if headers then + handle_body(body) + chunk = '' + else + break + end + end + end +end + --- Starts an LSP server process and create an LSP RPC client object to --- interact with it. Communication with the server is currently limited to stdio. --- @@ -592,31 +621,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local request_parser = coroutine.wrap(request_parser_loop) request_parser() - stdout:read_start(function(err, chunk) - if err then - -- TODO better handling. Can these be intermittent errors? - on_error(client_errors.READ_ERROR, err) - return - end - -- This should signal that we are done reading from the client. - if not chunk then - return - end - -- Flush anything in the parser by looping until we don't get a result - -- anymore. - while true do - local headers, body = request_parser(chunk) - -- If we successfully parsed, then handle the response. - if headers then - handle_body(body) - -- Set chunk to empty so that we can call request_parser to get - -- anything existing in the parser to flush. - chunk = '' - else - break - end - end - end) + stdout:read_start(create_read_loop(handle_body, nil, function(err) + on_error(client_errors.READ_ERROR, err) + end)) return { pid = pid, -- cgit From 7d3e4aee6a11f0bd4c53b0dcb18a496b5fdd32b2 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Tue, 23 Aug 2022 22:39:34 +0200 Subject: refactor(lsp): encapsulate rpc uv handle To prepare for different transports like TCP where the handle won't have a kill method. --- runtime/lua/vim/lsp.lua | 11 +++++------ runtime/lua/vim/lsp/rpc.lua | 12 ++++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index fd64c1a495..5986f5a5e8 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1464,14 +1464,13 @@ function lsp.start_client(config) --- you request to stop a client which has previously been requested to --- shutdown, it will automatically escalate and force shutdown. --- - ---@param force (bool, optional) + ---@param force boolean|nil function client.stop(force) - local handle = rpc.handle - if handle:is_closing() then + if rpc.is_closing() then return end if force or not client.initialized or graceful_shutdown_failed then - handle:kill(15) + rpc.terminate() return end -- Sending a signal after a process has exited is acceptable. @@ -1480,7 +1479,7 @@ function lsp.start_client(config) rpc.notify('exit') else -- If there was an error in the shutdown request, then term to be safe. - handle:kill(15) + rpc.terminate() graceful_shutdown_failed = true end end) @@ -1492,7 +1491,7 @@ function lsp.start_client(config) ---@returns (bool) true if client is stopped or in the process of being ---stopped; false otherwise function client.is_stopped() - return rpc.handle:is_closing() + return rpc.is_closing() end ---@private diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index f96321c845..1fbfd3c8e1 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -405,7 +405,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) -- --- Sends a notification to the LSP server. ---@param method (string) The invoked LSP method - ---@param params (table): Parameters for the invoked LSP method + ---@param params (table|nil): Parameters for the invoked LSP method ---@returns (bool) `true` if notification could be sent, `false` if not local function notify(method, params) return encode_and_send({ @@ -432,7 +432,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) --- 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 params (table|nil) Parameters for the invoked LSP method ---@param callback (function) Callback to invoke ---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending ---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not @@ -626,8 +626,12 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end)) return { - pid = pid, - handle = handle, + is_closing = function() + return handle:is_closing() + end, + terminate = function() + handle:kill(15) + end, request = request, notify = notify, } -- cgit From 46bb34e26b3bee89fd1d5d9d1bebced00000732d Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Wed, 24 Aug 2022 19:36:37 +0200 Subject: refactor(lsp): extract rpc client from rpc.start Makes the previously inner functions re-usable for a TCP client --- runtime/lua/vim/lsp/rpc.lua | 616 ++++++++++++++++++++++++-------------------- 1 file changed, 335 insertions(+), 281 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 1fbfd3c8e1..5ac1b91e70 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -270,6 +270,292 @@ local function create_read_loop(handle_body, on_no_chunk, on_error) end end +---@class RpcClient +---@field message_index number +---@field message_callbacks table +---@field notify_reply_callbacks table +---@field transport table +---@field dispatchers table + +---@class RpcClient +local Client = {} + +---@private +function Client:encode_and_send(payload) + local _ = log.debug() and log.debug('rpc.send', payload) + if self.transport.is_closing() then + return false + end + local encoded = vim.json.encode(payload) + self.transport.write(format_message_with_content_length(encoded)) + return true +end + +---@private +--- Sends a notification to the LSP server. +---@param method (string) The invoked LSP method +---@param params (table|nil): Parameters for the invoked LSP method +---@returns (bool) `true` if notification could be sent, `false` if not +function Client:notify(method, params) + return self:encode_and_send({ + jsonrpc = '2.0', + method = method, + params = params, + }) +end + +---@private +--- sends an error object to the remote LSP process. +function Client:send_response(request_id, err, result) + return self:encode_and_send({ + id = request_id, + jsonrpc = '2.0', + error = err, + result = result, + }) +end + +---@private +--- Sends a request to the LSP server and runs {callback} upon response. +--- +---@param method (string) The invoked LSP method +---@param params (table|nil) Parameters for the invoked LSP method +---@param callback (function) Callback to invoke +---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending +---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not +function Client:request(method, params, callback, notify_reply_callback) + validate({ + callback = { callback, 'f' }, + notify_reply_callback = { notify_reply_callback, 'f', true }, + }) + self.message_index = self.message_index + 1 + local message_id = self.message_index + local result = self:encode_and_send({ + id = message_id, + jsonrpc = '2.0', + 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 + 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 + end +end + +---@private +function Client:on_error(errkind, ...) + assert(client_errors[errkind]) + -- TODO what to do if this fails? + pcall(self.dispatchers.on_error, errkind, ...) +end + +---@private +function Client:pcall_handler(errkind, status, head, ...) + if not status then + self:on_error(errkind, head, ...) + return status, head + end + return status, head, ... +end + +---@private +function Client:try_call(errkind, fn, ...) + return self:pcall_handler(errkind, pcall(fn, ...)) +end + +-- TODO periodically check message_callbacks for old requests past a certain +-- time and log them. This would require storing the timestamp. I could call +-- them with an error then, perhaps. + +---@private +function Client:handle_body(body) + local ok, decoded = pcall(vim.json.decode, body, { luanil = { object = true } }) + if not ok then + self:on_error(client_errors.INVALID_SERVER_JSON, decoded) + return + end + local _ = log.debug() and log.debug('rpc.receive', decoded) + + if type(decoded.method) == 'string' and decoded.id then + local err + -- Schedule here so that the users functions don't trigger an error and + -- we can still use the result. + schedule(function() + local status, result + status, result, err = self:try_call( + client_errors.SERVER_REQUEST_HANDLER_ERROR, + self.dispatchers.server_request, + decoded.method, + decoded.params + ) + local _ = log.debug() + and log.debug( + 'server_request: callback result', + { status = status, result = result, err = err } + ) + if status then + if not (result or err) then + -- TODO this can be a problem if `null` is sent for result. needs vim.NIL + error( + string.format( + 'method %q: either a result or an error must be sent to the server in response', + decoded.method + ) + ) + end + if err then + assert( + type(err) == 'table', + 'err must be a table. Use rpc_response_error to help format errors.' + ) + local code_name = assert( + protocol.ErrorCodes[err.code], + 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.' + ) + err.message = err.message or code_name + end + else + -- On an exception, result will contain the error message. + err = rpc_response_error(protocol.ErrorCodes.InternalError, result) + result = nil + end + self:send_response(decoded.id, err, result) + end) + -- This works because we are expecting vim.NIL here + elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then + -- We sent a number, so we expect a number. + 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] + if notify_reply_callback then + validate({ + notify_reply_callback = { notify_reply_callback, 'f' }, + }) + notify_reply_callback(result_id) + 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 + if decoded.error.code == protocol.ErrorCodes.RequestCancelled then + local _ = log.debug() and 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 + end + return + end + end + + local callback = message_callbacks and message_callbacks[result_id] + if callback then + message_callbacks[result_id] = nil + validate({ + callback = { callback, 'f' }, + }) + if decoded.error then + decoded.error = setmetatable(decoded.error, { + __tostring = format_rpc_error, + }) + end + self:try_call( + client_errors.SERVER_RESULT_CALLBACK_ERROR, + callback, + decoded.error, + decoded.result + ) + else + self:on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded) + local _ = log.error() and log.error('No callback found for server response id ' .. result_id) + end + elseif type(decoded.method) == 'string' then + -- Notification + self:try_call( + client_errors.NOTIFICATION_HANDLER_ERROR, + self.dispatchers.notification, + decoded.method, + decoded.params + ) + else + -- Invalid server message + self:on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) + end +end + +---@private +---@return RpcClient +local function new_client(dispatchers, transport) + local state = { + message_index = 0, + message_callbacks = {}, + notify_reply_callbacks = {}, + transport = transport, + dispatchers = dispatchers, + } + return setmetatable(state, { __index = Client }) +end + +---@private +---@param client RpcClient +local function public_client(client) + local result = {} + + ---@private + function result.is_closing() + return client.transport.is_closing() + end + + ---@private + function result.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|nil) Parameters for the invoked LSP method + ---@param callback (function) Callback to invoke + ---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending + ---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not + function result.request(method, params, callback, notify_reply_callback) + return client:request(method, params, callback, notify_reply_callback) + end + + --- Sends a notification to the LSP server. + ---@param method (string) The invoked LSP method + ---@param params (table|nil): Parameters for the invoked LSP method + ---@returns (bool) `true` if notification could be sent, `false` if not + function result.notify(method, params) + return client:notify(method, params) + end + + return result +end + --- Starts an LSP server process and create an LSP RPC client object to --- interact with it. Communication with the server is currently limited to stdio. --- @@ -334,134 +620,59 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local stdin = uv.new_pipe(false) local stdout = uv.new_pipe(false) local stderr = uv.new_pipe(false) - - local message_index = 0 - local message_callbacks = {} - local notify_reply_callbacks = {} - local handle, pid - do - ---@private - --- Callback for |vim.loop.spawn()| Closes all streams and runs the `on_exit` dispatcher. - ---@param code (number) Exit code - ---@param signal (number) Signal that was used to terminate (if any) - local function onexit(code, signal) - stdin:close() - stdout:close() - stderr:close() - handle:close() - -- Make sure that message_callbacks/notify_reply_callbacks can be gc'd. - message_callbacks = nil - notify_reply_callbacks = nil - dispatchers.on_exit(code, signal) - end - local spawn_params = { - args = cmd_args, - stdio = { stdin, stdout, stderr }, - detached = not is_win, - } - if extra_spawn_params then - spawn_params.cwd = extra_spawn_params.cwd - spawn_params.env = env_merge(extra_spawn_params.env) - if extra_spawn_params.detached ~= nil then - spawn_params.detached = extra_spawn_params.detached - end - end - handle, pid = uv.spawn(cmd, spawn_params, onexit) - if handle == nil then - stdin:close() - stdout:close() - stderr:close() - local msg = string.format('Spawning language server with cmd: `%s` failed', cmd) - if string.match(pid, 'ENOENT') then - msg = msg - .. '. The language server is either not installed, missing from PATH, or not executable.' - else - msg = msg .. string.format(' with error message: %s', pid) + + local client = new_client(dispatchers, { + write = function(msg) + stdin:write(msg) + end, + is_closing = function() + return handle == nil or handle:is_closing() + end, + terminate = function() + if handle then + handle:kill(15) end - vim.notify(msg, vim.log.levels.WARN) - return - end - end + end, + }) ---@private - --- Encodes {payload} into a JSON-RPC message and sends it to the remote - --- process. - --- - ---@param payload table - ---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing. - local function encode_and_send(payload) - local _ = log.debug() and log.debug('rpc.send', payload) - if handle == nil or handle:is_closing() then - return false - end - local encoded = vim.json.encode(payload) - stdin:write(format_message_with_content_length(encoded)) - return true - end - - -- FIXME: DOC: Should be placed on the RPC client object returned by - -- `start()` - -- - --- Sends a notification to the LSP server. - ---@param method (string) The invoked LSP method - ---@param params (table|nil): Parameters for the invoked LSP method - ---@returns (bool) `true` if notification could be sent, `false` if not - local function notify(method, params) - return encode_and_send({ - jsonrpc = '2.0', - method = method, - params = params, - }) + --- Callback for |vim.loop.spawn()| Closes all streams and runs the `on_exit` dispatcher. + ---@param code (number) Exit code + ---@param signal (number) Signal that was used to terminate (if any) + local function onexit(code, signal) + stdin:close() + stdout:close() + stderr:close() + handle:close() + dispatchers.on_exit(code, signal) end - - ---@private - --- sends an error object to the remote LSP process. - local function send_response(request_id, err, result) - return encode_and_send({ - id = request_id, - jsonrpc = '2.0', - error = err, - result = result, - }) + local spawn_params = { + args = cmd_args, + stdio = { stdin, stdout, stderr }, + detached = not is_win, + } + if extra_spawn_params then + spawn_params.cwd = extra_spawn_params.cwd + spawn_params.env = env_merge(extra_spawn_params.env) + if extra_spawn_params.detached ~= nil then + spawn_params.detached = extra_spawn_params.detached + end end - - -- FIXME: DOC: Should be placed on the RPC client object returned by - -- `start()` - -- - --- Sends a request to the LSP server and runs {callback} upon response. - --- - ---@param method (string) The invoked LSP method - ---@param params (table|nil) Parameters for the invoked LSP method - ---@param callback (function) Callback to invoke - ---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending - ---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not - local function request(method, params, callback, notify_reply_callback) - validate({ - callback = { callback, 'f' }, - notify_reply_callback = { notify_reply_callback, 'f', true }, - }) - message_index = message_index + 1 - local message_id = message_index - local result = encode_and_send({ - id = message_id, - jsonrpc = '2.0', - method = method, - params = params, - }) - if result then - if message_callbacks then - message_callbacks[message_id] = schedule_wrap(callback) - else - return false - 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 + handle, pid = uv.spawn(cmd, spawn_params, onexit) + if handle == nil then + stdin:close() + stdout:close() + stderr:close() + local msg = string.format('Spawning language server with cmd: `%s` failed', cmd) + if string.match(pid, 'ENOENT') then + msg = msg + .. '. The language server is either not installed, missing from PATH, or not executable.' else - return false + msg = msg .. string.format(' with error message: %s', pid) end + vim.notify(msg, vim.log.levels.WARN) + return end stderr:read_start(function(_, chunk) @@ -470,171 +681,14 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end end) - ---@private - local function on_error(errkind, ...) - assert(client_errors[errkind]) - -- TODO what to do if this fails? - pcall(dispatchers.on_error, errkind, ...) - end - ---@private - local function pcall_handler(errkind, status, head, ...) - if not status then - on_error(errkind, head, ...) - return status, head - end - return status, head, ... - end - ---@private - local function try_call(errkind, fn, ...) - return pcall_handler(errkind, pcall(fn, ...)) - end - - -- TODO periodically check message_callbacks for old requests past a certain - -- time and log them. This would require storing the timestamp. I could call - -- them with an error then, perhaps. - - ---@private - local function handle_body(body) - local ok, decoded = pcall(vim.json.decode, body, { luanil = { object = true } }) - if not ok then - on_error(client_errors.INVALID_SERVER_JSON, decoded) - return - end - local _ = log.debug() and log.debug('rpc.receive', decoded) - - if type(decoded.method) == 'string' and decoded.id then - local err - -- Schedule here so that the users functions don't trigger an error and - -- we can still use the result. - schedule(function() - local status, result - status, result, err = try_call( - client_errors.SERVER_REQUEST_HANDLER_ERROR, - dispatchers.server_request, - decoded.method, - decoded.params - ) - local _ = log.debug() - and log.debug( - 'server_request: callback result', - { status = status, result = result, err = err } - ) - if status then - if not (result or err) then - -- TODO this can be a problem if `null` is sent for result. needs vim.NIL - error( - string.format( - 'method %q: either a result or an error must be sent to the server in response', - decoded.method - ) - ) - end - if err then - assert( - type(err) == 'table', - 'err must be a table. Use rpc_response_error to help format errors.' - ) - local code_name = assert( - protocol.ErrorCodes[err.code], - 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.' - ) - err.message = err.message or code_name - end - else - -- On an exception, result will contain the error message. - err = rpc_response_error(protocol.ErrorCodes.InternalError, result) - result = nil - end - send_response(decoded.id, err, result) - end) - -- This works because we are expecting vim.NIL here - elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then - -- We sent a number, so we expect a number. - 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_callback = notify_reply_callbacks and notify_reply_callbacks[result_id] - if notify_reply_callback then - validate({ - notify_reply_callback = { notify_reply_callback, 'f' }, - }) - notify_reply_callback(result_id) - notify_reply_callbacks[result_id] = nil - end - - -- Do not surface RequestCancelled to users, it is RPC-internal. - if decoded.error then - local mute_error = false - if decoded.error.code == protocol.ErrorCodes.RequestCancelled then - local _ = log.debug() and 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 - end - return - end - end - - local callback = message_callbacks and message_callbacks[result_id] - if callback then - message_callbacks[result_id] = nil - validate({ - callback = { callback, 'f' }, - }) - if decoded.error then - decoded.error = setmetatable(decoded.error, { - __tostring = format_rpc_error, - }) - end - try_call( - client_errors.SERVER_RESULT_CALLBACK_ERROR, - callback, - decoded.error, - decoded.result - ) - else - on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded) - local _ = log.error() - and log.error('No callback found for server response id ' .. result_id) - end - elseif type(decoded.method) == 'string' then - -- Notification - try_call( - client_errors.NOTIFICATION_HANDLER_ERROR, - dispatchers.notification, - decoded.method, - decoded.params - ) - else - -- Invalid server message - on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) - end + local handle_body = function(body) + client:handle_body(body) end - - local request_parser = coroutine.wrap(request_parser_loop) - request_parser() stdout:read_start(create_read_loop(handle_body, nil, function(err) - on_error(client_errors.READ_ERROR, err) + client:on_error(client_errors.READ_ERROR, err) end)) - return { - is_closing = function() - return handle:is_closing() - end, - terminate = function() - handle:kill(15) - end, - request = request, - notify = notify, - } + return public_client(client) end return { -- cgit From 60ec6e34d585a7f633d49aab790066c1740885e1 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Wed, 24 Aug 2022 20:25:34 +0200 Subject: feat(lsp): add tcp support --- runtime/lua/vim/lsp.lua | 38 +++++++++++----- runtime/lua/vim/lsp/rpc.lua | 104 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 109 insertions(+), 33 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5986f5a5e8..1dc1a045fd 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -289,7 +289,12 @@ local function validate_client_config(config) 'flags.debounce_text_changes must be a number with the debounce time in milliseconds' ) - local cmd, cmd_args = lsp._cmd_parts(config.cmd) + local cmd, cmd_args + if type(config.cmd) == 'function' then + cmd = config.cmd + else + cmd, cmd_args = lsp._cmd_parts(config.cmd) + end local offset_encoding = valid_encodings.UTF16 if config.offset_encoding then offset_encoding = validate_encoding(config.offset_encoding) @@ -855,14 +860,17 @@ end --- Used on all running clients. --- The default implementation re-uses a client if name --- and root_dir matches. ----@return number client_id +---@return number|nil client_id function lsp.start(config, opts) opts = opts or {} local reuse_client = opts.reuse_client or function(client, conf) return client.config.root_dir == conf.root_dir and client.name == conf.name end - config.name = config.name or (config.cmd[1] and vim.fs.basename(config.cmd[1])) or nil + config.name = config.name + if not config.name and type(config.cmd) == 'table' then + config.name = config.cmd[1] and vim.fs.basename(config.cmd[1]) or nil + end local bufnr = api.nvim_get_current_buf() for _, clients in ipairs({ uninitialized_clients, lsp.get_active_clients() }) do for _, client in pairs(clients) do @@ -893,8 +901,13 @@ end --- The following parameters describe fields in the {config} table. --- --- ----@param cmd: (required, string or list treated like |jobstart()|) Base command ---- that initiates the LSP client. +---@param cmd: (table|string|fun(dispatchers: table):table) command string or +--- list treated like |jobstart|. The command must launch the language server +--- process. `cmd` can also be a function that creates an RPC client. +--- The function receives a dispatchers table and must return a table with the +--- functions `request`, `notify`, `is_closing` and `terminate` +--- See |vim.lsp.rpc.request| and |vim.lsp.rpc.notify| +--- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect| --- ---@param cmd_cwd: (string, default=|getcwd()|) Directory to launch --- the `cmd` process. Not related to `root_dir`. @@ -1164,11 +1177,16 @@ function lsp.start_client(config) end -- Start the RPC client. - local rpc = lsp_rpc.start(cmd, cmd_args, dispatch, { - cwd = config.cmd_cwd, - env = config.cmd_env, - detached = config.detached, - }) + local rpc + if type(cmd) == 'function' then + rpc = cmd(dispatch) + else + rpc = lsp_rpc.start(cmd, cmd_args, dispatch, { + cwd = config.cmd_cwd, + env = config.cmd_env, + detached = config.detached, + }) + end -- Return nil if client fails to start if not rpc then diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 5ac1b91e70..238c6c0570 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -556,6 +556,84 @@ local function public_client(client) return result end +---@private +local function merge_dispatchers(dispatchers) + if dispatchers then + local user_dispatchers = dispatchers + dispatchers = {} + for dispatch_name, default_dispatch in pairs(default_dispatchers) do + local user_dispatcher = user_dispatchers[dispatch_name] + if user_dispatcher then + if type(user_dispatcher) ~= 'function' then + error(string.format('dispatcher.%s must be a function', dispatch_name)) + end + -- server_request is wrapped elsewhere. + if + not (dispatch_name == 'server_request' or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason. + then + user_dispatcher = schedule_wrap(user_dispatcher) + end + dispatchers[dispatch_name] = user_dispatcher + else + dispatchers[dispatch_name] = default_dispatch + end + end + else + dispatchers = default_dispatchers + end + return dispatchers +end + +--- Create a LSP RPC client factory that connects via TCP to the given host +--- and port +--- +---@param host string +---@param port number +---@return function +local function connect(host, port) + return function(dispatchers) + dispatchers = merge_dispatchers(dispatchers) + local tcp = uv.new_tcp() + local closing = false + local transport = { + write = function(msg) + tcp:write(msg) + end, + is_closing = function() + return closing + end, + terminate = function() + if not closing then + closing = true + tcp:shutdown() + tcp:close() + dispatchers.on_exit(0, 0) + end + end, + } + local client = new_client(dispatchers, transport) + tcp:connect(host, port, function(err) + if err then + vim.schedule(function() + vim.notify( + string.format('Could not connect to %s:%s, reason: %s', host, port, vim.inspect(err)), + vim.log.levels.WARN + ) + end) + return + end + local handle_body = function(body) + client:handle_body(body) + end + tcp:read_start(create_read_loop(handle_body, transport.terminate, function(read_err) + client:on_error(client_errors.READ_ERROR, read_err) + end)) + end) + + return public_client(client) + end +end + --- Starts an LSP server process and create an LSP RPC client object to --- interact with it. Communication with the server is currently limited to stdio. --- @@ -593,30 +671,8 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) if extra_spawn_params and extra_spawn_params.cwd then assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory') end - if dispatchers then - local user_dispatchers = dispatchers - dispatchers = {} - for dispatch_name, default_dispatch in pairs(default_dispatchers) do - local user_dispatcher = user_dispatchers[dispatch_name] - if user_dispatcher then - if type(user_dispatcher) ~= 'function' then - error(string.format('dispatcher.%s must be a function', dispatch_name)) - end - -- server_request is wrapped elsewhere. - if - not (dispatch_name == 'server_request' or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason. - then - user_dispatcher = schedule_wrap(user_dispatcher) - end - dispatchers[dispatch_name] = user_dispatcher - else - dispatchers[dispatch_name] = default_dispatch - end - end - else - dispatchers = default_dispatchers - end + dispatchers = merge_dispatchers(dispatchers) local stdin = uv.new_pipe(false) local stdout = uv.new_pipe(false) local stderr = uv.new_pipe(false) @@ -693,8 +749,10 @@ end return { start = start, + connect = connect, rpc_response_error = rpc_response_error, format_rpc_error = format_rpc_error, client_errors = client_errors, + create_read_loop = create_read_loop, } -- vim:sw=2 ts=2 et -- cgit From efacb6e974fa6391bcc916749103f04fa9b9f6f7 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 30 Aug 2022 01:09:14 +0800 Subject: fix(lsp): clean the diagnostic cache when buffer delete (#19449) Co-authored-by: Gregory Anders --- runtime/lua/vim/diagnostic.lua | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 3f71d4f70d..db92085423 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -45,18 +45,24 @@ local bufnr_and_namespace_cacher_mt = { end, } -local diagnostic_cache = setmetatable({}, { - __index = function(t, bufnr) - assert(bufnr > 0, 'Invalid buffer number') - vim.api.nvim_buf_attach(bufnr, false, { - on_detach = function() - rawset(t, bufnr, nil) -- clear cache - end, - }) - t[bufnr] = {} - return t[bufnr] - end, -}) +local diagnostic_cache +do + local group = vim.api.nvim_create_augroup('DiagnosticBufDelete', {}) + diagnostic_cache = setmetatable({}, { + __index = function(t, bufnr) + assert(bufnr > 0, 'Invalid buffer number') + vim.api.nvim_create_autocmd('BufDelete', { + group = group, + buffer = bufnr, + callback = function() + rawset(t, bufnr, nil) + end, + }) + t[bufnr] = {} + return t[bufnr] + end, + }) +end local diagnostic_cache_extmarks = setmetatable({}, bufnr_and_namespace_cacher_mt) local diagnostic_attached_buffers = {} -- cgit From 981ae83fadd3bf8603f144a8bc27347e4fb7b3ad Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Tue, 30 Aug 2022 13:14:27 +0200 Subject: fix(docs): update lsp.rpc.start docs to match return value changes (#20003) Follow up to https://github.com/neovim/neovim/pull/19916 --- runtime/lua/vim/lsp/rpc.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 238c6c0570..70f838f34d 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -654,11 +654,8 @@ end ---@returns Methods: --- - `notify()` |vim.lsp.rpc.notify()| --- - `request()` |vim.lsp.rpc.request()| ---- ----@returns Members: ---- - {pid} (number) The LSP server's PID. ---- - {handle} A handle for low-level interaction with the LSP server process ---- |vim.loop|. +--- - `is_closing()` returns a boolean indicating if the RPC is closing. +--- - `terminate()` terminates the RPC client. local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local _ = log.info() and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params }) -- cgit From 6b7eed1884c3b022cc15568c39f80f8f4da0780b Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 30 Aug 2022 21:16:03 +0200 Subject: Vim 9.0.{0314,0319}: some filetypes are not recognized (#20005) * vim-patch:9.0.0314: VDM files are not recognized Problem: VDM files are not recognized. Solution: Add patterns for VDM files. (Alessandro Pezzoni, closes vim/vim#11004) https://github.com/vim/vim/commit/bf26941f40923d331169a4ecb7341608f5d1ca38 * vim-patch:9.0.0319: Godot shader files are not recognized Problem: Godot shader files are not recognized. Solution: Add patterns for "gdshader". (Maxim Kim, closes vim/vim#11006) https://github.com/vim/vim/commit/d5c8f11905abc1bdf3b8864dbc40187855ed9374 --- runtime/lua/vim/filetype.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 99c98764dd..fcd697a7c1 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -422,9 +422,11 @@ local extension = { gdb = 'gdb', gdmo = 'gdmo', mo = 'gdmo', - tres = 'gdresource', tscn = 'gdresource', + tres = 'gdresource', gd = 'gdscript', + gdshader = 'gdshader', + shader = 'gdshader', ged = 'gedcom', gmi = 'gemtext', gemini = 'gemtext', @@ -1011,6 +1013,11 @@ local extension = { dsm = 'vb', ctl = 'vb', vbs = 'vb', + vdmpp = 'vdmpp', + vpp = 'vdmpp', + vdmrt = 'vdmrt', + vdmsl = 'vdmsl', + vdm = 'vdmsl', vr = 'vera', vri = 'vera', vrh = 'vera', -- cgit From ce80b8f50d7d56ac12aa06a64a65799ec18b69af Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Fri, 2 Sep 2022 08:16:17 +0200 Subject: vim-patch:9.0.0349: filetype of *.sil files not well detected (#20050) Problem: Filetype of *.sil files not well detected. Solution: Inspect the file contents to guess the filetype. https://github.com/vim/vim/commit/be807d582499acbe314ead3891481cba6ca136df --- runtime/lua/vim/filetype.lua | 4 +++- runtime/lua/vim/filetype/detect.lua | 13 +++++++++++++ 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 fcd697a7c1..6306605641 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -902,7 +902,9 @@ local extension = { sig = function(path, bufnr) return require('vim.filetype.detect').sig(bufnr) end, - sil = 'sil', + sil = function(path, bufnr) + return require('vim.filetype.detect').sil(bufnr) + end, sim = 'simula', ['s85'] = 'sinda', sin = 'sinda', diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 2be9dcff88..7fc7f1b7ca 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1194,6 +1194,19 @@ function M.shell(path, contents, name) return name end +-- Swift Intermediate Language or SILE +function M.sil(bufnr) + for _, line in ipairs(getlines(bufnr, 1, 100)) do + if line:find('^%s*[\\%%]') then + return 'sile' + elseif line:find('^%s*%S') then + return 'sil' + end + end + -- No clue, default to "sil" + return 'sil' +end + -- SMIL or SNMP MIB file function M.smi(bufnr) local line = getlines(bufnr, 1) -- cgit From 0822896efcf0da7002e323369fdc1e4a15ad1d57 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 5 Sep 2022 15:52:27 +0200 Subject: feat(treesitter): add vim.treesitter.start(), enable for Lua * Add vim.treesitter.start() for starting treesitter highlighting via ftplugin or autocommand (can be extended later for fold, indent, matchpairs, ...) * Add vim.treesitter.stop() for manually stopping treesitter highlighting * Enable treesitter highlighting for Lua if `vim.g.ts_highlight_lua = true` is set in `init.lua` --- runtime/lua/vim/treesitter.lua | 86 +++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 6431162799..9c43811e03 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -32,9 +32,11 @@ setmetatable(M, { --- --- It is not recommended to use this, use vim.treesitter.get_parser() instead. --- ----@param bufnr The buffer the parser will be tied to ----@param lang The language of the parser ----@param opts Options to pass to the created language tree +---@param bufnr string Buffer the parser will be tied to (0 for current buffer) +---@param lang string Language of the parser +---@param opts table|nil Options to pass to the created language tree +--- +---@returns table Created parser object function M._create_parser(bufnr, lang, opts) language.require_language(lang) if bufnr == 0 then @@ -79,11 +81,11 @@ end --- If needed this will create the parser. --- Unconditionally attach the provided callback --- ----@param bufnr The buffer the parser should be tied to ----@param lang The filetype of this parser ----@param opts Options object to pass to the created language tree +---@param bufnr number|nil Buffer the parser should be tied to: (default current buffer) +---@param lang string |nil Filetype of this parser (default: buffer filetype) +---@param opts table|nil Options to pass to the created language tree --- ----@returns The parser +---@returns table Parser object function M.get_parser(bufnr, lang, opts) opts = opts or {} @@ -120,8 +122,8 @@ end --- Determines whether a node is the ancestor of another --- ----@param dest table the possible ancestor ----@param source table the possible descendant node +---@param dest table Possible ancestor +---@param source table Possible descendant node --- ---@returns (boolean) True if dest is an ancestor of source function M.is_ancestor(dest, source) @@ -156,9 +158,11 @@ end ---Determines whether (line, col) position is in node range --- ----@param node Node defining the range ----@param line A line (0-based) ----@param col A column (0-based) +---@param node table Node defining the range +---@param line number Line (0-based) +---@param col number Column (0-based) +--- +---@returns (boolean) True if the position is in node range function M.is_in_node_range(node, line, col) local start_line, start_col, end_line, end_col = M.get_node_range(node) if line >= start_line and line <= end_line then @@ -177,8 +181,8 @@ function M.is_in_node_range(node, line, col) end ---Determines if a node contains a range ----@param node table The node ----@param range table The range +---@param node table +---@param range table --- ---@returns (boolean) True if the node contains the range function M.node_contains(node, range) @@ -190,9 +194,9 @@ function M.node_contains(node, range) end ---Gets a list of captures for a given cursor position ----@param bufnr number The buffer number ----@param row number The position row ----@param col number The position column +---@param bufnr number Buffer number (0 for current buffer) +---@param row number Position row +---@param col number Position column --- ---@returns (table) A table of captures function M.get_captures_at_position(bufnr, row, col) @@ -241,4 +245,52 @@ function M.get_captures_at_position(bufnr, row, col) return matches end +--- Start treesitter highlighting for a buffer +--- +--- Can be used in an ftplugin or FileType autocommand +--- +--- Note: By default, disables regex syntax highlighting, which may be required for some plugins. +--- In this case, add `{ syntax = true }`. +--- +--- Example: +--- +---
+--- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
+---     callback = function(args)
+---         vim.treesitter.start(args.buf, 'latex', { syntax = true })
+---     end
+--- })
+--- 
+--- +---@param bufnr number|nil Buffer to be highlighted (default: current buffer) +---@param lang string|nil Language of the parser (default: buffer filetype) +---@param opts table|nil Optional keyword arguments: +--- - `syntax` boolean Run regex syntax highlighting (default false) +function M.start(bufnr, lang, opts) + bufnr = bufnr or a.nvim_get_current_buf() + + local parser = M.get_parser(bufnr, lang) + + M.highlighter.new(parser) + + vim.b[bufnr].ts_highlight = true + + if opts and opts.syntax then + vim.bo[bufnr].syntax = 'on' + end +end + +---Stop treesitter highlighting for a buffer +--- +---@param bufnr number|nil Buffer to stop highlighting (default: current buffer) +function M.stop(bufnr) + bufnr = bufnr or a.nvim_get_current_buf() + + if M.highlighter.active[bufnr] then + M.highlighter.active[bufnr]:destroy() + end + + vim.bo[bufnr].syntax = 'on' +end + return M -- cgit From ffe98531b9a6a90a7f4a7ae2105b3c50ad9332fd Mon Sep 17 00:00:00 2001 From: Quentin Rasmont Date: Sat, 30 Apr 2022 11:51:14 +0200 Subject: feat(treesitter): upstream get_node_at_cursor() Util from the nvim-treesitter project. --- runtime/lua/vim/treesitter.lua | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 9c43811e03..d71f8611c1 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -81,7 +81,7 @@ end --- If needed this will create the parser. --- Unconditionally attach the provided callback --- ----@param bufnr number|nil Buffer the parser should be tied to: (default current buffer) +---@param bufnr number|nil Buffer the parser should be tied to (default: current buffer) ---@param lang string |nil Filetype of this parser (default: buffer filetype) ---@param opts table|nil Options to pass to the created language tree --- @@ -245,6 +245,27 @@ function M.get_captures_at_position(bufnr, row, col) return matches end +--- Gets the smallest named node under the cursor +--- +---@param winnr number Window handle or 0 for current window +---@param opts table Options table +---@param opts.ignore_injections boolean (default true) Ignore injected languages. +--- +---@returns (table) The named node under the cursor +function M.get_node_at_cursor(winnr, opts) + winnr = winnr or 0 + local cursor = a.nvim_win_get_cursor(winnr) + local ts_cursor_range = { cursor[1] - 1, cursor[2], cursor[1] - 1, cursor[2] } + + local buf = a.nvim_win_get_buf(winnr) + local root_lang_tree = M.get_parser(buf) + if not root_lang_tree then + return + end + + return root_lang_tree:named_node_for_range(ts_cursor_range, opts) +end + --- Start treesitter highlighting for a buffer --- --- Can be used in an ftplugin or FileType autocommand -- cgit From 95fd1ad83e24bbb14cc084fb001251939de6c0a9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 6 Sep 2022 08:50:06 +0200 Subject: refactor(treesitter): get_{nodes,captures}_at_{position,cursor} --- runtime/lua/vim/treesitter.lua | 65 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index d71f8611c1..8a540b9012 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -147,7 +147,7 @@ end --- ---@param node_or_range table --- ----@returns start_row, start_col, end_row, end_col +---@returns table start_row, start_col, end_row, end_col function M.get_node_range(node_or_range) if type(node_or_range) == 'table' then return unpack(node_or_range) @@ -198,7 +198,11 @@ end ---@param row number Position row ---@param col number Position column --- ----@returns (table) A table of captures +---@param bufnr number Buffer number (0 for current buffer) +---@param row number Position row +---@param col number Position column +--- +---@returns (table) Table of captures function M.get_captures_at_position(bufnr, row, col) if bufnr == 0 then bufnr = a.nvim_get_current_buf() @@ -245,25 +249,62 @@ function M.get_captures_at_position(bufnr, row, col) return matches end ---- Gets the smallest named node under the cursor +---Gets a list of captures under the cursor --- ----@param winnr number Window handle or 0 for current window ----@param opts table Options table ----@param opts.ignore_injections boolean (default true) Ignore injected languages. +---@param winnr number|nil Window handle or 0 for current window (default) --- ----@returns (table) The named node under the cursor -function M.get_node_at_cursor(winnr, opts) +---@returns (table) Named node under the cursor +function M.get_captures_at_cursor(winnr) winnr = winnr or 0 + local bufnr = a.nvim_win_get_buf(winnr) local cursor = a.nvim_win_get_cursor(winnr) - local ts_cursor_range = { cursor[1] - 1, cursor[2], cursor[1] - 1, cursor[2] } - local buf = a.nvim_win_get_buf(winnr) - local root_lang_tree = M.get_parser(buf) + local data = M.get_captures_at_position(bufnr, cursor[1] - 1, cursor[2]) + + local captures = {} + + for _, capture in ipairs(data) do + table.insert(captures, capture.capture) + end + + return captures +end + +--- Gets the smallest named node at position +--- +---@param bufnr number Buffer number (0 for current buffer) +---@param row number Position row +---@param col number Position column +---@param opts table Optional keyword arguments: +--- - ignore_injections boolean Ignore injected languages (default true) +--- +---@returns (table) Named node under the cursor +function M.get_node_at_position(bufnr, row, col, opts) + if bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + local ts_range = { row, col, row, col } + + local root_lang_tree = M.get_parser(bufnr) if not root_lang_tree then return end - return root_lang_tree:named_node_for_range(ts_cursor_range, opts) + return root_lang_tree:named_node_for_range(ts_range, opts) +end + +--- Gets the smallest named node under the cursor +--- +---@param winnr number|nil Window handle or 0 for current window (default) +--- +---@returns (string) Named node under the cursor +function M.get_node_at_cursor(winnr) + winnr = winnr or 0 + local bufnr = a.nvim_win_get_buf(winnr) + local cursor = a.nvim_win_get_cursor(winnr) + + return M.get_node_at_position(bufnr, cursor[1] - 1, cursor[2], { ignore_injections = false }) + :type() end --- Start treesitter highlighting for a buffer -- cgit From 75adfefc85bcf0d62d2c0f51a951e6003b595cea Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Mon, 18 Jul 2022 14:21:40 +0200 Subject: feat(extmarks,ts,spell): full support for spelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added 'spell' option to extmarks: Extmarks with this set will have the region spellchecked. - Added 'noplainbuffer' option to 'spelloptions': This is used to tell Neovim not to spellcheck the buffer. The old behaviour was to spell check the whole buffer unless :syntax was set. - Added spelling support to the treesitter highlighter: @spell captures in highlights.scm are used to define regions which should be spell checked. - Added support for navigating spell errors for extmarks: Works for both ephemeral and static extmarks - Added '_on_spell_nav' callback for decoration providers: Since ephemeral callbacks are only drawn for the visible screen, providers must implement this callback to instruct Neovim which regions in the buffer need can be spell checked. The callback takes a start position and an end position. Note: this callback is subject to change hence the _ prefix. - Added spell captures for built-in support languages Co-authored-by: Lewis Russell Co-authored-by: Björn Linse --- runtime/lua/vim/treesitter/highlighter.lua | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 1f242b0fdd..9e95af98db 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -97,6 +97,7 @@ function TSHighlighter.new(tree, opts) if vim.g.syntax_on ~= 1 then vim.api.nvim_command('runtime! syntax/synload.vim') end + vim.bo[self.bufnr].spelloptions = 'noplainbuffer' self.tree:parse() @@ -156,7 +157,7 @@ function TSHighlighter:get_query(lang) end ---@private -local function on_line_impl(self, buf, line) +local function on_line_impl(self, buf, line, spell) self.tree:for_each_tree(function(tstree, tree) if not tstree then return @@ -193,7 +194,9 @@ local function on_line_impl(self, buf, line) local start_row, start_col, end_row, end_col = node:range() local hl = highlighter_query.hl_cache[capture] - if hl and end_row >= line then + local is_spell = highlighter_query:query().captures[capture] == 'spell' + + if hl and end_row >= line and (not spell or is_spell) then a.nvim_buf_set_extmark(buf, ns, start_row, start_col, { end_line = end_row, end_col = end_col, @@ -201,6 +204,7 @@ local function on_line_impl(self, buf, line) ephemeral = true, priority = tonumber(metadata.priority) or 100, -- Low but leaves room below conceal = metadata.conceal, + spell = is_spell, }) end if start_row > line then @@ -217,7 +221,21 @@ function TSHighlighter._on_line(_, _win, buf, line, _) return end - on_line_impl(self, buf, line) + on_line_impl(self, buf, line, false) +end + +---@private +function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _) + local self = TSHighlighter.active[buf] + if not self then + return + end + + self:reset_highlight_state() + + for row = srow, erow do + on_line_impl(self, buf, row, true) + end end ---@private @@ -244,6 +262,7 @@ a.nvim_set_decoration_provider(ns, { on_buf = TSHighlighter._on_buf, on_win = TSHighlighter._on_win, on_line = TSHighlighter._on_line, + _on_spell_nav = TSHighlighter._on_spell_nav, }) return TSHighlighter -- cgit From d01cadd82fc74baf7d292c5cbbcf223e0aa2c097 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 6 Sep 2022 17:33:44 +0200 Subject: fix(treesitter): don't support legacy syntax in start() --- runtime/lua/vim/treesitter.lua | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 9c43811e03..5ebccff8f7 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -250,23 +250,22 @@ end --- Can be used in an ftplugin or FileType autocommand --- --- Note: By default, disables regex syntax highlighting, which may be required for some plugins. ---- In this case, add `{ syntax = true }`. +--- In this case, add `vim.bo.syntax = 'on'` after the call to `start`. --- --- Example: --- ---
 --- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
 ---     callback = function(args)
----         vim.treesitter.start(args.buf, 'latex', { syntax = true })
+---         vim.treesitter.start(args.buf, 'latex')
+---         vim.bo[args.buf].syntax = 'on'  -- only if additional legacy syntax is needed
 ---     end
 --- })
 --- 
--- ---@param bufnr number|nil Buffer to be highlighted (default: current buffer) ---@param lang string|nil Language of the parser (default: buffer filetype) ----@param opts table|nil Optional keyword arguments: ---- - `syntax` boolean Run regex syntax highlighting (default false) -function M.start(bufnr, lang, opts) +function M.start(bufnr, lang) bufnr = bufnr or a.nvim_get_current_buf() local parser = M.get_parser(bufnr, lang) @@ -274,10 +273,6 @@ function M.start(bufnr, lang, opts) M.highlighter.new(parser) vim.b[bufnr].ts_highlight = true - - if opts and opts.syntax then - vim.bo[bufnr].syntax = 'on' - end end ---Stop treesitter highlighting for a buffer -- cgit From 707edfc9e6a1745f268d1a9184183da521dc86b8 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 6 Sep 2022 19:22:05 +0100 Subject: fix(ts): do not clobber spelloptions (#20095) --- runtime/lua/vim/treesitter/highlighter.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 9e95af98db..1e625eddb8 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -86,7 +86,7 @@ function TSHighlighter.new(tree, opts) end end - a.nvim_buf_set_option(self.bufnr, 'syntax', '') + vim.bo[self.bufnr].syntax = '' TSHighlighter.active[self.bufnr] = self @@ -95,9 +95,12 @@ function TSHighlighter.new(tree, opts) -- syntax FileType autocmds. Later on we should integrate with the -- `:syntax` and `set syntax=...` machinery properly. if vim.g.syntax_on ~= 1 then - vim.api.nvim_command('runtime! syntax/synload.vim') + vim.cmd.runtime({ 'syntax/synload.vim', bang = true }) end - vim.bo[self.bufnr].spelloptions = 'noplainbuffer' + + a.nvim_buf_call(self.bufnr, function() + vim.opt_local.spelloptions:append('noplainbuffer') + end) self.tree:parse() -- cgit From f32fd19f1eedbd75e6a37b73f28cf8761e0e875c Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 7 Sep 2022 03:55:03 +0100 Subject: fix(diagnostic): remove buf from cache on `BufWipeout` (#20099) Doing so on `BufDelete` has issues: - `BufDelete` is also fired for listed buffers that are made unlisted. - `BufDelete` is not fired for unlisted buffers that are deleted. This means that diagnostics will be lost for a buffer that becomes unlisted. It also means that if an entry exists for an unlisted buffer, deleting that buffer later will not remove its entry from the cache (and you may see "Invalid buffer id" errors when using diagnostic functions if it was wiped). Instead, remove a buffer from the cache if it is wiped out. This means simply `:bd`ing a buffer will not clear its diagnostics now. --- 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 db92085423..4f7d8cccd5 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -47,11 +47,11 @@ local bufnr_and_namespace_cacher_mt = { local diagnostic_cache do - local group = vim.api.nvim_create_augroup('DiagnosticBufDelete', {}) + local group = vim.api.nvim_create_augroup('DiagnosticBufWipeout', {}) diagnostic_cache = setmetatable({}, { __index = function(t, bufnr) assert(bufnr > 0, 'Invalid buffer number') - vim.api.nvim_create_autocmd('BufDelete', { + vim.api.nvim_create_autocmd('BufWipeout', { group = group, buffer = bufnr, callback = function() -- cgit From fd1595514b747d8b083f78007579d869ccfbe89c Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Wed, 7 Sep 2022 08:39:56 +0200 Subject: Use weak tables in tree-sitter code (#17117) feat(treesitter): use weak tables when possible Also add the defaulttable function to create a table whose values are created when a key is missing. --- runtime/lua/vim/shared.lua | 25 +++++++++++++++++++++++++ runtime/lua/vim/treesitter.lua | 5 +---- runtime/lua/vim/treesitter/query.lua | 9 +++------ 3 files changed, 29 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index e1b4ed4ea9..59cb669609 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -715,5 +715,30 @@ function vim.is_callable(f) return type(m.__call) == 'function' end +--- Creates a table whose members are automatically created when accessed, if they don't already +--- exist. +--- +--- They mimic defaultdict in python. +--- +--- If @p create is @c nil, this will create a defaulttable whose constructor function is +--- this function, effectively allowing to create nested tables on the fly: +--- +---
+--- local a = vim.defaulttable()
+--- a.b.c = 1
+--- 
+--- +---@param create function|nil The function called to create a missing value. +---@return table Empty table with metamethod +function vim.defaulttable(create) + create = create or vim.defaulttable + return setmetatable({}, { + __index = function(tbl, key) + rawset(tbl, key, create()) + return rawget(tbl, key) + end, + }) +end + return vim -- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 5ebccff8f7..cde0491b12 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -3,10 +3,7 @@ local query = require('vim.treesitter.query') local language = require('vim.treesitter.language') local LanguageTree = require('vim.treesitter.languagetree') --- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. --- Consider use weak references to release parser if all plugins are done with --- it. -local parsers = {} +local parsers = setmetatable({}, { __mode = 'v' }) local M = vim.tbl_extend('error', query, language) diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 697e2e7691..78042a74bf 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -140,12 +140,9 @@ function M.get_query(lang, query_name) end end -local query_cache = setmetatable({}, { - __index = function(tbl, key) - rawset(tbl, key, {}) - return rawget(tbl, key) - end, -}) +local query_cache = vim.defaulttable(function() + return setmetatable({}, { __mode = 'v' }) +end) --- Parse {query} as a string. (If the query is in a file, the caller --- should read the contents into a string before calling). -- cgit From 9d1d3a67073ab04f29a1e437e90faede764a4313 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 7 Sep 2022 15:55:39 +0200 Subject: vim-patch:9.0.0402: javascript module files are not recoginzed (#20108) Problem: Javascript module files are not recoginzed. Solution: Recognize "*.jsm" files as Javascript. (Brett Holman, closes vim/vim#11069) https://github.com/vim/vim/commit/bb6c4073e79e86ef69c315338e00c12f0d8d6395 --- 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 6306605641..f76b4cced9 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -540,6 +540,7 @@ local extension = { mjs = 'javascript', javascript = 'javascript', js = 'javascript', + jsm = 'javascript', cjs = 'javascript', jsx = 'javascriptreact', clp = 'jess', -- cgit From 4dc4cf346755375e49410e16635c00a602b26c36 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Wed, 7 Sep 2022 17:59:27 +0200 Subject: fix(options): mark `winhighlight` as list style (#19477) Also add missing fcs, lcs and winhighlight to list of key-value options for `vim.opt`. Co-authored-by: ii14 --- runtime/lua/vim/_meta.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index f1652718ee..0f45c916dc 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -198,7 +198,10 @@ end -- Can be done in a separate PR. local key_value_options = { fillchars = true, + fcs = true, listchars = true, + lcs = true, + winhighlight = true, winhl = true, } -- cgit From 99e6e0f221ccdb7aa983121359aedb7791e870dd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 8 Sep 2022 12:54:41 +0800 Subject: docs(treesitter): fix doxygen --- runtime/lua/vim/treesitter.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index cde0491b12..69faea7edc 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -247,7 +247,7 @@ end --- Can be used in an ftplugin or FileType autocommand --- --- 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`. +--- In this case, add ``vim.bo.syntax = 'on'`` after the call to `start`. --- --- Example: --- -- cgit From 0405594399741babd6d935d581eac2584b289f92 Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Thu, 8 Sep 2022 09:47:36 +0200 Subject: feat(treesitter)!: do not merge queries by default (#20105) Problem: Treesitter queries for a given language in runtime were merged together, leading to errors if they targeted different parser versions (e.g., bundled viml queries and those shipped by nvim-treesitter). Solution: Runtime queries now work as follows: * The last query in the rtp without `; extends` in the header will be used as the base query * All queries (without a specific order) with `; extends` are concatenated with the base query BREAKING CHANGE: queries need to be updated if they are meant to extend other queries --- runtime/lua/vim/treesitter/query.lua | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 78042a74bf..be5b24fd95 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -47,6 +47,9 @@ function M.get_query_files(lang, query_name, is_included) return {} end + local base_query = nil + local extensions = {} + local base_langs = {} -- Now get the base languages by looking at the first line of every file @@ -55,13 +58,26 @@ function M.get_query_files(lang, query_name, is_included) -- -- {language} ::= {lang} | ({lang}) local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$' + local EXTENDS_FORMAT = '^;+%s*extends%s*$' - for _, file in ipairs(lang_files) do - local modeline = safe_read(file, '*l') + for _, filename in ipairs(lang_files) do + local file, err = io.open(filename, 'r') + if not file then + error(err) + end - if modeline then - local langlist = modeline:match(MODELINE_FORMAT) + local extension = false + + for modeline in + function() + return file:read('*l') + end + do + if not vim.startswith(modeline, ';') then + break + end + local langlist = modeline:match(MODELINE_FORMAT) if langlist then for _, incllang in ipairs(vim.split(langlist, ',', true)) do local is_optional = incllang:match('%(.*%)') @@ -74,16 +90,25 @@ function M.get_query_files(lang, query_name, is_included) table.insert(base_langs, incllang) end end + elseif modeline:match(EXTENDS_FORMAT) then + extension = true end end + + if extension then + table.insert(extensions, filename) + else + base_query = filename + end + io.close(file) end - local query_files = {} + local query_files = { base_query } for _, base_lang in ipairs(base_langs) do local base_files = M.get_query_files(base_lang, query_name, true) vim.list_extend(query_files, base_files) end - vim.list_extend(query_files, lang_files) + vim.list_extend(query_files, extensions) return query_files end -- cgit From 893b659e88c61a8c3ce5b140ab475cd67e0ca6bc Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 8 Sep 2022 11:17:29 +0200 Subject: fix(treesitter): use the right loading order for base queries (#20117) Use the first, not last, query for a language on runtimepath. Typically, this implies that a user query will override a site plugin query, which will override a bundled runtime query. --- 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 be5b24fd95..2f6227af8e 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -97,7 +97,7 @@ function M.get_query_files(lang, query_name, is_included) if extension then table.insert(extensions, filename) - else + elseif base_query == nil then base_query = filename end io.close(file) -- cgit From 11167ab6d569994dd0a4f58155c84b118706380c Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 8 Sep 2022 11:33:04 +0200 Subject: feat(lsp): add range option to lsp.buf.format (#19998) --- runtime/lua/vim/lsp/buf.lua | 82 ++++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 24 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6a070928d9..2ce565e1d9 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -150,6 +150,33 @@ local function select_client(method, on_choice) end end +---@private +---@return table {start={row, col}, end={row, col}} using (1, 0) indexing +local function range_from_selection() + -- TODO: Use `vim.region()` instead https://github.com/neovim/neovim/pull/13896 + + -- [bufnum, lnum, col, off]; both row and column 1-indexed + local start = vim.fn.getpos('v') + local end_ = vim.fn.getpos('.') + local start_row = start[2] + local start_col = start[3] + local end_row = end_[2] + local end_col = end_[3] + + -- 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 + elseif end_row < start_row then + start_row, end_row = end_row, start_row + start_col, end_col = end_col, start_col + end + return { + ['start'] = { start_row, start_col - 1 }, + ['end'] = { end_row, end_col - 1 }, + } +end + --- Formats a buffer using the attached (and optionally filtered) language --- server clients. --- @@ -184,7 +211,12 @@ end --- Restrict formatting to the client with ID (client.id) matching this field. --- - name (string|nil): --- Restrict formatting to the client with name (client.name) matching this field. - +--- +--- - range (table|nil) Range to format. +--- Table must contain `start` and `end` keys with {row, col} tuples using +--- (1,0) indexing. +--- Defaults to current selection in visual mode +--- Defaults to `nil` in other modes, formatting the full buffer function M.format(options) options = options or {} local bufnr = options.bufnr or api.nvim_get_current_buf() @@ -206,16 +238,32 @@ function M.format(options) vim.notify('[LSP] Format request failed, no matching language servers.') end + local mode = api.nvim_get_mode().mode + local range = options.range + if not range and mode == 'v' or mode == 'V' then + range = range_from_selection() + end + + ---@private + local function set_range(client, params) + if range then + local range_params = + util.make_given_range_params(range.start, range['end'], bufnr, client.offset_encoding) + params.range = range_params.range + end + return params + end + + local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' if options.async then local do_format do_format = function(idx, client) if not client then return end - local params = util.make_formatting_params(options.formatting_options) - client.request('textDocument/formatting', params, function(...) - local handler = client.handlers['textDocument/formatting'] - or vim.lsp.handlers['textDocument/formatting'] + local params = set_range(client, util.make_formatting_params(options.formatting_options)) + client.request(method, params, function(...) + local handler = client.handlers[method] or vim.lsp.handlers[method] handler(...) do_format(next(clients, idx)) end, bufnr) @@ -224,8 +272,8 @@ function M.format(options) else local timeout_ms = options.timeout_ms or 1000 for _, client in pairs(clients) do - local params = util.make_formatting_params(options.formatting_options) - local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr) + local params = set_range(client, util.make_formatting_params(options.formatting_options)) + 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 @@ -356,6 +404,7 @@ end ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_formatting(options, start_pos, end_pos) + vim.deprecate('vim.lsp.buf.range_formatting', 'vim.lsp.formatexpr or vim.lsp.format', '0.9.0') local params = util.make_given_range_params(start_pos, end_pos) params.options = util.make_formatting_params(options).options select_client('textDocument/rangeFormatting', function(client) @@ -885,23 +934,8 @@ function M.code_action(options) local end_ = assert(options.range['end'], 'range must have a `end` property') params = util.make_given_range_params(start, end_) elseif mode == 'v' or mode == 'V' then - -- [bufnum, lnum, col, off]; both row and column 1-indexed - local start = vim.fn.getpos('v') - local end_ = vim.fn.getpos('.') - local start_row = start[2] - local start_col = start[3] - local end_row = end_[2] - local end_col = end_[3] - - -- 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 - elseif end_row < start_row then - start_row, end_row = end_row, start_row - start_col, end_col = end_col, start_col - end - params = util.make_given_range_params({ start_row, start_col - 1 }, { end_row, end_col - 1 }) + local range = range_from_selection() + params = util.make_given_range_params(range.start, range['end']) else params = util.make_range_params() end -- cgit From 08602ec1ab2674385ddda8feaef2d3d9360d834d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 8 Sep 2022 16:06:00 +0200 Subject: vim-patch:9.0.0417: Jsonnet files are not recognized (#20119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Jsonnet files are not recognized. Solution: Add a pattern for Jsonnet files. (Cezary Drożak, closes vim/vim#11073, closes vim/vim#11081) https://github.com/vim/vim/commit/2a4c885d54171f68ec2c2d6eb4ae281c7fefb802 --- 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 f76b4cced9..fc2bcdabd2 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -557,6 +557,8 @@ local extension = { ['json-patch'] = 'json', json5 = 'json5', jsonc = 'jsonc', + jsonnet = 'jsonnet', + libjsonnet = 'jsonnet', jsp = 'jsp', jl = 'julia', kv = 'kivy', -- cgit From bc88691dbd2f5352d8793cac952bf90eea6442b2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 6 Sep 2022 22:51:32 +0100 Subject: refactor(vim.opt): remove del arg --- runtime/lua/vim/_meta.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 0f45c916dc..5dbadd3ff4 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -43,7 +43,7 @@ local function _setup() win_options = get_scoped_options('win') end -local function make_meta_accessor(get, set, del, validator) +local function make_meta_accessor(get, set, validator) validator = validator or function() return true end @@ -51,7 +51,6 @@ local function make_meta_accessor(get, set, del, validator) validate({ get = { get, 'f' }, set = { set, 'f' }, - del = { del, 'f', true }, validator = { validator, 'f' }, }) @@ -61,9 +60,6 @@ local function make_meta_accessor(get, set, del, validator) return end - if del and v == nil then - return del(k) - end return set(k, v) end function mt:__index(k) @@ -98,7 +94,7 @@ do -- buffer option accessor return a.nvim_set_option_value(k, v, { buf = bufnr or 0 }) end - return make_meta_accessor(get, set, nil, function(k) + return make_meta_accessor(get, set, function(k) if type(k) == 'string' then _setup() if win_options[k] then @@ -132,7 +128,7 @@ do -- window option accessor return a.nvim_set_option_value(k, v, { win = winnr or 0 }) end - return make_meta_accessor(get, set, nil, function(k) + return make_meta_accessor(get, set, function(k) if type(k) == 'string' then _setup() if buf_options[k] then -- cgit From f21e2a51ba43a06d929ca372bf5cfce40d4e4331 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 6 Sep 2022 22:56:55 +0100 Subject: refactor(vim.opt): remove enums --- runtime/lua/vim/_meta.lua | 124 +++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 79 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 5dbadd3ff4..fb3a1e72e9 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -4,12 +4,6 @@ local vim = assert(vim) local a = vim.api local validate = vim.validate -local SET_TYPES = setmetatable({ - SET = 0, - LOCAL = 1, - GLOBAL = 2, -}, { __index = error }) - local options_info = nil local buf_options = nil local glb_options = nil @@ -201,51 +195,30 @@ local key_value_options = { winhl = true, } ----@class OptionTypes ---- Option Type Enum -local OptionTypes = setmetatable({ - BOOLEAN = 0, - NUMBER = 1, - STRING = 2, - ARRAY = 3, - MAP = 4, - SET = 5, -}, { - __index = function(_, k) - error('Not a valid OptionType: ' .. k) - end, - __newindex = function(_, k) - error('Cannot set a new OptionType: ' .. k) - end, -}) - --- Convert a vimoption_T style dictionary to the correct OptionType associated with it. ----@return OptionType +---@return string local get_option_type = function(name, info) if info.type == 'boolean' then - return OptionTypes.BOOLEAN + return 'boolean' elseif info.type == 'number' then - return OptionTypes.NUMBER + return 'number' elseif info.type == 'string' then if not info.commalist and not info.flaglist then - return OptionTypes.STRING + return 'string' end if key_value_options[name] then assert(info.commalist, 'Must be a comma list to use key:value style') - return OptionTypes.MAP + return 'map' end if info.flaglist then - return OptionTypes.SET + return 'set' elseif info.commalist then - return OptionTypes.ARRAY + return 'array' end - - error('Fallthrough in OptionTypes') - else - error('Not a known info.type:' .. info.type) end + error('Not a known info.type:' .. info.type) end -- Check whether the OptionTypes is allowed for vim.opt @@ -269,12 +242,12 @@ local function assert_valid_value(name, value, types) end local valid_types = { - [OptionTypes.BOOLEAN] = { 'boolean' }, - [OptionTypes.NUMBER] = { 'number' }, - [OptionTypes.STRING] = { 'string' }, - [OptionTypes.SET] = { 'string', 'table' }, - [OptionTypes.ARRAY] = { 'string', 'table' }, - [OptionTypes.MAP] = { 'string', 'table' }, + boolean = { 'boolean' }, + number = { 'number' }, + string = { 'string' }, + set = { 'string', 'table' }, + array = { 'string', 'table' }, + map = { 'string', 'table' }, } --- Convert a lua value to a vimoption_T value @@ -282,17 +255,17 @@ local convert_value_to_vim = (function() -- Map of functions to take a Lua style value and convert to vimoption_T style value. -- Each function takes (info, lua_value) -> vim_value local to_vim_value = { - [OptionTypes.BOOLEAN] = function(_, value) + boolean = function(_, value) return value end, - [OptionTypes.NUMBER] = function(_, value) + number = function(_, value) return value end, - [OptionTypes.STRING] = function(_, value) + string = function(_, value) return value end, - [OptionTypes.SET] = function(info, value) + set = function(info, value) if type(value) == 'string' then return value end @@ -319,7 +292,7 @@ local convert_value_to_vim = (function() end end, - [OptionTypes.ARRAY] = function(info, value) + array = function(info, value) if type(value) == 'string' then return value end @@ -329,7 +302,7 @@ local convert_value_to_vim = (function() return table.concat(value, ',') end, - [OptionTypes.MAP] = function(_, value) + map = function(_, value) if type(value) == 'string' then return value end @@ -361,17 +334,17 @@ local convert_value_to_lua = (function() -- Map of OptionType to functions that take vimoption_T values and convert to lua values. -- Each function takes (info, vim_value) -> lua_value local to_lua_value = { - [OptionTypes.BOOLEAN] = function(_, value) + boolean = function(_, value) return value end, - [OptionTypes.NUMBER] = function(_, value) + number = function(_, value) return value end, - [OptionTypes.STRING] = function(_, value) + string = function(_, value) return value end, - [OptionTypes.ARRAY] = function(info, value) + array = function(info, value) if type(value) == 'table' then if not info.allows_duplicates then value = remove_duplicate_values(value) @@ -420,7 +393,7 @@ local convert_value_to_lua = (function() return vim.split(value, ',') end, - [OptionTypes.SET] = function(info, value) + set = function(info, value) if type(value) == 'table' then return value end @@ -451,7 +424,7 @@ local convert_value_to_lua = (function() end end, - [OptionTypes.MAP] = function(info, raw_value) + map = function(info, raw_value) if type(raw_value) == 'table' then return raw_value end @@ -485,15 +458,15 @@ end --- Handles the '^' operator local prepend_value = (function() local methods = { - [OptionTypes.NUMBER] = function() + number = function() error("The '^' operator is not currently supported for") end, - [OptionTypes.STRING] = function(left, right) + string = function(left, right) return right .. left end, - [OptionTypes.ARRAY] = function(left, right) + array = function(left, right) for i = #right, 1, -1 do table.insert(left, 1, right[i]) end @@ -501,11 +474,11 @@ local prepend_value = (function() return left end, - [OptionTypes.MAP] = function(left, right) + map = function(left, right) return vim.tbl_extend('force', left, right) end, - [OptionTypes.SET] = function(left, right) + set = function(left, right) return vim.tbl_extend('force', left, right) end, } @@ -524,15 +497,15 @@ end)() --- Handles the '+' operator local add_value = (function() local methods = { - [OptionTypes.NUMBER] = function(left, right) + number = function(left, right) return left + right end, - [OptionTypes.STRING] = function(left, right) + string = function(left, right) return left .. right end, - [OptionTypes.ARRAY] = function(left, right) + array = function(left, right) for _, v in ipairs(right) do table.insert(left, v) end @@ -540,11 +513,11 @@ local add_value = (function() return left end, - [OptionTypes.MAP] = function(left, right) + map = function(left, right) return vim.tbl_extend('force', left, right) end, - [OptionTypes.SET] = function(left, right) + set = function(left, right) return vim.tbl_extend('force', left, right) end, } @@ -580,15 +553,15 @@ local remove_value = (function() end local methods = { - [OptionTypes.NUMBER] = function(left, right) + number = function(left, right) return left - right end, - [OptionTypes.STRING] = function() + string = function() error('Subtraction not supported for strings.') end, - [OptionTypes.ARRAY] = function(left, right) + array = function(left, right) if type(right) == 'string' then remove_one_item(left, right) else @@ -600,7 +573,7 @@ local remove_value = (function() return left end, - [OptionTypes.MAP] = function(left, right) + map = function(left, right) if type(right) == 'string' then left[right] = nil else @@ -612,7 +585,7 @@ local remove_value = (function() return left end, - [OptionTypes.SET] = function(left, right) + set = function(left, right) if type(right) == 'string' then left[right] = nil else @@ -630,7 +603,7 @@ local remove_value = (function() end end)() -local create_option_metatable = function(set_type) +local create_option_metatable = function(scope) local set_mt, option_mt local make_option = function(name, value) @@ -650,13 +623,6 @@ local create_option_metatable = function(set_type) }, option_mt) end - local scope - if set_type == SET_TYPES.GLOBAL then - scope = 'global' - elseif set_type == SET_TYPES.LOCAL then - scope = 'local' - end - option_mt = { -- To set a value, instead use: -- opt[my_option] = value @@ -711,6 +677,6 @@ local create_option_metatable = function(set_type) return set_mt end -vim.opt = setmetatable({}, create_option_metatable(SET_TYPES.SET)) -vim.opt_local = setmetatable({}, create_option_metatable(SET_TYPES.LOCAL)) -vim.opt_global = setmetatable({}, create_option_metatable(SET_TYPES.GLOBAL)) +vim.opt = setmetatable({}, create_option_metatable()) +vim.opt_local = setmetatable({}, create_option_metatable('local')) +vim.opt_global = setmetatable({}, create_option_metatable('global')) -- cgit From b8de5ada80f7cc2cda3dc55defeb635ee39f3a24 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 15:42:17 +0100 Subject: refactor(vim.opt): replace _setup with lazy table --- runtime/lua/vim/_meta.lua | 154 +++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 91 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index fb3a1e72e9..9e10e5ccf5 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -4,39 +4,52 @@ local vim = assert(vim) local a = vim.api local validate = vim.validate -local options_info = nil -local buf_options = nil -local glb_options = nil -local win_options = nil - -local function _setup() - if options_info ~= nil then - return - end - options_info = {} - for _, v in pairs(a.nvim_get_all_options_info()) do - options_info[v.name] = v - if v.shortname ~= '' then - options_info[v.shortname] = v +-- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded. +-- Can be done in a separate PR. +local key_value_options = { + fillchars = true, + fcs = true, + listchars = true, + lcs = true, + winhighlight = true, + winhl = true, +} + +--- Convert a vimoption_T style dictionary to the correct OptionType associated with it. +---@return string +local function get_option_metatype(name, info) + if info.type == 'boolean' then + return 'boolean' + elseif info.type == 'number' then + return 'number' + elseif info.type == 'string' then + if not info.commalist and not info.flaglist then + return 'string' end - end - local function get_scoped_options(scope) - local result = {} - for name, option_info in pairs(options_info) do - if option_info.scope == scope then - result[name] = true - end + if key_value_options[name] then + assert(info.commalist, 'Must be a comma list to use key:value style') + return 'map' end - return result + if info.flaglist then + return 'set' + elseif info.commalist then + return 'array' + end end - - buf_options = get_scoped_options('buf') - glb_options = get_scoped_options('global') - win_options = get_scoped_options('win') + error('Not a known info.type:' .. info.type) end +local options_info = setmetatable({}, { + __index = function(t, k) + local info = a.nvim_get_option_info(k) + info.metatype = get_option_metatype(k, info) + rawset(t, k, info) + return rawget(t, k) + end, +}) + local function make_meta_accessor(get, set, validator) validator = validator or function() return true @@ -90,12 +103,12 @@ do -- buffer option accessor return make_meta_accessor(get, set, function(k) if type(k) == 'string' then - _setup() - if win_options[k] then + local scope = options_info[k].scope + if scope == 'win' then error( string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k) ) - elseif glb_options[k] then + elseif scope == 'global' then error( string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k) ) @@ -124,12 +137,12 @@ do -- window option accessor return make_meta_accessor(get, set, function(k) if type(k) == 'string' then - _setup() - if buf_options[k] then + local scope = options_info[k].scope + if scope == 'buf' then error( string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k) ) - elseif glb_options[k] then + elseif scope == 'global' then error( string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k) ) @@ -184,43 +197,6 @@ local remove_duplicate_values = function(t) return result end --- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded. --- Can be done in a separate PR. -local key_value_options = { - fillchars = true, - fcs = true, - listchars = true, - lcs = true, - winhighlight = true, - winhl = true, -} - ---- Convert a vimoption_T style dictionary to the correct OptionType associated with it. ----@return string -local get_option_type = function(name, info) - if info.type == 'boolean' then - return 'boolean' - elseif info.type == 'number' then - return 'number' - elseif info.type == 'string' then - if not info.commalist and not info.flaglist then - return 'string' - end - - if key_value_options[name] then - assert(info.commalist, 'Must be a comma list to use key:value style') - return 'map' - end - - if info.flaglist then - return 'set' - elseif info.commalist then - return 'array' - end - end - error('Not a known info.type:' .. info.type) -end - -- Check whether the OptionTypes is allowed for vim.opt -- If it does not match, throw an error which indicates which option causes the error. local function assert_valid_value(name, value, types) @@ -322,10 +298,9 @@ local convert_value_to_vim = (function() return vim.NIL end - local option_type = get_option_type(name, info) - assert_valid_value(name, value, valid_types[option_type]) + assert_valid_value(name, value, valid_types[info.metatype]) - return to_vim_value[option_type](info, value) + return to_vim_value[info.metatype](info, value) end end)() @@ -445,14 +420,14 @@ local convert_value_to_lua = (function() end, } - return function(name, info, option_value) - return to_lua_value[get_option_type(name, info)](info, option_value) + return function(info, option_value) + return to_lua_value[info.metatype](info, option_value) end end)() --- Handles the mutation of various different values. -local value_mutator = function(name, info, current, new, mutator) - return mutator[get_option_type(name, info)](current, new) +local value_mutator = function(info, current, new, mutator) + return mutator[info.metatype](current, new) end --- Handles the '^' operator @@ -483,12 +458,11 @@ local prepend_value = (function() end, } - return function(name, info, current, new) + return function(info, current, new) return value_mutator( - name, info, - convert_value_to_lua(name, info, current), - convert_value_to_lua(name, info, new), + convert_value_to_lua(info, current), + convert_value_to_lua(info, new), methods ) end @@ -522,12 +496,11 @@ local add_value = (function() end, } - return function(name, info, current, new) + return function(info, current, new) return value_mutator( - name, info, - convert_value_to_lua(name, info, current), - convert_value_to_lua(name, info, new), + convert_value_to_lua(info, current), + convert_value_to_lua(info, new), methods ) end @@ -598,8 +571,8 @@ local remove_value = (function() end, } - return function(name, info, current, new) - return value_mutator(name, info, convert_value_to_lua(name, info, current), new, methods) + return function(info, current, new) + return value_mutator(info, convert_value_to_lua(info, current), new, methods) end end)() @@ -607,7 +580,6 @@ local create_option_metatable = function(scope) local set_mt, option_mt local make_option = function(name, value) - _setup() local info = assert(options_info[name], 'Not a valid option name: ' .. name) if type(value) == 'table' and getmetatable(value) == option_mt then @@ -634,7 +606,7 @@ local create_option_metatable = function(scope) end, get = function(self) - return convert_value_to_lua(self._name, self._info, self._value) + return convert_value_to_lua(self._info, self._value) end, append = function(self, right) @@ -642,7 +614,7 @@ local create_option_metatable = function(scope) end, __add = function(self, right) - return make_option(self._name, add_value(self._name, self._info, self._value, right)) + return make_option(self._name, add_value(self._info, self._value, right)) end, prepend = function(self, right) @@ -650,7 +622,7 @@ local create_option_metatable = function(scope) end, __pow = function(self, right) - return make_option(self._name, prepend_value(self._name, self._info, self._value, right)) + return make_option(self._name, prepend_value(self._info, self._value, right)) end, remove = function(self, right) @@ -658,7 +630,7 @@ local create_option_metatable = function(scope) end, __sub = function(self, right) - return make_option(self._name, remove_value(self._name, self._info, self._value, right)) + return make_option(self._name, remove_value(self._info, self._value, right)) end, } option_mt.__index = option_mt -- cgit From 514a1679dcd7444c7a1b66612b3313fb249d310a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 15:44:00 +0100 Subject: refactor(vim.opt): simplify get_option_metatype --- runtime/lua/vim/_meta.lua | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 9e10e5ccf5..66b55a3d47 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -18,27 +18,18 @@ local key_value_options = { --- Convert a vimoption_T style dictionary to the correct OptionType associated with it. ---@return string local function get_option_metatype(name, info) - if info.type == 'boolean' then - return 'boolean' - elseif info.type == 'number' then - return 'number' - elseif info.type == 'string' then - if not info.commalist and not info.flaglist then - return 'string' - end - - if key_value_options[name] then - assert(info.commalist, 'Must be a comma list to use key:value style') - return 'map' - end - + if info.type == 'string' then if info.flaglist then return 'set' elseif info.commalist then + if key_value_options[name] then + return 'map' + end return 'array' end + return 'string' end - error('Not a known info.type:' .. info.type) + return info.type end local options_info = setmetatable({}, { -- cgit From 30ca6d23a9c77175a76a4cd59da81de83d9253af Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 8 Sep 2022 23:09:32 +0800 Subject: fix(lsp): when buffer detach remove buffer from client attached buffers (#20081) Co-authored-by: Mathias Fussenegger --- runtime/lua/vim/lsp.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1dc1a045fd..051127c9c6 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1644,6 +1644,7 @@ function lsp.buf_attach_client(bufnr, client_id) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) end + client.attached_buffers[bufnr] = nil end) util.buf_versions[bufnr] = nil all_buffer_active_clients[bufnr] = nil -- cgit From 164752b38074e9d71493c304dc3b8c672aac397d Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 15:52:39 +0100 Subject: refactor(vim.opt): remove make_meta_accessor() --- runtime/lua/vim/_meta.lua | 171 +++++++++++++++++++++------------------------- 1 file changed, 77 insertions(+), 94 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 66b55a3d47..93077c89d6 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -2,7 +2,6 @@ local vim = assert(vim) local a = vim.api -local validate = vim.validate -- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded. -- Can be done in a separate PR. @@ -41,107 +40,85 @@ local options_info = setmetatable({}, { end, }) -local function make_meta_accessor(get, set, validator) - validator = validator or function() - return true - end - - validate({ - get = { get, 'f' }, - set = { set, 'f' }, - validator = { validator, 'f' }, - }) - - local mt = {} - function mt:__newindex(k, v) - if not validator(k) then - return - end - - return set(k, v) - end - function mt:__index(k) - if not validator(k) then - return +vim.env = setmetatable({}, { + __index = function(_, k) + local v = vim.fn.getenv(k) + if v == vim.NIL then + return nil end + return v + end, - return get(k) - end - return setmetatable({}, mt) -end + __newindex = function(_, k, v) + vim.fn.setenv(k, v) + end, +}) -vim.env = make_meta_accessor(function(k) - local v = vim.fn.getenv(k) - if v == vim.NIL then - return nil +do -- buffer option accessor + local function buf_opt_validate(k) + local scope = options_info[k].scope + if scope == 'win' then + error( + string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k) + ) + elseif scope == 'global' then + error( + string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k) + ) + end end - return v -end, vim.fn.setenv) -do -- buffer option accessor local function new_buf_opt_accessor(bufnr) - local function get(k) - if bufnr == nil and type(k) == 'number' then - return new_buf_opt_accessor(k) - end - - return a.nvim_get_option_value(k, { buf = bufnr or 0 }) - end + return setmetatable({},{ + __index = function(_, k) + if bufnr == nil and type(k) == 'number' then + return new_buf_opt_accessor(k) + end + buf_opt_validate(k) - local function set(k, v) - return a.nvim_set_option_value(k, v, { buf = bufnr or 0 }) - end + return a.nvim_get_option_value(k, { buf = bufnr or 0 }) + end, - return make_meta_accessor(get, set, function(k) - if type(k) == 'string' then - local scope = options_info[k].scope - if scope == 'win' then - error( - string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k) - ) - elseif scope == 'global' then - error( - string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k) - ) - end - end + __newindex = function(_, k, v) + buf_opt_validate(k) + return a.nvim_set_option_value(k, v, { buf = bufnr or 0 }) + end, + }) - return true - end) end vim.bo = new_buf_opt_accessor(nil) end do -- window option accessor - local function new_win_opt_accessor(winnr) - local function get(k) - if winnr == nil and type(k) == 'number' then - return new_win_opt_accessor(k) - end - return a.nvim_get_option_value(k, { win = winnr or 0 }) - end - - local function set(k, v) - return a.nvim_set_option_value(k, v, { win = winnr or 0 }) + local function win_opt_validate(k) + local scope = options_info[k].scope + if scope == 'buf' then + error( + string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k) + ) + elseif scope == 'global' then + error( + string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k) + ) end + end - return make_meta_accessor(get, set, function(k) - if type(k) == 'string' then - local scope = options_info[k].scope - if scope == 'buf' then - error( - string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k) - ) - elseif scope == 'global' then - error( - string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k) - ) + local function new_win_opt_accessor(winnr) + return setmetatable({}, { + __index = function(_, k) + if winnr == nil and type(k) == 'number' then + return new_win_opt_accessor(k) end - end - - return true - end) + win_opt_validate(k) + return a.nvim_get_option_value(k, { win = winnr or 0 }) + end, + + __newindex = function(_, k, v) + win_opt_validate(k) + return a.nvim_set_option_value(k, v, { win = winnr or 0 }) + end, + }) end vim.wo = new_win_opt_accessor(nil) @@ -149,19 +126,25 @@ end -- vim global option -- this ONLY sets the global option. like `setglobal` -vim.go = make_meta_accessor(function(k) - return a.nvim_get_option_value(k, { scope = 'global' }) -end, function(k, v) - return a.nvim_set_option_value(k, v, { scope = 'global' }) -end) +vim.go = setmetatable({}, { + __index = function(_, k) + return a.nvim_get_option_value(k, { scope = 'global' }) + end, + __newindex = function(_, k, v) + return a.nvim_set_option_value(k, v, { scope = 'global' }) + end, +}) -- vim `set` style options. -- it has no additional metamethod magic. -vim.o = make_meta_accessor(function(k) - return a.nvim_get_option_value(k, {}) -end, function(k, v) - return a.nvim_set_option_value(k, v, {}) -end) +vim.o = setmetatable({}, { + __index = function(_, k) + return a.nvim_get_option_value(k, {}) + end, + __newindex = function(_, k, v) + return a.nvim_set_option_value(k, v, {}) + end, +}) ---@brief [[ --- vim.opt, vim.opt_local and vim.opt_global implementation -- cgit From 19a3b2c26e28382a65529a38be9ff63ca58cc023 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 8 Sep 2022 19:25:16 +0200 Subject: docs(lsp): update rpc.start stdio limitations (#20120) --- runtime/lua/vim/lsp/rpc.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 70f838f34d..7d047f8958 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -635,7 +635,8 @@ local function connect(host, port) end --- Starts an LSP server process and create an LSP RPC client object to ---- interact with it. Communication with the server is currently limited to stdio. +--- interact with it. Communication with the spawned process happens via stdio. For +--- communication via TCP, spawn a process manually and use |vim.lsp.rpc.connect| --- ---@param cmd (string) Command to start the LSP server. ---@param cmd_args (table) List of additional string arguments to pass to {cmd}. -- cgit From 7533ceec13a1dd9a1e46a523975bddf52f533a93 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 15:56:35 +0100 Subject: refactor(vim.opt): unify vim.bo/wo building --- runtime/lua/vim/_meta.lua | 92 +++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 63 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 93077c89d6..f78c2da581 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -54,76 +54,42 @@ vim.env = setmetatable({}, { end, }) -do -- buffer option accessor - local function buf_opt_validate(k) - local scope = options_info[k].scope - if scope == 'win' then - error( - string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k) +local function opt_validate(option_name, target_scope) + local scope = options_info[option_name].scope + if scope ~= target_scope then + local scope_to_string = { buf = 'buffer', win = 'window' } + error( + string.format( + [['%s' is a %s option, not a %s option. See ":help %s"]], + option_name, + scope_to_string[scope] or scope, + scope_to_string[target_scope] or target_scope, + option_name ) - elseif scope == 'global' then - error( - string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k) - ) - end - end - - local function new_buf_opt_accessor(bufnr) - return setmetatable({},{ - __index = function(_, k) - if bufnr == nil and type(k) == 'number' then - return new_buf_opt_accessor(k) - end - buf_opt_validate(k) - - return a.nvim_get_option_value(k, { buf = bufnr or 0 }) - end, - - __newindex = function(_, k, v) - buf_opt_validate(k) - return a.nvim_set_option_value(k, v, { buf = bufnr or 0 }) - end, - }) - + ) end - - vim.bo = new_buf_opt_accessor(nil) end -do -- window option accessor - local function win_opt_validate(k) - local scope = options_info[k].scope - if scope == 'buf' then - error( - string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k) - ) - elseif scope == 'global' then - error( - string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k) - ) - end - end - - local function new_win_opt_accessor(winnr) - return setmetatable({}, { - __index = function(_, k) - if winnr == nil and type(k) == 'number' then - return new_win_opt_accessor(k) - end - win_opt_validate(k) - return a.nvim_get_option_value(k, { win = winnr or 0 }) - end, - - __newindex = function(_, k, v) - win_opt_validate(k) - return a.nvim_set_option_value(k, v, { win = winnr or 0 }) - end, - }) - end +local function new_opt_accessor(handle, scope) + return setmetatable({}, { + __index = function(_, k) + if handle == nil and type(k) == 'number' then + return new_opt_accessor(k, scope) + end + opt_validate(k, scope) + return a.nvim_get_option_value(k, { [scope] = handle or 0 }) + end, - vim.wo = new_win_opt_accessor(nil) + __newindex = function(_, k, v) + opt_validate(k, scope) + return a.nvim_set_option_value(k, v, { [scope] = handle or 0 }) + end, + }) end +vim.bo = new_opt_accessor(nil, 'buf') +vim.wo = new_opt_accessor(nil, 'win') + -- vim global option -- this ONLY sets the global option. like `setglobal` vim.go = setmetatable({}, { -- cgit From 925a8119902ebe63343374f9edc57b4ea6038d08 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 16:44:18 +0100 Subject: refactor(vim.opt): remove value_mutator() --- runtime/lua/vim/_meta.lua | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index f78c2da581..9daef780f4 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -365,11 +365,6 @@ local convert_value_to_lua = (function() end end)() ---- Handles the mutation of various different values. -local value_mutator = function(info, current, new, mutator) - return mutator[info.metatype](current, new) -end - --- Handles the '^' operator local prepend_value = (function() local methods = { @@ -399,11 +394,9 @@ local prepend_value = (function() } return function(info, current, new) - return value_mutator( - info, + methods[info.metatype]( convert_value_to_lua(info, current), - convert_value_to_lua(info, new), - methods + convert_value_to_lua(info, new) ) end end)() @@ -437,11 +430,9 @@ local add_value = (function() } return function(info, current, new) - return value_mutator( - info, + methods[info.metatype]( convert_value_to_lua(info, current), - convert_value_to_lua(info, new), - methods + convert_value_to_lua(info, new) ) end end)() @@ -512,7 +503,7 @@ local remove_value = (function() } return function(info, current, new) - return value_mutator(info, convert_value_to_lua(info, current), new, methods) + return methods[info.metatype](convert_value_to_lua(info, current), new) end end)() -- cgit From b364bc2c340e4d56ca34960bbd73e2774e7f235c Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 16:54:34 +0100 Subject: refactor(vim.opt): dry up and tidy --- runtime/lua/vim/_meta.lua | 117 +++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 75 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 9daef780f4..cb02174e71 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -123,9 +123,6 @@ vim.o = setmetatable({}, { --- Preserves the order and does not mutate the original list local remove_duplicate_values = function(t) local result, seen = {}, {} - if type(t) == 'function' then - error(debug.traceback('asdf')) - end for _, v in ipairs(t) do if not seen[v] then table.insert(result, v) @@ -157,6 +154,26 @@ local function assert_valid_value(name, value, types) ) end +local function passthrough(_, x) + return x +end + +local function tbl_merge(left, right) + return vim.tbl_extend('force', left, right) +end + +local function tbl_remove(t, value) + if type(value) == 'string' then + t[value] = nil + else + for _, v in ipairs(value) do + t[v] = nil + end + end + + return t +end + local valid_types = { boolean = { 'boolean' }, number = { 'number' }, @@ -171,15 +188,9 @@ local convert_value_to_vim = (function() -- Map of functions to take a Lua style value and convert to vimoption_T style value. -- Each function takes (info, lua_value) -> vim_value local to_vim_value = { - boolean = function(_, value) - return value - end, - number = function(_, value) - return value - end, - string = function(_, value) - return value - end, + boolean = passthrough, + number = passthrough, + string = passthrough, set = function(info, value) if type(value) == 'string' then @@ -249,15 +260,9 @@ local convert_value_to_lua = (function() -- Map of OptionType to functions that take vimoption_T values and convert to lua values. -- Each function takes (info, vim_value) -> lua_value local to_lua_value = { - boolean = function(_, value) - return value - end, - number = function(_, value) - return value - end, - string = function(_, value) - return value - end, + boolean = passthrough, + number = passthrough, + string = passthrough, array = function(info, value) if type(value) == 'table' then @@ -276,9 +281,7 @@ local convert_value_to_lua = (function() -- Handles unescaped commas in a list. if string.find(value, ',,,') then - local comma_split = vim.split(value, ',,,') - local left = comma_split[1] - local right = comma_split[2] + local left, right = unpack(vim.split(value, ',,,')) local result = {} vim.list_extend(result, vim.split(left, ',')) @@ -291,9 +294,7 @@ local convert_value_to_lua = (function() end if string.find(value, ',^,,', 1, true) then - local comma_split = vim.split(value, ',^,,', true) - local left = comma_split[1] - local right = comma_split[2] + local left, right = unpack(vim.split(value, ',^,,', true)) local result = {} vim.list_extend(result, vim.split(left, ',')) @@ -384,13 +385,8 @@ local prepend_value = (function() return left end, - map = function(left, right) - return vim.tbl_extend('force', left, right) - end, - - set = function(left, right) - return vim.tbl_extend('force', left, right) - end, + map = tbl_merge, + set = tbl_merge, } return function(info, current, new) @@ -420,13 +416,8 @@ local add_value = (function() return left end, - map = function(left, right) - return vim.tbl_extend('force', left, right) - end, - - set = function(left, right) - return vim.tbl_extend('force', left, right) - end, + map = tbl_merge, + set = tbl_merge, } return function(info, current, new) @@ -477,29 +468,8 @@ local remove_value = (function() return left end, - map = function(left, right) - if type(right) == 'string' then - left[right] = nil - else - for _, v in ipairs(right) do - left[v] = nil - end - end - - return left - end, - - set = function(left, right) - if type(right) == 'string' then - left[right] = nil - else - for _, v in ipairs(right) do - left[v] = nil - end - end - - return left - end, + map = tbl_remove, + set = tbl_remove, } return function(info, current, new) @@ -507,8 +477,8 @@ local remove_value = (function() end end)() -local create_option_metatable = function(scope) - local set_mt, option_mt +local create_option_accessor = function(scope) + local option_mt local make_option = function(name, value) local info = assert(options_info[name], 'Not a valid option name: ' .. name) @@ -566,20 +536,17 @@ local create_option_metatable = function(scope) } option_mt.__index = option_mt - set_mt = { + return setmetatable({}, { __index = function(_, k) return make_option(k, a.nvim_get_option_value(k, { scope = scope })) end, __newindex = function(_, k, v) - local opt = make_option(k, v) - opt:_set() + make_option(k, v):_set() end, - } - - return set_mt + }) end -vim.opt = setmetatable({}, create_option_metatable()) -vim.opt_local = setmetatable({}, create_option_metatable('local')) -vim.opt_global = setmetatable({}, create_option_metatable('global')) +vim.opt = create_option_accessor() +vim.opt_local = create_option_accessor('local') +vim.opt_global = create_option_accessor('global') -- cgit From ad972990ad7ee59f0be7d267b58ca880c9ccaa7b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 16:57:58 +0100 Subject: refactor(vim.opt): optimize append/prepend/remove --- runtime/lua/vim/_meta.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index cb02174e71..7ec132fbb5 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -502,8 +502,6 @@ local create_option_accessor = function(scope) _set = function(self) local value = convert_value_to_vim(self._name, self._info, self._value) a.nvim_set_option_value(self._name, value, { scope = scope }) - - return self end, get = function(self) @@ -511,7 +509,8 @@ local create_option_accessor = function(scope) end, append = function(self, right) - return self:__add(right):_set() + self._value = add_value(self._info, self._value, right) + self:_set() end, __add = function(self, right) @@ -519,7 +518,8 @@ local create_option_accessor = function(scope) end, prepend = function(self, right) - return self:__pow(right):_set() + self._value = prepend_value(self._info, self._value, right) + self:_set() end, __pow = function(self, right) @@ -527,7 +527,8 @@ local create_option_accessor = function(scope) end, remove = function(self, right) - return self:__sub(right):_set() + self._value = remove_value(self._info, self._value, right) + self:_set() end, __sub = function(self, right) -- cgit From 9272d20ea48ea0beb1ecefc0331344a1a3e4b05e Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 16:59:09 +0100 Subject: refactor(vim.opt): use local function syntax --- runtime/lua/vim/_meta.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 7ec132fbb5..fc5df0116d 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -121,7 +121,7 @@ vim.o = setmetatable({}, { ---@brief ]] --- Preserves the order and does not mutate the original list -local remove_duplicate_values = function(t) +local function remove_duplicate_values(t) local result, seen = {}, {} for _, v in ipairs(t) do if not seen[v] then @@ -430,7 +430,7 @@ end)() --- Handles the '-' operator local remove_value = (function() - local remove_one_item = function(t, val) + local function remove_one_item(t, val) if vim.tbl_islist(t) then local remove_index = nil for i, v in ipairs(t) do @@ -477,10 +477,10 @@ local remove_value = (function() end end)() -local create_option_accessor = function(scope) +local function create_option_accessor(scope) local option_mt - local make_option = function(name, value) + local function make_option(name, value) local info = assert(options_info[name], 'Not a valid option name: ' .. name) if type(value) == 'table' and getmetatable(value) == option_mt then -- cgit From 038c7115393c931b29c451eb1434d283c74ba55e Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 8 Sep 2022 17:09:44 +0100 Subject: refactor(vim.opt): de-nest code --- runtime/lua/vim/_meta.lua | 446 ++++++++++++++++++++++------------------------ 1 file changed, 218 insertions(+), 228 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index fc5df0116d..dada918d69 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -183,299 +183,289 @@ local valid_types = { map = { 'string', 'table' }, } ---- Convert a lua value to a vimoption_T value -local convert_value_to_vim = (function() - -- Map of functions to take a Lua style value and convert to vimoption_T style value. - -- Each function takes (info, lua_value) -> vim_value - local to_vim_value = { - boolean = passthrough, - number = passthrough, - string = passthrough, - - set = function(info, value) - if type(value) == 'string' then - return value - end - - if info.flaglist and info.commalist then - local keys = {} - for k, v in pairs(value) do - if v then - table.insert(keys, k) - end - end +-- Map of functions to take a Lua style value and convert to vimoption_T style value. +-- Each function takes (info, lua_value) -> vim_value +local to_vim_value = { + boolean = passthrough, + number = passthrough, + string = passthrough, + + set = function(info, value) + if type(value) == 'string' then + return value + end - table.sort(keys) - return table.concat(keys, ',') - else - local result = '' - for k, v in pairs(value) do - if v then - result = result .. k - end + if info.flaglist and info.commalist then + local keys = {} + for k, v in pairs(value) do + if v then + table.insert(keys, k) end - - return result end - end, - array = function(info, value) - if type(value) == 'string' then - return value - end - if not info.allows_duplicates then - value = remove_duplicate_values(value) + table.sort(keys) + return table.concat(keys, ',') + else + local result = '' + for k, v in pairs(value) do + if v then + result = result .. k + end end - return table.concat(value, ',') - end, - map = function(_, value) - if type(value) == 'string' then - return value - end + return result + end + end, - local result = {} - for opt_key, opt_value in pairs(value) do - table.insert(result, string.format('%s:%s', opt_key, opt_value)) - end + array = function(info, value) + if type(value) == 'string' then + return value + end + if not info.allows_duplicates then + value = remove_duplicate_values(value) + end + return table.concat(value, ',') + end, - table.sort(result) - return table.concat(result, ',') - end, - } + map = function(_, value) + if type(value) == 'string' then + return value + end - return function(name, info, value) - if value == nil then - return vim.NIL + local result = {} + for opt_key, opt_value in pairs(value) do + table.insert(result, string.format('%s:%s', opt_key, opt_value)) end - assert_valid_value(name, value, valid_types[info.metatype]) + table.sort(result) + return table.concat(result, ',') + end, +} - return to_vim_value[info.metatype](info, value) +--- Convert a lua value to a vimoption_T value +local function convert_value_to_vim(name, info, value) + if value == nil then + return vim.NIL end -end)() ---- Converts a vimoption_T style value to a Lua value -local convert_value_to_lua = (function() - -- Map of OptionType to functions that take vimoption_T values and convert to lua values. - -- Each function takes (info, vim_value) -> lua_value - local to_lua_value = { - boolean = passthrough, - number = passthrough, - string = passthrough, - - array = function(info, value) - if type(value) == 'table' then - if not info.allows_duplicates then - value = remove_duplicate_values(value) - end + assert_valid_value(name, value, valid_types[info.metatype]) - return value - end + return to_vim_value[info.metatype](info, value) +end + +-- Map of OptionType to functions that take vimoption_T values and convert to lua values. +-- Each function takes (info, vim_value) -> lua_value +local to_lua_value = { + boolean = passthrough, + number = passthrough, + string = passthrough, - -- Empty strings mean that there is nothing there, - -- so empty table should be returned. - if value == '' then - return {} + array = function(info, value) + if type(value) == 'table' then + if not info.allows_duplicates then + value = remove_duplicate_values(value) end - -- Handles unescaped commas in a list. - if string.find(value, ',,,') then - local left, right = unpack(vim.split(value, ',,,')) + return value + end - local result = {} - vim.list_extend(result, vim.split(left, ',')) - table.insert(result, ',') - vim.list_extend(result, vim.split(right, ',')) + -- Empty strings mean that there is nothing there, + -- so empty table should be returned. + if value == '' then + return {} + end - table.sort(result) + -- Handles unescaped commas in a list. + if string.find(value, ',,,') then + local left, right = unpack(vim.split(value, ',,,')) - return result - end + local result = {} + vim.list_extend(result, vim.split(left, ',')) + table.insert(result, ',') + vim.list_extend(result, vim.split(right, ',')) - if string.find(value, ',^,,', 1, true) then - local left, right = unpack(vim.split(value, ',^,,', true)) + table.sort(result) - local result = {} - vim.list_extend(result, vim.split(left, ',')) - table.insert(result, '^,') - vim.list_extend(result, vim.split(right, ',')) + return result + end - table.sort(result) + if string.find(value, ',^,,', 1, true) then + local left, right = unpack(vim.split(value, ',^,,', true)) - return result - end + local result = {} + vim.list_extend(result, vim.split(left, ',')) + table.insert(result, '^,') + vim.list_extend(result, vim.split(right, ',')) - return vim.split(value, ',') - end, + table.sort(result) - set = function(info, value) - if type(value) == 'table' then - return value - end + return result + end - -- Empty strings mean that there is nothing there, - -- so empty table should be returned. - if value == '' then - return {} - end + return vim.split(value, ',') + end, - assert(info.flaglist, 'That is the only one I know how to handle') + set = function(info, value) + if type(value) == 'table' then + return value + end - if info.flaglist and info.commalist then - local split_value = vim.split(value, ',') - local result = {} - for _, v in ipairs(split_value) do - result[v] = true - end + -- Empty strings mean that there is nothing there, + -- so empty table should be returned. + if value == '' then + return {} + end - return result - else - local result = {} - for i = 1, #value do - result[value:sub(i, i)] = true - end + assert(info.flaglist, 'That is the only one I know how to handle') - return result + if info.flaglist and info.commalist then + local split_value = vim.split(value, ',') + local result = {} + for _, v in ipairs(split_value) do + result[v] = true end - end, - map = function(info, raw_value) - if type(raw_value) == 'table' then - return raw_value + return result + else + local result = {} + for i = 1, #value do + result[value:sub(i, i)] = true end - assert(info.commalist, 'Only commas are supported currently') + return result + end + end, - local result = {} + map = function(info, raw_value) + if type(raw_value) == 'table' then + return raw_value + end - local comma_split = vim.split(raw_value, ',') - for _, key_value_str in ipairs(comma_split) do - local key, value = unpack(vim.split(key_value_str, ':')) - key = vim.trim(key) + assert(info.commalist, 'Only commas are supported currently') - result[key] = value - end + local result = {} - return result - end, - } + local comma_split = vim.split(raw_value, ',') + for _, key_value_str in ipairs(comma_split) do + local key, value = unpack(vim.split(key_value_str, ':')) + key = vim.trim(key) - return function(info, option_value) - return to_lua_value[info.metatype](info, option_value) - end -end)() + result[key] = value + end ---- Handles the '^' operator -local prepend_value = (function() - local methods = { - number = function() - error("The '^' operator is not currently supported for") - end, + return result + end, +} - string = function(left, right) - return right .. left - end, +--- Converts a vimoption_T style value to a Lua value +local function convert_value_to_lua(info, option_value) + return to_lua_value[info.metatype](info, option_value) +end - array = function(left, right) - for i = #right, 1, -1 do - table.insert(left, 1, right[i]) - end +local prepend_methods = { + number = function() + error("The '^' operator is not currently supported for") + end, - return left - end, + string = function(left, right) + return right .. left + end, - map = tbl_merge, - set = tbl_merge, - } + array = function(left, right) + for i = #right, 1, -1 do + table.insert(left, 1, right[i]) + end - return function(info, current, new) - methods[info.metatype]( - convert_value_to_lua(info, current), - convert_value_to_lua(info, new) - ) - end -end)() + return left + end, ---- Handles the '+' operator -local add_value = (function() - local methods = { - number = function(left, right) - return left + right - end, + map = tbl_merge, + set = tbl_merge, +} - string = function(left, right) - return left .. right - end, +--- Handles the '^' operator +local function prepend_value(info, current, new) + return prepend_methods[info.metatype]( + convert_value_to_lua(info, current), + convert_value_to_lua(info, new) + ) +end - array = function(left, right) - for _, v in ipairs(right) do - table.insert(left, v) - end +local add_methods = { + number = function(left, right) + return left + right + end, - return left - end, + string = function(left, right) + return left .. right + end, - map = tbl_merge, - set = tbl_merge, - } + array = function(left, right) + for _, v in ipairs(right) do + table.insert(left, v) + end - return function(info, current, new) - methods[info.metatype]( - convert_value_to_lua(info, current), - convert_value_to_lua(info, new) - ) - end -end)() + return left + end, ---- Handles the '-' operator -local remove_value = (function() - local function remove_one_item(t, val) - if vim.tbl_islist(t) then - local remove_index = nil - for i, v in ipairs(t) do - if v == val then - remove_index = i - end - end + map = tbl_merge, + set = tbl_merge, +} - if remove_index then - table.remove(t, remove_index) +--- Handles the '+' operator +local function add_value(info, current, new) + return add_methods[info.metatype]( + convert_value_to_lua(info, current), + convert_value_to_lua(info, new) + ) +end + +local function remove_one_item(t, val) + if vim.tbl_islist(t) then + local remove_index = nil + for i, v in ipairs(t) do + if v == val then + remove_index = i end - else - t[val] = nil end + + if remove_index then + table.remove(t, remove_index) + end + else + t[val] = nil end +end - local methods = { - number = function(left, right) - return left - right - end, +local remove_methods = { + number = function(left, right) + return left - right + end, - string = function() - error('Subtraction not supported for strings.') - end, + string = function() + error('Subtraction not supported for strings.') + end, - array = function(left, right) - if type(right) == 'string' then - remove_one_item(left, right) - else - for _, v in ipairs(right) do - remove_one_item(left, v) - end + array = function(left, right) + if type(right) == 'string' then + remove_one_item(left, right) + else + for _, v in ipairs(right) do + remove_one_item(left, v) end + end - return left - end, + return left + end, - map = tbl_remove, - set = tbl_remove, - } + map = tbl_remove, + set = tbl_remove, +} - return function(info, current, new) - return methods[info.metatype](convert_value_to_lua(info, current), new) - end -end)() +--- Handles the '-' operator +local function remove_value(info, current, new) + return remove_methods[info.metatype](convert_value_to_lua(info, current), new) +end local function create_option_accessor(scope) local option_mt -- cgit From ad2d6a624b10a52cdcfb7fdd9d8b1be24b13ed83 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 9 Sep 2022 17:53:15 +0200 Subject: vim-patch:9.0.0424: gitattributes files are not recognized (#20134) Problem: gitattributes files are not recognized. Solution: Add patterns to match gitattributes files. (closes vim/vim#11085) https://github.com/vim/vim/commit/7d56cfc861e57145f003315efd835cf5dfd5b145 --- runtime/lua/vim/filetype.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index fc2bcdabd2..12e6fa837b 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1383,6 +1383,7 @@ local filename = { ['EDIT_DESCRIPTION'] = 'gitcommit', ['.gitconfig'] = 'gitconfig', ['.gitmodules'] = 'gitconfig', + ['.gitattributes'] = 'gitattributes', ['gitolite.conf'] = 'gitolite', ['git-rebase-todo'] = 'gitrebase', gkrellmrc = 'gkrellmrc', @@ -1825,6 +1826,14 @@ local pattern = { return 'gitconfig' end end, + ['.*%.git/info/attributes'] = 'gitattributes', + ['.*/etc/gitattributes'] = 'gitattributes', + ['.*/%.config/git/attributes'] = 'gitattributes', + ['.*/git/attributes'] = function(path, bufnr) + if vim.env.XDG_CONFIG_HOME and path:find(vim.env.XDG_CONFIG_HOME .. '/git/attributes') then + return 'gitattributes' + end + end, ['%.gitsendemail%.msg%.......'] = 'gitsendemail', ['gkrellmrc_.'] = 'gkrellmrc', ['.*/usr/.*/gnupg/options%.skel'] = 'gpg', -- cgit From 9b0e1256e25d387bf65cb9baa1edd99fbc128724 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 9 Sep 2022 18:48:12 +0200 Subject: vim-patch:9.0.0427: Drupal theme files are not recognized (#20138) Problem: Drupal theme files are not recognized. Solution: Use php filetype for Drupl theme files. Remove trailing spaces. (Rodrigo Aguilera, closes vim/vim#11096) https://github.com/vim/vim/commit/8995c4cd4e697141faf74da9a87e0c1221bfb161 --- 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 12e6fa837b..6aa765ebaf 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -746,6 +746,7 @@ local extension = { php = 'php', phpt = 'php', phtml = 'php', + theme = 'php', pike = 'pike', pmod = 'pike', rcp = 'pilrc', -- cgit From 40f9f479b746d0f76fbdd4bc0567d593ca7a6070 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 10 Sep 2022 13:30:54 +0200 Subject: vim-patch:9.0.0434: gitignore files are not recognized (#20143) Problem: gitignore files are not recognized. Solution: Add patterns for the gitignore filetype. (closes vim/vim#11102) https://github.com/vim/vim/commit/9ba2786f15f0b53a90fd221832a5bedfc6dbfe20 --- runtime/lua/vim/filetype.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 6aa765ebaf..c8777801ec 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1385,6 +1385,7 @@ local filename = { ['.gitconfig'] = 'gitconfig', ['.gitmodules'] = 'gitconfig', ['.gitattributes'] = 'gitattributes', + ['.gitignore'] = 'gitignore', ['gitolite.conf'] = 'gitolite', ['git-rebase-todo'] = 'gitrebase', gkrellmrc = 'gkrellmrc', @@ -1835,6 +1836,13 @@ local pattern = { return 'gitattributes' end end, + ['.*%.git/info/exclude'] = 'gitignore', + ['.*/%.config/git/ignore'] = 'gitignore', + ['.*/git/ignore'] = function(path, bufnr) + if vim.env.XDG_CONFIG_HOME and path:find(vim.env.XDG_CONFIG_HOME .. '/git/ignore') then + return 'gitignore' + end + end, ['%.gitsendemail%.msg%.......'] = 'gitsendemail', ['gkrellmrc_.'] = 'gkrellmrc', ['.*/usr/.*/gnupg/options%.skel'] = 'gpg', -- cgit From 9b4cab012662514af6fda3648d544633e1d73d4b Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sat, 10 Sep 2022 18:56:29 -0600 Subject: fix(lsp): schedule removal of client object (#20148) The execution of the LspDetach autocommands in the LSP client's on_exit function are scheduled on the event loop to avoid making API calls in a fast context; however, this means that by the time the LspDetach autocommands finally run the client object has already been deleted. To address this, we also schedule the deletion of the client on the event loop so that it is guaranteed to occur after all of the LspDetach autocommands have fired. --- runtime/lua/vim/lsp.lua | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 051127c9c6..22933d8143 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1147,33 +1147,34 @@ function lsp.start_client(config) local namespace = vim.lsp.diagnostic.get_namespace(client_id) vim.diagnostic.reset(namespace, bufnr) - end) - client_ids[client_id] = nil - end - if vim.tbl_isempty(client_ids) then - vim.schedule(function() - unset_defaults(bufnr) + client_ids[client_id] = nil + if vim.tbl_isempty(client_ids) then + unset_defaults(bufnr) + end end) end end - local client = active_clients[client_id] and active_clients[client_id] - or uninitialized_clients[client_id] - active_clients[client_id] = nil - uninitialized_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', client_id, code, signal) - vim.schedule(function() + -- Schedule the deletion of the client object so that it exists in the execution of LspDetach + -- autocommands + vim.schedule(function() + local client = active_clients[client_id] and active_clients[client_id] + or uninitialized_clients[client_id] + active_clients[client_id] = nil + uninitialized_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', client_id, code, signal) vim.notify(msg, vim.log.levels.WARN) - end) - end + end + end) end -- Start the RPC client. -- cgit From 1939518ebab72878f2a8ca0cb85c09f7e70d1093 Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Sat, 10 Sep 2022 11:26:23 +0200 Subject: fix(treesitter): prevent endless loop on self-inheritence Fixes #20139 --- runtime/lua/vim/treesitter/query.lua | 20 ++++++++++++++++++-- 1 file changed, 18 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 2f6227af8e..90ed2a357c 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -34,6 +34,18 @@ local function safe_read(filename, read_quantifier) return content end +---@private +--- Adds @p ilang to @p base_langs, only if @p ilang is different than @lang +--- +---@return boolean true it lang == ilang +local function add_included_lang(base_langs, lang, ilang) + if lang == ilang then + return true + end + table.insert(base_langs, ilang) + return false +end + --- Gets the list of files used to make up a query --- ---@param lang The language @@ -84,10 +96,14 @@ function M.get_query_files(lang, query_name, is_included) if is_optional then if not is_included then - table.insert(base_langs, incllang:sub(2, #incllang - 1)) + if add_included_lang(base_langs, lang, incllang:sub(2, #incllang - 1)) then + extension = true + end end else - table.insert(base_langs, incllang) + if add_included_lang(base_langs, lang, incllang) then + extension = true + end end end elseif modeline:match(EXTENDS_FORMAT) then -- cgit From f98cff9575e75a050d2bde01ad950c0c72bcfc3e Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 11 Sep 2022 16:07:54 +0200 Subject: vim-patch:9.0.0443: blueprint files are not recognized (#20155) Problem: Blueprint files are not recognized. Solution: Add a pattern for blueprint files. (Gabriele Musco, closes vim/vim#11107) https://github.com/vim/vim/commit/cce82a55b8105560a2ef724999c856966337b48e --- 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 c8777801ec..e4be1f04a2 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -177,6 +177,7 @@ local extension = { bbappend = 'bitbake', bbclass = 'bitbake', bl = 'blank', + blp = 'blueprint', bsd = 'bsdl', bsdl = 'bsdl', bst = 'bst', -- cgit From afe01842ef37620e63cab815b84c89454a6b4a87 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 12 Sep 2022 15:12:39 +0200 Subject: vim-patch:9.0.0448: SubRip files are not recognized (#20167) Problem: SubRip files are not recognized. Solution: Add a pattern for SubRip. (closes vim/vim#11113) https://github.com/vim/vim/commit/5a4eb55122e45444d3a6c56ce108ce29bc8e52ab --- 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 e4be1f04a2..39985c948e 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -959,6 +959,7 @@ local extension = { srec = 'srec', mot = 'srec', ['s19'] = 'srec', + srt = 'srt', st = 'st', imata = 'stata', ['do'] = 'stata', -- cgit From 1970d2ac43059639e4e2c83223d86397b38786ff Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 13 Sep 2022 08:33:39 -0600 Subject: feat(diagnostic): pass diagnostics as data to DiagnosticChanged autocmd (#20173) --- 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 4f7d8cccd5..172bd867c7 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -727,6 +727,7 @@ function M.set(namespace, bufnr, diagnostics, opts) vim.api.nvim_exec_autocmds('DiagnosticChanged', { modeline = false, buffer = bufnr, + data = { diagnostics = diagnostics }, }) end -- cgit From a8c9e721d91efe4730db78c1115261fc128dca68 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Tue, 13 Sep 2022 22:16:20 +0200 Subject: feat(fs): extend fs.find to accept predicate (#20193) Makes it possible to use `vim.fs.find` to find files where only a substring is known. This is useful for `vim.lsp.start` to get the `root_dir` for languages where the project-file is only known by its extension, not by the full name. For example in .NET projects there is usually a `.csproj` file in the project root. Example: vim.fs.find(function(x) return vim.endswith(x, '.csproj') end, { upward = true }) --- runtime/lua/vim/fs.lua | 56 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index ce845eda15..7bd635d8b6 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -76,8 +76,11 @@ end --- The search can be narrowed to find only files or or only directories by --- specifying {type} to be "file" or "directory", respectively. --- ----@param names (string|table) Names of the files and directories to find. Must ---- be base names, paths and globs are not supported. +---@param names (string|table|fun(name: string): boolean) Names of the files +--- and directories to find. +--- Must be base names, paths and globs are not supported. +--- If a function it is called per file and dir within the +--- traversed directories to test if they match. ---@param opts (table) Optional keyword arguments: --- - path (string): Path to begin searching from. If --- omitted, the current working directory is used. @@ -98,7 +101,7 @@ end function M.find(names, opts) opts = opts or {} vim.validate({ - names = { names, { 's', 't' } }, + names = { names, { 's', 't', 'f' } }, path = { opts.path, 's', true }, upward = { opts.upward, 'b', true }, stop = { opts.stop, 's', true }, @@ -123,18 +126,31 @@ function M.find(names, opts) end if opts.upward then - ---@private - local function test(p) - local t = {} - for _, name in ipairs(names) do - local f = p .. '/' .. name - local stat = vim.loop.fs_stat(f) - if stat and (not opts.type or opts.type == stat.type) then - t[#t + 1] = f + local test + + if type(names) == 'function' then + test = function(p) + local t = {} + for name, type in M.dir(p) do + if names(name) and (not opts.type or opts.type == type) then + table.insert(t, p .. '/' .. name) + end end + return t end + else + test = function(p) + local t = {} + for _, name in ipairs(names) do + local f = p .. '/' .. name + local stat = vim.loop.fs_stat(f) + if stat and (not opts.type or opts.type == stat.type) then + t[#t + 1] = f + end + end - return t + return t + end end for _, match in ipairs(test(path)) do @@ -162,17 +178,25 @@ function M.find(names, opts) break end - for other, type in M.dir(dir) do + for other, type_ in M.dir(dir) do local f = dir .. '/' .. other - for _, name in ipairs(names) do - if name == other and (not opts.type or opts.type == type) then + if type(names) == 'function' then + if names(other) and (not opts.type or opts.type == type_) then if add(f) then return matches end end + else + for _, name in ipairs(names) do + if name == other and (not opts.type or opts.type == type_) then + if add(f) then + return matches + end + end + end end - if type == 'directory' then + if type_ == 'directory' then dirs[#dirs + 1] = f end end -- cgit From ddb762f4013ac2532ad45704466058d867e3a6ed Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 14 Sep 2022 11:08:31 +0200 Subject: docs(treesitter): clean up and update treesitter.txt (#20142) * add type annotations to code * clean up and expand static documentation * consistent use of tags for static and generated docs --- runtime/lua/vim/shared.lua | 2 +- runtime/lua/vim/treesitter.lua | 85 ++++++++++---------- runtime/lua/vim/treesitter/health.lua | 2 +- runtime/lua/vim/treesitter/highlighter.lua | 14 ++-- runtime/lua/vim/treesitter/language.lua | 16 ++-- runtime/lua/vim/treesitter/languagetree.lua | 115 +++++++++++++++++----------- runtime/lua/vim/treesitter/query.lua | 102 ++++++++++++------------ 7 files changed, 187 insertions(+), 149 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 59cb669609..de5f7240aa 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -720,7 +720,7 @@ end --- --- They mimic defaultdict in python. --- ---- If @p create is @c nil, this will create a defaulttable whose constructor function is +--- If {create} is `nil`, this will create a defaulttable whose constructor function is --- this function, effectively allowing to create nested tables on the fly: --- ---
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index d93c485dfe..89aa611acd 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -25,15 +25,15 @@ setmetatable(M, {
   end,
 })
 
---- Creates a new parser.
+--- Creates a new parser
 ---
---- It is not recommended to use this, use vim.treesitter.get_parser() instead.
+--- It is not recommended to use this; use |get_parser()| instead.
 ---
 ---@param bufnr string Buffer the parser will be tied to (0 for current buffer)
 ---@param lang string Language of the parser
----@param opts table|nil Options to pass to the created language tree
+---@param opts (table|nil) Options to pass to the created language tree
 ---
----@returns table Created parser object
+---@return LanguageTree |LanguageTree| object to use for parsing
 function M._create_parser(bufnr, lang, opts)
   language.require_language(lang)
   if bufnr == 0 then
@@ -73,16 +73,15 @@ function M._create_parser(bufnr, lang, opts)
   return self
 end
 
---- Gets the parser for this bufnr / ft combination.
+--- Returns the parser for a specific buffer and filetype and attaches it to the buffer
 ---
---- If needed this will create the parser.
---- Unconditionally attach the provided callback
+--- If needed, this will create the parser.
 ---
----@param bufnr number|nil Buffer the parser should be tied to (default: current buffer)
----@param lang string |nil Filetype of this parser (default: buffer filetype)
----@param opts table|nil Options to pass to the created language tree
+---@param bufnr (number|nil) Buffer the parser should be tied to (default: current buffer)
+---@param lang (string|nil) Filetype of this parser (default: buffer filetype)
+---@param opts (table|nil) Options to pass to the created language tree
 ---
----@returns table Parser object
+---@return LanguageTree |LanguageTree| object to use for parsing
 function M.get_parser(bufnr, lang, opts)
   opts = opts or {}
 
@@ -102,11 +101,13 @@ function M.get_parser(bufnr, lang, opts)
   return parsers[bufnr]
 end
 
---- Gets a string parser
+--- Returns a string parser
 ---
----@param str The string to parse
----@param lang The language of this string
----@param opts Options to pass to the created language tree
+---@param str string Text to parse
+---@param lang string Language of this string
+---@param opts (table|nil) Options to pass to the created language tree
+---
+---@return LanguageTree |LanguageTree| object to use for parsing
 function M.get_string_parser(str, lang, opts)
   vim.validate({
     str = { str, 'string' },
@@ -119,10 +120,10 @@ end
 
 --- Determines whether a node is the ancestor of another
 ---
----@param dest table Possible ancestor
----@param source table Possible descendant node
+---@param dest userdata Possible ancestor |tsnode|
+---@param source userdata Possible descendant |tsnode|
 ---
----@returns (boolean) True if dest is an ancestor of source
+---@return boolean True if {dest} is an ancestor of {source}
 function M.is_ancestor(dest, source)
   if not (dest and source) then
     return false
@@ -140,11 +141,11 @@ function M.is_ancestor(dest, source)
   return false
 end
 
---- Get the node's range or unpack a range table
+--- Returns the node's range or an unpacked range table
 ---
----@param node_or_range table
+---@param node_or_range (userdata|table) |tsnode| or table of positions
 ---
----@returns table start_row, start_col, end_row, end_col
+---@return table `{ start_row, start_col, end_row, end_col }`
 function M.get_node_range(node_or_range)
   if type(node_or_range) == 'table' then
     return unpack(node_or_range)
@@ -155,11 +156,11 @@ end
 
 ---Determines whether (line, col) position is in node range
 ---
----@param node table Node defining the range
+---@param node userdata |tsnode| defining the range
 ---@param line number Line (0-based)
 ---@param col number Column (0-based)
 ---
----@returns (boolean) True if the position is in node range
+---@return boolean True if the position is in node range
 function M.is_in_node_range(node, line, col)
   local start_line, start_col, end_line, end_col = M.get_node_range(node)
   if line >= start_line and line <= end_line then
@@ -178,10 +179,10 @@ function M.is_in_node_range(node, line, col)
 end
 
 ---Determines if a node contains a range
----@param node table
+---@param node userdata |tsnode|
 ---@param range table
 ---
----@returns (boolean) True if the node contains the range
+---@return boolean True if the {node} contains the {range}
 function M.node_contains(node, range)
   local start_row, start_col, end_row, end_col = node:range()
   local start_fits = start_row < range[1] or (start_row == range[1] and start_col <= range[2])
@@ -190,7 +191,8 @@ function M.node_contains(node, range)
   return start_fits and end_fits
 end
 
----Gets a list of captures for a given cursor position
+---Returns a list of highlight captures at the given position
+--
 ---@param bufnr number Buffer number (0 for current buffer)
 ---@param row number Position row
 ---@param col number Position column
@@ -199,7 +201,7 @@ end
 ---@param row number Position row
 ---@param col number Position column
 ---
----@returns (table) Table of captures
+---@return table[] Captures of the form `{ capture = "capture name", priority = capture priority }`
 function M.get_captures_at_position(bufnr, row, col)
   if bufnr == 0 then
     bufnr = a.nvim_get_current_buf()
@@ -246,11 +248,11 @@ function M.get_captures_at_position(bufnr, row, col)
   return matches
 end
 
----Gets a list of captures under the cursor
+---Returns a list of highlight capture names under the cursor
 ---
----@param winnr number|nil Window handle or 0 for current window (default)
+---@param winnr (number|nil) Window handle or 0 for current window (default)
 ---
----@returns (table) Named node under the cursor
+---@return string[] List of capture names
 function M.get_captures_at_cursor(winnr)
   winnr = winnr or 0
   local bufnr = a.nvim_win_get_buf(winnr)
@@ -267,7 +269,7 @@ function M.get_captures_at_cursor(winnr)
   return captures
 end
 
---- Gets the smallest named node at position
+--- Returns the smallest named node at the given position
 ---
 ---@param bufnr number Buffer number (0 for current buffer)
 ---@param row number Position row
@@ -275,7 +277,7 @@ end
 ---@param opts table Optional keyword arguments:
 ---             - ignore_injections boolean Ignore injected languages (default true)
 ---
----@returns (table) Named node under the cursor
+---@return userdata |tsnode| under the cursor
 function M.get_node_at_position(bufnr, row, col, opts)
   if bufnr == 0 then
     bufnr = a.nvim_get_current_buf()
@@ -290,11 +292,11 @@ function M.get_node_at_position(bufnr, row, col, opts)
   return root_lang_tree:named_node_for_range(ts_range, opts)
 end
 
---- Gets the smallest named node under the cursor
+--- Returns the smallest named node under the cursor
 ---
----@param winnr number|nil Window handle or 0 for current window (default)
+---@param winnr (number|nil) Window handle or 0 for current window (default)
 ---
----@returns (string) Named node under the cursor
+---@return string Name of node under the cursor
 function M.get_node_at_cursor(winnr)
   winnr = winnr or 0
   local bufnr = a.nvim_win_get_buf(winnr)
@@ -304,15 +306,14 @@ function M.get_node_at_cursor(winnr)
     :type()
 end
 
---- Start treesitter highlighting for a buffer
+--- Starts treesitter highlighting for a buffer
 ---
---- Can be used in an ftplugin or FileType autocommand
+--- Can be used in an ftplugin or FileType autocommand.
 ---
 --- 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`.
 ---
 --- Example:
----
 --- 
 --- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
 ---     callback = function(args)
@@ -322,8 +323,8 @@ end
 --- })
 --- 
--- ----@param bufnr number|nil Buffer to be highlighted (default: current buffer) ----@param lang string|nil Language of the parser (default: buffer filetype) +---@param bufnr (number|nil) Buffer to be highlighted (default: current buffer) +---@param lang (string|nil) Language of the parser (default: buffer filetype) function M.start(bufnr, lang) bufnr = bufnr or a.nvim_get_current_buf() @@ -334,9 +335,9 @@ function M.start(bufnr, lang) vim.b[bufnr].ts_highlight = true end ----Stop treesitter highlighting for a buffer +--- Stops treesitter highlighting for a buffer --- ----@param bufnr number|nil Buffer to stop highlighting (default: current buffer) +---@param bufnr (number|nil) Buffer to stop highlighting (default: current buffer) function M.stop(bufnr) bufnr = bufnr or a.nvim_get_current_buf() diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua index 3bd59ca282..4995c80a02 100644 --- a/runtime/lua/vim/treesitter/health.lua +++ b/runtime/lua/vim/treesitter/health.lua @@ -3,7 +3,7 @@ local ts = vim.treesitter --- Lists the parsers currently installed --- ----@return A list of parsers +---@return string[] list of parser files function M.list_parsers() return vim.api.nvim_get_runtime_file('parser/*', true) end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 1e625eddb8..83a26aff13 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -2,6 +2,7 @@ local a = vim.api local query = require('vim.treesitter.query') -- support reload for quick experimentation +---@class TSHighlighter local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {} TSHighlighter.__index = TSHighlighter @@ -45,9 +46,10 @@ end --- Creates a new highlighter using @param tree --- ----@param tree The language tree to use for highlighting ----@param opts Table used to configure the highlighter ---- - queries: Table to overwrite queries used by the highlighter +---@param tree LanguageTree |LanguageTree| parser object to use for highlighting +---@param opts (table|nil) Configuration of the highlighter: +--- - queries table overwrite queries used by the highlighter +---@return TSHighlighter Created highlighter object function TSHighlighter.new(tree, opts) local self = setmetatable({}, TSHighlighter) @@ -149,8 +151,10 @@ function TSHighlighter:on_changedtree(changes) end --- Gets the query used for @param lang ---- ----@param lang A language used by the highlighter. +-- +---@private +---@param lang string Language used by the highlighter. +---@return Query function TSHighlighter:get_query(lang) if not self._queries[lang] then self._queries[lang] = TSHighlighterQuery.new(lang) diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index d14b825603..c92d63b8c4 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -2,14 +2,15 @@ local a = vim.api local M = {} ---- Asserts that the provided language is installed, and optionally provide a path for the parser +--- Asserts that a parser for the language {lang} is installed. --- ---- Parsers are searched in the `parser` runtime directory. +--- Parsers are searched in the `parser` runtime directory, or the provided {path} --- ----@param lang string The language the parser should parse ----@param path string|nil Optional path the parser is located at ----@param silent boolean|nil Don't throw an error if language not found ----@param symbol_name string|nil Internal symbol name for the language to load +---@param lang string Language the parser should parse +---@param path (string|nil) Optional path the parser is located at +---@param silent (boolean|nil) Don't throw an error if language not found +---@param symbol_name (string|nil) Internal symbol name for the language to load +---@return boolean If the specified language is installed function M.require_language(lang, path, silent, symbol_name) if vim._ts_has_language(lang) then return true @@ -42,7 +43,8 @@ end --- --- Inspecting provides some useful information on the language like node names, ... --- ----@param lang The language. +---@param lang string Language +---@return table function M.inspect_language(lang) M.require_language(lang) return vim._ts_inspect_language(lang) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 70317a9f94..e9d70c4204 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -2,19 +2,35 @@ local a = vim.api local query = require('vim.treesitter.query') local language = require('vim.treesitter.language') +---@class LanguageTree +---@field _callbacks function[] Callback handlers +---@field _children LanguageTree[] Injected languages +---@field _injection_query table Queries defining injected languages +---@field _opts table Options +---@field _parser userdata Parser for language +---@field _regions table List of regions this tree should manage and parse +---@field _lang string Language name +---@field _regions table +---@field _source (number|string) Buffer or string to parse +---@field _trees userdata[] Reference to parsed |tstree| (one for each language) +---@field _valid boolean If the parsed tree is valid + local LanguageTree = {} LanguageTree.__index = LanguageTree ---- Represents a single treesitter parser for a language. ---- The language can contain child languages with in its range, ---- hence the tree. +--- A |LanguageTree| holds the treesitter parser for a given language {lang} used +--- to parse a buffer. As the buffer may contain injected languages, the LanguageTree +--- needs to store parsers for these child languages as well (which in turn may contain +--- child languages themselves, hence the name). --- ----@param source Can be a bufnr or a string of text to parse ----@param lang The language this tree represents ----@param opts Options table ----@param opts.injections A table of language to injection query strings. ---- This is useful for overriding the built-in runtime file ---- searching for the injection language query per language. +---@param source (number|string) Buffer or a string of text to parse +---@param lang string Root language this tree represents +---@param opts (table|nil) Optional keyword arguments: +--- - injections table Mapping language to injection query strings. +--- This is useful for overriding the built-in +--- runtime file searching for the injection language +--- query per language. +---@return LanguageTree |LanguageTree| parser object function LanguageTree.new(source, lang, opts) language.require_language(lang) opts = opts or {} @@ -94,6 +110,9 @@ end --- for the language this tree represents. --- This will run the injection query for this language to --- determine if any child languages should be created. +--- +---@return userdata[] Table of parsed |tstree| +---@return table Change list function LanguageTree:parse() if self._valid then return self._trees @@ -167,10 +186,10 @@ function LanguageTree:parse() return self._trees, changes end ---- Invokes the callback for each LanguageTree and it's children recursively +--- Invokes the callback for each |LanguageTree| and its children recursively --- ----@param fn The function to invoke. This is invoked with arguments (tree: LanguageTree, lang: string) ----@param include_self Whether to include the invoking tree in the results. +---@param fn function(tree: LanguageTree, lang: string) +---@param include_self boolean Whether to include the invoking tree in the results function LanguageTree:for_each_child(fn, include_self) if include_self then fn(self, self._lang) @@ -181,12 +200,11 @@ function LanguageTree:for_each_child(fn, include_self) end end ---- Invokes the callback for each treesitter trees recursively. +--- Invokes the callback for each |LanguageTree| recursively. --- ---- Note, this includes the invoking language tree's trees as well. +--- Note: This includes the invoking tree's child trees as well. --- ----@param fn The callback to invoke. The callback is invoked with arguments ---- (tree: TSTree, languageTree: LanguageTree) +---@param fn function(tree: TSTree, languageTree: LanguageTree) function LanguageTree:for_each_tree(fn) for _, tree in ipairs(self._trees) do fn(tree, self) @@ -197,11 +215,13 @@ function LanguageTree:for_each_tree(fn) end end ---- Adds a child language to this tree. +--- Adds a child language to this |LanguageTree|. --- --- If the language already exists as a child, it will first be removed. --- ----@param lang The language to add. +---@private +---@param lang string Language to add. +---@return LanguageTree Injected |LanguageTree| function LanguageTree:add_child(lang) if self._children[lang] then self:remove_child(lang) @@ -215,9 +235,10 @@ function LanguageTree:add_child(lang) return self._children[lang] end ---- Removes a child language from this tree. +--- Removes a child language from this |LanguageTree|. --- ----@param lang The language to remove. +---@private +---@param lang string Language to remove. function LanguageTree:remove_child(lang) local child = self._children[lang] @@ -229,12 +250,11 @@ function LanguageTree:remove_child(lang) end end ---- Destroys this language tree and all its children. +--- Destroys this |LanguageTree| and all its children. --- --- Any cleanup logic should be performed here. --- ---- Note: ---- This DOES NOT remove this tree from a parent. Instead, +--- Note: This DOES NOT remove this tree from a parent. Instead, --- `remove_child` must be called on the parent to remove it. function LanguageTree:destroy() -- Cleanup here @@ -243,23 +263,24 @@ function LanguageTree:destroy() end end ---- Sets the included regions that should be parsed by this parser. +--- Sets the included regions that should be parsed by this |LanguageTree|. --- A region is a set of nodes and/or ranges that will be parsed in the same context. --- ---- For example, `{ { node1 }, { node2} }` is two separate regions. ---- This will be parsed by the parser in two different contexts... thus resulting +--- For example, `{ { node1 }, { node2} }` contains two separate regions. +--- They will be parsed by the parser in two different contexts, thus resulting --- in two separate trees. --- ---- `{ { node1, node2 } }` is a single region consisting of two nodes. ---- This will be parsed by the parser in a single context... thus resulting +--- On the other hand, `{ { node1, node2 } }` is a single region consisting of +--- two nodes. This will be parsed by the parser in a single context, thus resulting --- in a single tree. --- --- This allows for embedded languages to be parsed together across different --- nodes, which is useful for templating languages like ERB and EJS. --- ---- Note, this call invalidates the tree and requires it to be parsed again. +--- Note: This call invalidates the tree and requires it to be parsed again. --- ----@param regions (table) list of regions this tree should manage and parse. +---@private +---@param regions table List of regions this tree should manage and parse. function LanguageTree:set_included_regions(regions) -- Transform the tables from 4 element long to 6 element long (with byte offset) for _, region in ipairs(regions) do @@ -288,7 +309,7 @@ function LanguageTree:set_included_regions(regions) -- Trees are no longer valid now that we have changed regions. -- TODO(vigoux,steelsojka): Look into doing this smarter so we can use some of the -- old trees for incremental parsing. Currently, this only - -- effects injected languages. + -- affects injected languages. self._trees = {} self:invalidate() end @@ -493,8 +514,8 @@ function LanguageTree:_on_detach(...) self:_do_callback('detach', ...) end ---- Registers callbacks for the parser. ----@param cbs table An |nvim_buf_attach()|-like table argument with the following keys : +--- Registers callbacks for the |LanguageTree|. +---@param cbs table An |nvim_buf_attach()|-like table argument with the following handlers: --- - `on_bytes` : see |nvim_buf_attach()|, but this will be called _after_ the parsers callback. --- - `on_changedtree` : a callback that will be called every time the tree has syntactical changes. --- It will only be passed one argument, which is a table of the ranges (as node ranges) that @@ -536,9 +557,10 @@ local function tree_contains(tree, range) return start_fits and end_fits end ---- Determines whether {range} is contained in this language tree +--- Determines whether {range} is contained in the |LanguageTree|. --- ----@param range A range, that is a `{ start_line, start_col, end_line, end_col }` table. +---@param range table `{ start_line, start_col, end_line, end_col }` +---@return boolean function LanguageTree:contains(range) for _, tree in pairs(self._trees) do if tree_contains(tree, range) then @@ -549,11 +571,12 @@ function LanguageTree:contains(range) return false end ---- Gets the tree that contains {range} +--- Gets the tree that contains {range}. --- ----@param range table A text range ----@param opts table Options table ----@param opts.ignore_injections boolean (default true) Ignore injected languages. +---@param range table `{ start_line, start_col, end_line, end_col }` +---@param opts table|nil Optional keyword arguments: +--- - ignore_injections boolean Ignore injected languages (default true) +---@return userdata|nil Contained |tstree| function LanguageTree:tree_for_range(range, opts) opts = opts or {} local ignore = vim.F.if_nil(opts.ignore_injections, true) @@ -577,19 +600,21 @@ function LanguageTree:tree_for_range(range, opts) return nil end ---- Gets the smallest named node that contains {range} +--- Gets the smallest named node that contains {range}. --- ----@param range table A text range ----@param opts table Options table ----@param opts.ignore_injections boolean (default true) Ignore injected languages. +---@param range table `{ start_line, start_col, end_line, end_col }` +---@param opts table|nil Optional keyword arguments: +--- - ignore_injections boolean Ignore injected languages (default true) +---@return userdata|nil Found |tsnode| function LanguageTree:named_node_for_range(range, opts) local tree = self:tree_for_range(range, opts) return tree:root():named_descendant_for_range(unpack(range)) end ---- Gets the appropriate language that contains {range} +--- Gets the appropriate language that contains {range}. --- ----@param range A text range, see |LanguageTree:contains| +---@param range table `{ start_line, start_col, end_line, end_col }` +---@return LanguageTree Managing {range} function LanguageTree:language_for_range(range) for _, child in pairs(self._children) do if child:contains(range) then diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 90ed2a357c..d1dc29969b 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -3,6 +3,11 @@ local language = require('vim.treesitter.language') -- query: pattern matching on trees -- predicate matching is implemented in lua +-- +---@class Query +---@field captures string[] List of captures used in query +---@field info table Contains used queries, predicates, directives +---@field query userdata Parsed query local Query = {} Query.__index = Query @@ -35,9 +40,9 @@ local function safe_read(filename, read_quantifier) end ---@private ---- Adds @p ilang to @p base_langs, only if @p ilang is different than @lang +--- Adds {ilang} to {base_langs}, only if {ilang} is different than {lang} --- ----@return boolean true it lang == ilang +---@return boolean true If lang == ilang local function add_included_lang(base_langs, lang, ilang) if lang == ilang then return true @@ -48,9 +53,10 @@ end --- Gets the list of files used to make up a query --- ----@param lang The language ----@param query_name The name of the query to load ----@param is_included Internal parameter, most of the time left as `nil` +---@param lang string Language to get query for +---@param query_name string Name of the query to load (e.g., 'highlights') +---@param is_included (boolean|nil) Internal parameter, most of the time left as `nil` +---@return string[] query_files List of files to load for given query and language function M.get_query_files(lang, query_name, is_included) local query_path = string.format('queries/%s/%s.scm', lang, query_name) local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true)) @@ -150,24 +156,24 @@ local explicit_queries = setmetatable({}, { end, }) ---- Sets the runtime query {query_name} for {lang} +--- Sets the runtime query named {query_name} for {lang} --- --- This allows users to override any runtime files and/or configuration --- set by plugins. --- ----@param lang string: The language to use for the query ----@param query_name string: The name of the query (i.e. "highlights") ----@param text string: The query text (unparsed). +---@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_query(lang, query_name, text) explicit_queries[lang][query_name] = M.parse_query(lang, text) end --- Returns the runtime query {query_name} for {lang}. --- ----@param lang The language to use for the query ----@param query_name The name of the query (i.e. "highlights") +---@param lang string Language to use for the query +---@param query_name string Name of the query (e.g. 'highlights') --- ----@return The corresponding query, parsed. +---@return Query Parsed query function M.get_query(lang, query_name) if explicit_queries[lang][query_name] then return explicit_queries[lang][query_name] @@ -198,10 +204,10 @@ end) --- -` info.captures` also points to `captures`. --- - `info.patterns` contains information about predicates. --- ----@param lang string The language ----@param query string A string containing the query (s-expr syntax) +---@param lang string Language to use for the query +---@param query string Query in s-expr syntax --- ----@returns The query +---@return Query Parsed query function M.parse_query(lang, query) language.require_language(lang) local cached = query_cache[lang][query] @@ -219,10 +225,11 @@ end --- Gets the text corresponding to a given node --- ----@param node table The node ----@param source table The buffer or string from which the node is extracted ----@param opts table Optional parameters. ---- - concat: (boolean default true) Concatenate result in a string +---@param node userdata |tsnode| +---@param source (number|string) Buffer or string from which the {node} is extracted +---@param opts (table|nil) Optional parameters. +--- - concat: (boolean) Concatenate result in a string (default true) +---@return (string[]|string) function M.get_node_text(node, source, opts) opts = opts or {} local concat = vim.F.if_nil(opts.concat, true) @@ -410,9 +417,8 @@ local directive_handlers = { --- Adds a new predicate to be used in queries --- ----@param name the name of the predicate, without leading # ----@param handler the handler function to be used ---- signature will be (match, pattern, bufnr, predicate) +---@param name string Name of the predicate, without leading # +---@param handler function(match:string, pattern:string, bufnr:number, predicate:function) function M.add_predicate(name, handler, force) if predicate_handlers[name] and not force then error(string.format('Overriding %s', name)) @@ -428,9 +434,8 @@ end --- can set node level data by using the capture id on the --- metadata table `metadata[capture_id].key = value` --- ----@param name the name of the directive, without leading # ----@param handler the handler function to be used ---- signature will be (match, pattern, bufnr, predicate, metadata) +---@param name string Name of the directive, without leading # +---@param handler function(match:string, pattern:string, bufnr:number, predicate:function, metadata:table) function M.add_directive(name, handler, force) if directive_handlers[name] and not force then error(string.format('Overriding %s', name)) @@ -440,12 +445,13 @@ function M.add_directive(name, handler, force) end --- Lists the currently available directives to use in queries. ----@return The list of supported directives. +---@return string[] List of supported directives. function M.list_directives() return vim.tbl_keys(directive_handlers) end ----@return The list of supported predicates. +--- Lists the currently available predicates to use in queries. +---@return string[] List of supported predicates. function M.list_predicates() return vim.tbl_keys(predicate_handlers) end @@ -532,17 +538,16 @@ end --- Iterate over all captures from all matches inside {node} --- ---- {source} is needed if the query contains predicates, then the caller +--- {source} is needed 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_row} and {end_row} 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 end row values are used from the given node. +--- as the {node}, i.e., to get syntax highlight matches in the current +--- viewport). When omitted, the {start} and {end} row values are used from the given node. --- ---- The iterator returns three values, a numeric id identifying the capture, +--- The iterator returns three values: a numeric id identifying the capture, --- the captured node, and metadata from any directives processing the match. --- The following example shows how to get captures by name: ---- ---
 --- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
 ---   local name = query.captures[id] -- name of the capture in the query
@@ -553,13 +558,14 @@ end
 --- end
 --- 
--- ----@param node The node under which the search will occur ----@param source The source buffer or string to extract text from ----@param start The starting line of the search ----@param stop The stopping line of the search (end-exclusive) +---@param node userdata |tsnode| under which the search will occur +---@param source (number|string) Source buffer or string to extract text from +---@param start number Starting line for the search +---@param stop number Stopping line for the search (end-exclusive) --- ----@returns The matching capture id ----@returns The captured node +---@return number capture Matching capture id +---@return table capture_node Capture for {node} +---@return table metadata for the {capture} function Query:iter_captures(node, source, start, stop) if type(source) == 'number' and source == 0 then source = vim.api.nvim_get_current_buf() @@ -589,14 +595,13 @@ end --- Iterates the matches of self on a given range. --- ---- Iterate over all matches within a node. The arguments are the same as +--- Iterate over all matches within a {node}. The arguments are the same as --- for |query:iter_captures()| but the iterated values are different: --- an (1-based) index of the pattern in the query, a table mapping --- capture indices to nodes, and metadata from any directives processing the match. ---- If the query has more than one pattern the capture table might be sparse, +--- If the query has more than one pattern, the capture table might be sparse --- and e.g. `pairs()` method should be used over `ipairs`. ---- Here an example iterating over all captures in every match: ---- +--- Here is an example iterating over all captures in every match: ---
 --- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
 ---   for id, node in pairs(match) do
@@ -610,13 +615,14 @@ end
 --- end
 --- 
--- ----@param node The node under which the search will occur ----@param source The source buffer or string to search ----@param start The starting line of the search ----@param stop The stopping line of the search (end-exclusive) +---@param node userdata |tsnode| under which the search will occur +---@param source (number|string) Source buffer or string to search +---@param start number Starting line for the search +---@param stop number Stopping line for the search (end-exclusive) --- ----@returns The matching pattern id ----@returns The matching match +---@return number pattern id +---@return table match +---@return table metadata function Query:iter_matches(node, source, start, stop) if type(source) == 'number' and source == 0 then source = vim.api.nvim_get_current_buf() -- cgit From 982fef6018fb64c883ddafc897c8f7c58fb1c62d Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 15 Sep 2022 11:03:07 -0600 Subject: fix(diagnostic): populate data key in DiagnosticChanged autocmd in reset (#20207) Follow up to #20173. --- 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 172bd867c7..98dbe0779b 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1426,6 +1426,7 @@ function M.reset(namespace, bufnr) vim.api.nvim_exec_autocmds('DiagnosticChanged', { modeline = false, buffer = iter_bufnr, + data = { diagnostics = {} }, }) end end -- cgit From 9ec4b20be695501ba166f31dec39ef9e30cc7dd8 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 16 Sep 2022 09:05:05 +0200 Subject: fix(treesitter): return full metadata for get_captures_at_position (#20203) fix(treesitter): get_captures_at_position returns metadata Return the full `metadata` table for the capture instead of just the priority. Further cleanup of related docs. --- runtime/lua/vim/treesitter.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 89aa611acd..04e12cbe0b 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -154,7 +154,7 @@ function M.get_node_range(node_or_range) end end ----Determines whether (line, col) position is in node range +--- Determines whether (line, col) position is in node range --- ---@param node userdata |tsnode| defining the range ---@param line number Line (0-based) @@ -178,7 +178,8 @@ function M.is_in_node_range(node, line, col) end end ----Determines if a node contains a range +--- Determines if a node contains a range +--- ---@param node userdata |tsnode| ---@param range table --- @@ -191,17 +192,16 @@ function M.node_contains(node, range) return start_fits and end_fits end ----Returns a list of highlight captures at the given position --- ----@param bufnr number Buffer number (0 for current buffer) ----@param row number Position row ----@param col number Position column +--- 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). --- ---@param bufnr number Buffer number (0 for current buffer) ---@param row number Position row ---@param col number Position column --- ----@return table[] Captures of the form `{ capture = "capture name", priority = capture priority }` +---@return table[] List of captures `{ capture = "capture name", metadata = { ... } }` function M.get_captures_at_position(bufnr, row, col) if bufnr == 0 then bufnr = a.nvim_get_current_buf() @@ -240,7 +240,7 @@ function M.get_captures_at_position(bufnr, row, col) if M.is_in_node_range(node, row, col) then local c = q._query.captures[capture] -- name of the capture in the query if c ~= nil then - table.insert(matches, { capture = c, priority = metadata.priority }) + table.insert(matches, { capture = c, metadata = metadata }) end end end @@ -248,7 +248,7 @@ function M.get_captures_at_position(bufnr, row, col) return matches end ----Returns a list of highlight capture names under the cursor +--- Returns a list of highlight capture names under the cursor --- ---@param winnr (number|nil) Window handle or 0 for current window (default) --- -- cgit From e512d3ecf2b6e0104d2df0d863c8d51a2d7e5ab1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 16 Sep 2022 17:52:08 +0200 Subject: vim-patch:9.0.0479: in :def function all closures in loop get the sam… (#20220) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vim-patch:9.0.0479: in :def function all closures in loop get the same variables Problem: In a :def function all closures in a loop get the same variables. Solution: Use a separate list of variables for LOADOUTER and SAVEOUTER. https://github.com/vim/vim/commit/1aea184a0dc558a222cc5bcbaad9ab0fd700c7b9 (note: patch description is wrong) --- 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 39985c948e..8fb8e98f7d 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1021,6 +1021,7 @@ local extension = { dsm = 'vb', ctl = 'vb', vbs = 'vb', + vdf = 'vdf', vdmpp = 'vdmpp', vpp = 'vdmpp', vdmrt = 'vdmrt', -- cgit From 8dc61713d9db433dfa2024b45f3bc590dcb58f83 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 18 Sep 2022 14:16:24 +0200 Subject: vim-patch:9.0.0497: LyRiCs files are not recognized (#20239) Problem: LyRiCs files are not recognized. Solution: Add a pattern to detect LyRiCs files. (closes vim/vim#11155) https://github.com/vim/vim/commit/65ee49decf5677690cd695d5d288e39344965fff --- 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 8fb8e98f7d..5c595df087 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -611,6 +611,7 @@ local extension = { nse = 'lua', rockspec = 'lua', lua = 'lua', + lrc = 'lyrics', m = function(path, bufnr) return require('vim.filetype.detect').m(bufnr) end, -- cgit From 10196f1b462400b8a4a9e8f13893da2514f8c850 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Tue, 20 Sep 2022 09:28:23 +0200 Subject: fix(lsp): support `false` result in handlers (#20252) Closes https://github.com/neovim/neovim/issues/20111 --- runtime/lua/vim/lsp/rpc.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 7d047f8958..755c0ffc6f 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -405,8 +405,7 @@ function Client:handle_body(body) { status = status, result = result, err = err } ) if status then - if not (result or err) then - -- TODO this can be a problem if `null` is sent for result. needs vim.NIL + if result == nil and err == nil then error( string.format( 'method %q: either a result or an error must be sent to the server in response', -- cgit From e76215830522f75dea1495a0de38933d18954eca Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 20 Sep 2022 10:42:45 +0100 Subject: docs(lua): opts in `vim.keymap.{set,del}` can be optional (#20255) --- runtime/lua/vim/keymap.lua | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index 219de16b5c..af41794c53 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -36,17 +36,17 @@ local keymap = {} ---@param lhs string Left-hand side |{lhs}| of the mapping. ---@param rhs string|function Right-hand side |{rhs}| of the mapping. Can also be a Lua function. -- ----@param opts table A table of |:map-arguments|. ---- + Accepts options accepted by the {opts} parameter in |nvim_set_keymap()|, ---- with the following notable differences: ---- - replace_keycodes: Defaults to `true` if "expr" is `true`. ---- - noremap: Always overridden with the inverse of "remap" (see below). ---- + In addition to those options, the table accepts the following keys: ---- - buffer: (number or boolean) Add a mapping to the given buffer. ---- When `0` or `true`, use the current buffer. ---- - remap: (boolean) Make the mapping recursive. ---- This is the inverse of the "noremap" option from |nvim_set_keymap()|. ---- Defaults to `false`. +---@param opts table|nil A table of |:map-arguments|. +--- + Accepts options accepted by the {opts} parameter in |nvim_set_keymap()|, +--- with the following notable differences: +--- - replace_keycodes: Defaults to `true` if "expr" is `true`. +--- - noremap: Always overridden with the inverse of "remap" (see below). +--- + In addition to those options, the table accepts the following keys: +--- - buffer: (number or boolean) Add a mapping to the given buffer. +--- When `0` or `true`, use the current buffer. +--- - remap: (boolean) Make the mapping recursive. +--- This is the inverse of the "noremap" option from |nvim_set_keymap()|. +--- Defaults to `false`. ---@see |nvim_set_keymap()| function keymap.set(mode, lhs, rhs, opts) vim.validate({ @@ -57,7 +57,6 @@ function keymap.set(mode, lhs, rhs, opts) }) opts = vim.deepcopy(opts) or {} - local is_rhs_luaref = type(rhs) == 'function' mode = type(mode) == 'string' and { mode } or mode if opts.expr and opts.replace_keycodes ~= false then @@ -73,7 +72,7 @@ function keymap.set(mode, lhs, rhs, opts) opts.remap = nil end - if is_rhs_luaref then + if type(rhs) == 'function' then opts.callback = rhs rhs = '' end @@ -99,9 +98,9 @@ end --- --- vim.keymap.del({'n', 'i', 'v'}, 'w', { buffer = 5 }) ---
----@param opts table A table of optional arguments: ---- - buffer: (number or boolean) Remove a mapping from the given buffer. ---- When "true" or 0, use the current buffer. +---@param opts table|nil A table of optional arguments: +--- - buffer: (number or boolean) Remove a mapping from the given buffer. +--- When "true" or 0, use the current buffer. ---@see |vim.keymap.set()| --- function keymap.del(modes, lhs, opts) -- cgit From abe2d90693e5cec3428c0162c48f0ea38972ff31 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 20 Sep 2022 11:15:32 +0100 Subject: feat(lua): move compat module from runtime to test (#20257) --- runtime/lua/vim/compat.lua | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 runtime/lua/vim/compat.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/compat.lua b/runtime/lua/vim/compat.lua deleted file mode 100644 index 2c9786d491..0000000000 --- a/runtime/lua/vim/compat.lua +++ /dev/null @@ -1,12 +0,0 @@ --- Lua 5.1 forward-compatibility layer. --- For background see https://github.com/neovim/neovim/pull/9280 --- --- Reference the lua-compat-5.2 project for hints: --- https://github.com/keplerproject/lua-compat-5.2/blob/c164c8f339b95451b572d6b4b4d11e944dc7169d/compat52/mstrict.lua --- https://github.com/keplerproject/lua-compat-5.2/blob/c164c8f339b95451b572d6b4b4d11e944dc7169d/tests/test.lua - -local lua_version = _VERSION:sub(-3) - -if lua_version >= '5.2' then - unpack = table.unpack -- luacheck: ignore 121 143 -end -- cgit From ae30e388dee08e59c08dffc20c2f5122b8c31249 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 20 Sep 2022 14:10:00 +0200 Subject: vim-patch:9.0.0510: Chatito files are not recognized (#20260) Problem: Chatito files are not recognized. Solution: Add a pattern for Chatito files. (closes vim/vim#11174) https://github.com/vim/vim/commit/7c046ae99ba85a4fdf1a546157e2ed6f12b79ea6 --- 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 5c595df087..e58d56ad39 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -202,6 +202,7 @@ local extension = { return require('vim.filetype.detect').change(bufnr) end, chs = 'chaskell', + chatito = 'chatito', chopro = 'chordpro', crd = 'chordpro', crdpro = 'chordpro', -- cgit From ec94014cd1d09884b12cb19021d5a1eff52cb76d Mon Sep 17 00:00:00 2001 From: ofwinterpassed Date: Tue, 20 Sep 2022 22:14:58 +0200 Subject: fix(lsp): out of bounds error in lsp.util.apply_text_edits (#20137) Co-authored-by: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> --- runtime/lua/vim/lsp/util.lua | 65 ++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 24 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 283099bbcf..1909dbd4d1 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -459,35 +459,52 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) text = split(text_edit.newText, '\n', true), } - -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here. local max = api.nvim_buf_line_count(bufnr) - if max <= e.start_row or max <= e.end_row then - local len = #(get_line(bufnr, max - 1) or '') - if max <= e.start_row then - e.start_row = max - 1 - e.start_col = len - table.insert(e.text, 1, '') - end + -- If the whole edit is after the lines in the buffer we can simply add the new text to the end + -- of the buffer. + if max <= e.start_row then + api.nvim_buf_set_lines(bufnr, max, max, false, e.text) + else + local last_line_len = #(get_line(bufnr, math.min(e.end_row, max - 1)) or '') + -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't + -- accept it so we should fix it here. if max <= e.end_row then e.end_row = max - 1 - e.end_col = len + e.end_col = last_line_len + has_eol_text_edit = true + else + -- If the replacement is over the end of a line (i.e. e.end_col is out of bounds and the + -- replacement text ends with a newline We can likely assume that the replacement is assumed + -- to be meant to replace the newline with another newline and we need to make sure this + -- doens't add an extra empty line. E.g. when the last line to be replaced contains a '\r' + -- in the file some servers (clangd on windows) will include that character in the line + -- while nvim_buf_set_text doesn't count it as part of the line. + if + e.end_col > last_line_len + and #text_edit.newText > 0 + and string.sub(text_edit.newText, -1) == '\n' + then + table.remove(e.text, #e.text) + end end - has_eol_text_edit = true - end - api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text) - - -- Fix cursor position. - local row_count = (e.end_row - e.start_row) + 1 - if e.end_row < cursor.row then - cursor.row = cursor.row + (#e.text - row_count) - is_cursor_fixed = true - elseif e.end_row == cursor.row and e.end_col <= cursor.col then - cursor.row = cursor.row + (#e.text - row_count) - cursor.col = #e.text[#e.text] + (cursor.col - e.end_col) - if #e.text == 1 then - cursor.col = cursor.col + e.start_col + -- Make sure we don't go out of bounds for e.end_col + e.end_col = math.min(last_line_len, e.end_col) + + api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text) + + -- Fix cursor position. + local row_count = (e.end_row - e.start_row) + 1 + if e.end_row < cursor.row then + cursor.row = cursor.row + (#e.text - row_count) + is_cursor_fixed = true + elseif e.end_row == cursor.row and e.end_col <= cursor.col then + cursor.row = cursor.row + (#e.text - row_count) + cursor.col = #e.text[#e.text] + (cursor.col - e.end_col) + if #e.text == 1 then + cursor.col = cursor.col + e.start_col + end + is_cursor_fixed = true end - is_cursor_fixed = true end end -- cgit From b4b05f160dbb6b9b945c173b7e910b0e4c1a8b01 Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Wed, 21 Sep 2022 23:58:57 +0200 Subject: feat(filetype): expand environment variables in filetype patterns (#20145) --- runtime/lua/vim/filetype.lua | 59 ++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 27 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index e58d56ad39..5f58da7a34 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1296,7 +1296,6 @@ local filename = { WORKSPACE = 'bzl', BUILD = 'bzl', ['cabal.project'] = 'cabalproject', - [vim.env.HOME .. '/cabal.config'] = 'cabalconfig', ['cabal.config'] = 'cabalconfig', calendar = 'calendar', catalog = 'catalog', @@ -1704,6 +1703,7 @@ local pattern = { ['.*/meta%-.*/conf/.*%.conf'] = 'bitbake', ['bzr_log%..*'] = 'bzr', ['.*enlightenment/.*%.cfg'] = 'c', + ['${HOME}/cabal%.config'] = 'cabalconfig', ['cabal%.project%..*'] = starsetf('cabalproject'), ['.*/%.calendar/.*'] = starsetf('calendar'), ['.*/share/calendar/.*/calendar%..*'] = starsetf('calendar'), @@ -1828,41 +1828,21 @@ local pattern = { ['.*/%.config/git/config'] = 'gitconfig', ['.*%.git/config%.worktree'] = 'gitconfig', ['.*%.git/worktrees/.*/config%.worktree'] = 'gitconfig', - ['.*/git/config'] = function(path, bufnr) - if vim.env.XDG_CONFIG_HOME and path:find(vim.env.XDG_CONFIG_HOME .. '/git/config') then - return 'gitconfig' - end - end, + ['${XDG_CONFIG_HOME}/git/config'] = 'gitconfig', ['.*%.git/info/attributes'] = 'gitattributes', ['.*/etc/gitattributes'] = 'gitattributes', ['.*/%.config/git/attributes'] = 'gitattributes', - ['.*/git/attributes'] = function(path, bufnr) - if vim.env.XDG_CONFIG_HOME and path:find(vim.env.XDG_CONFIG_HOME .. '/git/attributes') then - return 'gitattributes' - end - end, + ['${XDG_CONFIG_HOME}/git/attributes'] = 'gitattributes', ['.*%.git/info/exclude'] = 'gitignore', ['.*/%.config/git/ignore'] = 'gitignore', - ['.*/git/ignore'] = function(path, bufnr) - if vim.env.XDG_CONFIG_HOME and path:find(vim.env.XDG_CONFIG_HOME .. '/git/ignore') then - return 'gitignore' - end - end, + ['${XDG_CONFIG_HOME}/git/ignore'] = 'gitignore', ['%.gitsendemail%.msg%.......'] = 'gitsendemail', ['gkrellmrc_.'] = 'gkrellmrc', ['.*/usr/.*/gnupg/options%.skel'] = 'gpg', ['.*/%.gnupg/options'] = 'gpg', ['.*/%.gnupg/gpg%.conf'] = 'gpg', - ['.*/options'] = function(path, bufnr) - if vim.env.GNUPGHOME and path:find(vim.env.GNUPGHOME .. '/options') then - return 'gpg' - end - end, - ['.*/gpg%.conf'] = function(path, bufnr) - if vim.env.GNUPGHOME and path:find(vim.env.GNUPGHOME .. '/gpg%.conf') then - return 'gpg' - end - end, + ['${GNUPGHOME}/options'] = 'gpg', + ['${GNUPGHOME}/gpg%.conf'] = 'gpg', ['.*/etc/group'] = 'group', ['.*/etc/gshadow'] = 'group', ['.*/etc/group%.edit'] = 'group', @@ -1876,7 +1856,7 @@ local pattern = { ['.*/etc/grub%.conf'] = 'grub', -- gtkrc* and .gtkrc* ['%.?gtkrc.*'] = starsetf('gtkrc'), - [vim.env.VIMRUNTIME .. '/doc/.*%.txt'] = 'help', + ['${VIMRUNTIME}/doc/.*%.txt'] = 'help', ['hg%-editor%-.*%.txt'] = 'hgcommit', ['.*/etc/host%.conf'] = 'hostconf', ['.*/etc/hosts%.deny'] = 'hostsaccess', @@ -2280,6 +2260,9 @@ end --- Filename patterns can specify an optional priority to resolve cases when a --- file path matches multiple patterns. Higher priorities are matched first. --- When omitted, the priority defaults to 0. +--- A pattern can contain environment variables of the form "${SOME_VAR}" that will +--- be automatically expanded. If the environment variable is not set, the pattern +--- won't be matched. --- --- See $VIMRUNTIME/lua/vim/filetype.lua for more examples. --- @@ -2308,6 +2291,8 @@ end --- ['.*/etc/foo/.*'] = 'fooscript', --- -- Using an optional priority --- ['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } }, +--- -- A pattern containing an environment variable +--- ['${XDG_CONFIG_HOME}/foo/git'] = 'git', --- ['README.(%a+)$'] = function(path, bufnr, ext) --- if ext == 'md' then --- return 'markdown' @@ -2381,8 +2366,28 @@ local function dispatch(ft, path, bufnr, ...) end end +-- Lookup table/cache for patterns that contain an environment variable pattern, e.g. ${SOME_VAR}. +local expand_env_lookup = {} + ---@private local function match_pattern(name, path, tail, pat) + if expand_env_lookup[pat] == nil then + expand_env_lookup[pat] = pat:find('%${') ~= nil + end + if expand_env_lookup[pat] then + local return_early + pat = pat:gsub('%${(%S-)}', function(env) + -- If an environment variable is present in the pattern but not set, there is no match + if not vim.env[env] then + return_early = true + return nil + end + return vim.env[env] + end) + if return_early then + return false + end + end -- If the pattern contains a / match against the full path, otherwise just the tail local fullpat = '^' .. pat .. '$' local matches -- cgit From 14610332b2e851f9464a2d32a0ef3869dcff9834 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 23 Sep 2022 20:20:56 -0500 Subject: fix(lsp): use correct function name in deprecated message (#20308) fix: use correct function name in deprecated message --- 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 2ce565e1d9..8567619228 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -404,7 +404,7 @@ end ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_formatting(options, start_pos, end_pos) - vim.deprecate('vim.lsp.buf.range_formatting', 'vim.lsp.formatexpr or vim.lsp.format', '0.9.0') + vim.deprecate('vim.lsp.buf.range_formatting', 'vim.lsp.formatexpr or vim.lsp.buf.format', '0.9.0') local params = util.make_given_range_params(start_pos, end_pos) params.options = util.make_formatting_params(options).options select_client('textDocument/rangeFormatting', function(client) -- cgit From 24b5449b3d9b39a436bb8fe935116afa15a9473e Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 24 Sep 2022 10:16:30 +0200 Subject: vim-patch:9.0.0562: HSL playlist files are not recognized (#20307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: HSL playlist files are not recognized. Solution: Add a pattern to recognize HSL palylist files. (Benoît Ryder, closes vim/vim#11204) https://github.com/vim/vim/commit/35fdd9a67d73d4750152c419d4193ebb6b6d6eee --- 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 5f58da7a34..c96ead323b 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -473,6 +473,7 @@ local extension = { hex = 'hex', ['h32'] = 'hex', hjson = 'hjson', + m3u8 = 'hlsplaylist', hog = 'hog', hws = 'hollywood', hoon = 'hoon', -- cgit From caf5738fa9cc12fd448a9c0787a3ebf0c8e696e9 Mon Sep 17 00:00:00 2001 From: shaunsingh Date: Sat, 24 Sep 2022 06:46:21 -0400 Subject: fix(lsp): create missing directory before creating file (#19835) Co-authored-by: Mathias Fussenegger --- runtime/lua/vim/lsp/util.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 1909dbd4d1..64512a9739 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -772,8 +772,11 @@ local function create_file(change) -- from spec: Overwrite wins over `ignoreIfExists` local fname = vim.uri_to_fname(change.uri) if not opts.ignoreIfExists or opts.overwrite then + vim.fn.mkdir(vim.fs.dirname(fname), 'p') local file = io.open(fname, 'w') - file:close() + if file then + file:close() + end end vim.fn.bufadd(fname) end -- cgit From f8b656c582c6f0d9cb35523ac2d2f3d0edbcd52b Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 24 Sep 2022 12:59:37 +0200 Subject: vim-patch:9.0.0566: Nim files are not recognized (#20317) Problem: Nim files are not recognized. Solution: Add patterns for Nim files. (Nbiba Bedis, closes vim/vim#11205) https://github.com/vim/vim/commit/9fd1583c839c5e43b0d48ec815a79005a2364776 --- 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 c96ead323b..d8ede33dfa 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -704,6 +704,9 @@ local extension = { nanorc = 'nanorc', ncf = 'ncf', nginx = 'nginx', + nim = 'nim', + nims = 'nim', + nimble = 'nim', ninja = 'ninja', nix = 'nix', nqc = 'nqc', -- cgit From 3169fc54a129f6dd1c0baf2d404e50381cf48e8f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 25 Sep 2022 00:45:15 +0200 Subject: refactor(treesitter): rename x_position => x_pos "pos" has a long precedent as "position" in vim, and there is no reason to use a verbose name here. --- runtime/lua/vim/treesitter.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 04e12cbe0b..6cd00516bf 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -202,7 +202,7 @@ end ---@param col number Position column --- ---@return table[] List of captures `{ capture = "capture name", metadata = { ... } }` -function M.get_captures_at_position(bufnr, row, col) +function M.get_captures_at_pos(bufnr, row, col) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end @@ -258,7 +258,7 @@ function M.get_captures_at_cursor(winnr) local bufnr = a.nvim_win_get_buf(winnr) local cursor = a.nvim_win_get_cursor(winnr) - local data = M.get_captures_at_position(bufnr, cursor[1] - 1, cursor[2]) + local data = M.get_captures_at_pos(bufnr, cursor[1] - 1, cursor[2]) local captures = {} @@ -278,7 +278,7 @@ end --- - ignore_injections boolean Ignore injected languages (default true) --- ---@return userdata |tsnode| under the cursor -function M.get_node_at_position(bufnr, row, col, opts) +function M.get_node_at_pos(bufnr, row, col, opts) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end @@ -302,8 +302,7 @@ function M.get_node_at_cursor(winnr) local bufnr = a.nvim_win_get_buf(winnr) local cursor = a.nvim_win_get_cursor(winnr) - return M.get_node_at_position(bufnr, cursor[1] - 1, cursor[2], { ignore_injections = false }) - :type() + return M.get_node_at_pos(bufnr, cursor[1] - 1, cursor[2], { ignore_injections = false }):type() end --- Starts treesitter highlighting for a buffer -- cgit From 2a5692c64628ee0af3ef4931a774a2eb0a7e046f Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 25 Sep 2022 13:59:11 +0200 Subject: vim-patch:9.0.0583: only recognizing .m3u8 files is inconsistent (#20342) Problem: Only recognizing .m3u8 files is inconsistent. Solution: Also matc .m3u files. (issue vim/vim#11204) https://github.com/vim/vim/commit/b9725bc7f6427654eb4e35874034b0ec1b6b96b3 --- 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 d8ede33dfa..bd317baae9 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -473,6 +473,7 @@ local extension = { hex = 'hex', ['h32'] = 'hex', hjson = 'hjson', + m3u = 'hlsplaylist', m3u8 = 'hlsplaylist', hog = 'hog', hws = 'hollywood', -- cgit From 63be7651829f8b77c4974d08ebe09f7775e41a8a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 25 Sep 2022 19:58:27 -0400 Subject: fix(docs): invalid :help links #20345 Fix those naughty single quotes. closes #20159 --- runtime/lua/vim/_editor.lua | 4 ++-- runtime/lua/vim/highlight.lua | 2 +- runtime/lua/vim/lsp.lua | 22 +++++++++++----------- runtime/lua/vim/lsp/buf.lua | 14 +++++++------- runtime/lua/vim/lsp/handlers.lua | 2 +- runtime/lua/vim/lsp/rpc.lua | 2 +- runtime/lua/vim/lsp/util.lua | 6 +++--- runtime/lua/vim/shared.lua | 2 +- runtime/lua/vim/treesitter/query.lua | 8 ++++---- 9 files changed, 31 insertions(+), 31 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index b8a7f71145..96a87b8bb9 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -401,7 +401,7 @@ end ---@param bufnr number of buffer ---@param pos1 (line, column) tuple marking beginning of region ---@param pos2 (line, column) tuple marking end of region ----@param regtype type of selection (:help setreg) +---@param regtype type of selection, see |setreg()| ---@param inclusive boolean indicating whether the selection is end-inclusive ---@return region lua table of the form {linenr = {startcol,endcol}} function vim.region(bufnr, pos1, pos2, regtype, inclusive) @@ -448,7 +448,7 @@ end --- Defers calling `fn` until `timeout` ms passes. --- --- Use to do a one-shot timer that calls `fn` ---- Note: The {fn} is |schedule_wrap|ped automatically, so API functions are +--- Note: The {fn} is |vim.schedule_wrap()|ped automatically, so API functions are --- safe to call. ---@param fn Callback to call once `timeout` expires ---@param timeout Number of milliseconds to wait before calling `fn` diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index ddd504a0e0..0fde515bd9 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -41,7 +41,7 @@ end ---@param start first position (tuple {line,col}) ---@param finish second position (tuple {line,col}) ---@param opts table with options: --- - regtype type of range (:help setreg, default charwise) +-- - regtype type of range (see |setreg()|, default charwise) -- - inclusive boolean indicating whether the range is end-inclusive (default false) -- - priority number indicating priority of highlight (default priorities.user) function M.range(bufnr, ns, higroup, start, finish, opts) diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 22933d8143..a64facf214 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -823,7 +823,7 @@ end --- }) --- --- ---- See |lsp.start_client| for all available options. The most important are: +--- See |vim.lsp.start_client()| for all available options. The most important are: --- --- `name` is an arbitrary name for the LSP client. It should be unique per --- language server. @@ -834,7 +834,7 @@ end --- --- `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.find| and |vim.fs.dirname| to detect the +--- The example above uses |vim.fs.find()| and |vim.fs.dirname()| to detect the --- root by traversing the file system upwards starting --- from the current directory until either a `pyproject.toml` or `setup.py` --- file is found. @@ -849,11 +849,11 @@ end --- --- --- To ensure a language server is only started for languages it can handle, ---- make sure to call |vim.lsp.start| within a |FileType| autocmd. +--- make sure to call |vim.lsp.start()| within a |FileType| autocmd. --- Either use |:au|, |nvim_create_autocmd()| or put the call in a --- `ftplugin/.lua` (See |ftplugin-name|) --- ----@param config table Same configuration as documented in |lsp.start_client()| +---@param config table Same configuration as documented in |vim.lsp.start_client()| ---@param opts nil|table Optional keyword arguments: --- - reuse_client (fun(client: client, config: table): boolean) --- Predicate used to decide if a client should be re-used. @@ -902,12 +902,12 @@ end --- --- ---@param cmd: (table|string|fun(dispatchers: table):table) command string or ---- list treated like |jobstart|. The command must launch the language server +--- list treated like |jobstart()|. The command must launch the language server --- process. `cmd` can also be a function that creates an RPC client. --- The function receives a dispatchers table and must return a table with the --- functions `request`, `notify`, `is_closing` and `terminate` ---- See |vim.lsp.rpc.request| and |vim.lsp.rpc.notify| ---- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect| +--- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()| +--- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()| --- ---@param cmd_cwd: (string, default=|getcwd()|) Directory to launch --- the `cmd` process. Not related to `root_dir`. @@ -963,7 +963,7 @@ end ---@param on_error Callback with parameters (code, ...), invoked --- when the client operation throws an error. `code` is a number describing --- the error. Other arguments may be passed depending on the error kind. See ---- |vim.lsp.rpc.client_errors| for possible errors. +--- `vim.lsp.rpc.client_errors` for possible errors. --- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name. --- ---@param before_init Callback with parameters (initialize_params, config) @@ -999,8 +999,8 @@ end --- notifications to the server by the given number in milliseconds. No debounce --- occurs if nil --- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to ---- exit cleanly after sending the 'shutdown' request before sending kill -15. ---- If set to false, nvim exits immediately after sending the 'shutdown' request to the server. +--- exit cleanly after sending the "shutdown" request before sending kill -15. +--- If set to false, nvim exits immediately after sending the "shutdown" request to the server. --- ---@param root_dir string Directory where the LSP --- server will base its workspaceFolders, rootUri, and rootPath @@ -1078,7 +1078,7 @@ function lsp.start_client(config) --- ---@param code (number) Error code ---@param err (...) Other arguments may be passed depending on the error kind - ---@see |vim.lsp.rpc.client_errors| for possible errors. Use + ---@see `vim.lsp.rpc.client_errors` for possible errors. Use ---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name. function dispatch.on_error(code, err) local _ = log.error() diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8567619228..b9aaacb437 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -111,7 +111,7 @@ end --- about the context in which a completion was triggered (how it was triggered, --- and by which trigger character, if applicable) --- ----@see |vim.lsp.protocol.constants.CompletionTriggerKind| +---@see vim.lsp.protocol.constants.CompletionTriggerKind function M.completion(context) local params = util.make_position_params() params.context = context @@ -317,7 +317,7 @@ end --- ---@param options table|nil with valid `FormattingOptions` entries ---@param timeout_ms (number) Request timeout ----@see |vim.lsp.buf.formatting_seq_sync| +---@see |vim.lsp.buf.format()| function M.formatting_sync(options, timeout_ms) vim.notify_once( 'vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', @@ -614,14 +614,14 @@ end --- Lists all the call sites of the symbol under the cursor in the --- |quickfix| window. If the symbol can resolve to multiple ---- items, the user can pick one in the |inputlist|. +--- items, the user can pick one in the |inputlist()|. function M.incoming_calls() call_hierarchy('callHierarchy/incomingCalls') end --- Lists all the items that are called by the symbol under the --- cursor in the |quickfix| window. If the symbol can resolve to ---- multiple items, the user can pick one in the |inputlist|. +--- multiple items, the user can pick one in the |inputlist()|. function M.outgoing_calls() call_hierarchy('callHierarchy/outgoingCalls') end @@ -730,9 +730,9 @@ end --- --- Note: Usage of |vim.lsp.buf.document_highlight()| requires the following highlight groups --- to be defined or you won't be able to see the actual highlights. ---- |LspReferenceText| ---- |LspReferenceRead| ---- |LspReferenceWrite| +--- |hl-LspReferenceText| +--- |hl-LspReferenceRead| +--- |hl-LspReferenceWrite| function M.document_highlight() local params = util.make_position_params() request('textDocument/documentHighlight', params) diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 624436bc9b..a32c59dd17 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -389,7 +389,7 @@ M['textDocument/implementation'] = location_handler ---@param config table Configuration table. --- - border: (default=nil) --- - Add borders to the floating window ---- - See |vim.api.nvim_open_win()| +--- - See |nvim_open_win()| function M.signature_help(_, result, ctx, config) config = config or {} config.focus_id = ctx.method diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 755c0ffc6f..842a0ce329 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -635,7 +635,7 @@ end --- Starts an LSP server process and create an LSP RPC client object to --- interact with it. Communication with the spawned process happens via stdio. For ---- communication via TCP, spawn a process manually and use |vim.lsp.rpc.connect| +--- communication via TCP, spawn a process manually and use |vim.lsp.rpc.connect()| --- ---@param cmd (string) Command to start the LSP server. ---@param cmd_args (table) List of additional string arguments to pass to {cmd}. diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 64512a9739..dbc18963f9 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1504,7 +1504,7 @@ end --- ---@param contents table of lines to show in window ---@param syntax string of syntax to set for opened buffer ----@param opts table with optional fields (additional keys are passed on to |vim.api.nvim_open_win()|) +---@param opts table with optional fields (additional keys are passed on to |nvim_open_win()|) --- - height: (number) height of floating window --- - width: (number) width of floating window --- - wrap: (boolean, default true) wrap long lines @@ -1819,7 +1819,7 @@ end --- CAUTION: Modifies the input in-place! --- ---@param lines (table) list of lines ----@returns (string) filetype or 'markdown' if it was unchanged. +---@returns (string) filetype or "markdown" if it was unchanged. function M.try_trim_markdown_code_blocks(lines) local language_id = lines[1]:match('^```(.*)') if language_id then @@ -1992,7 +1992,7 @@ function M.make_workspace_params(added, removed) end --- Returns indentation size. --- ----@see |shiftwidth| +---@see 'shiftwidth' ---@param bufnr (number|nil): Buffer handle, defaults to current ---@returns (number) indentation size function M.get_effective_tabstop(bufnr) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index de5f7240aa..c5c31b6ddf 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -302,7 +302,7 @@ end --- Merges recursively two or more map-like tables. --- ----@see |tbl_extend()| +---@see |vim.tbl_extend()| --- ---@param behavior string Decides what to do if a key is found in more than one map: --- - "error": raise an error diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index d1dc29969b..1e46daaccf 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -54,7 +54,7 @@ end --- Gets the list of files used to make up a query --- ---@param lang string Language to get query for ----@param query_name string Name of the query to load (e.g., 'highlights') +---@param query_name string Name of the query to load (e.g., "highlights") ---@param is_included (boolean|nil) Internal parameter, most of the time left as `nil` ---@return string[] query_files List of files to load for given query and language function M.get_query_files(lang, query_name, is_included) @@ -162,7 +162,7 @@ local explicit_queries = setmetatable({}, { --- set by plugins. --- ---@param lang string Language to use for the query ----@param query_name string Name of the query (e.g., 'highlights') +---@param query_name string Name of the query (e.g., "highlights") ---@param text string Query text (unparsed). function M.set_query(lang, query_name, text) explicit_queries[lang][query_name] = M.parse_query(lang, text) @@ -171,7 +171,7 @@ end --- Returns the runtime query {query_name} for {lang}. --- ---@param lang string Language to use for the query ----@param query_name string Name of the query (e.g. 'highlights') +---@param query_name string Name of the query (e.g. "highlights") --- ---@return Query Parsed query function M.get_query(lang, query_name) @@ -596,7 +596,7 @@ end --- Iterates the matches of self on a given range. --- --- Iterate over all matches within a {node}. The arguments are the same as ---- for |query:iter_captures()| but the iterated values are different: +--- for |Query:iter_captures()| but the iterated values are different: --- an (1-based) index of the pattern in the query, a table mapping --- capture indices to nodes, and metadata from any directives processing the match. --- If the query has more than one pattern, the capture table might be sparse -- cgit From 760a8754c07a62afa4d83f4e254b1d45ae8cfc65 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 27 Sep 2022 12:36:33 +0200 Subject: vim-patch:9.0.0599: latexmkrc files are not recognized Problem: Latexmkrc files are not recognized. Solution: Use Perl filetype for latexmkrc files. (closes vim/vim#11241) https://github.com/vim/vim/commit/cde031938537970938437cdbb235bc0da755ae4a --- 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 bd317baae9..642f783e78 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1496,6 +1496,8 @@ local filename = { ['/etc/shadow-'] = 'passwd', ['/etc/shadow'] = 'passwd', ['/etc/passwd.edit'] = 'passwd', + ['latexmkrc'] = 'perl', + ['.latexmkrc'] = 'perl', ['pf.conf'] = 'pf', ['main.cf'] = 'pfmain', pinerc = 'pine', -- cgit From e176f9dacf28f56e91fcbc92f392228588a46c85 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 27 Sep 2022 12:38:31 +0200 Subject: vim-patch:9.0.0600: GYP files are not recognized Problem: GYP files are not recognized. Solution: Recognize GYP files. (closes vim/vim#11242) https://github.com/vim/vim/commit/d32474229213276c64cb293885a975dcb406fbc9 --- 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 642f783e78..66b89a7078 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -448,6 +448,8 @@ local extension = { gsp = 'gsp', gjs = 'javascript.glimmer', gts = 'typescript.glimmer', + gyp = 'gyp', + gypi = 'gyp', hack = 'hack', hackpartial = 'hack', haml = 'haml', -- cgit From fe0727a1bfebdb8dc27bd8c276566f203b4d6a18 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 27 Sep 2022 13:03:28 +0200 Subject: vim-patch:9.0.0602: new TypeScript extensions are not recognized Problem: New TypeScript extensions are not recognized. Solution: Recognize .mts and .cts files. (closes vim/vim#11237) https://github.com/vim/vim/commit/7fc6c0e4dab4e80b9806a973936af54276468513 --- 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 66b89a7078..a7a34a54cd 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1021,6 +1021,8 @@ local extension = { ts = function(path, bufnr) return M.getlines(bufnr, 1):find('<%?xml') and 'xml' or 'typescript' end, + mts = 'typescript', + cts = 'typescript', tsx = 'typescriptreact', uc = 'uc', uit = 'uil', -- cgit From f46060c4cbc1efe100019075214def53fe4d47b3 Mon Sep 17 00:00:00 2001 From: ObserverOfTime Date: Tue, 27 Sep 2022 17:10:19 +0300 Subject: vim-patch:9.0.0604: luacheckrc file is not recognized (#20371) Problem: Luacheckrc file is not recognized. Solution: Use lua filetype for luacheckrc. (closes vim/vim#11236) https://github.com/vim/vim/commit/49c311c9b18e18c05f93728d1f8a552923a18423 --- 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 a7a34a54cd..d584d2659d 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1455,6 +1455,7 @@ local filename = { ['.sawfishrc'] = 'lisp', ['/etc/login.access'] = 'loginaccess', ['/etc/login.defs'] = 'logindefs', + ['.luacheckrc'] = 'lua', ['lynx.cfg'] = 'lynx', ['m3overrides'] = 'm3build', ['m3makefile'] = 'm3build', -- cgit From 16336c486ecb5a60e85a870904316308c7d7fc3f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 25 Sep 2022 02:20:47 +0200 Subject: feat(gen_help_html.lua): adapt to new parser - adapt to parser changes from https://github.com/vigoux/tree-sitter-vimdoc/pull/16 - numerous other generator improvements --- runtime/lua/vim/lsp/util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index dbc18963f9..aea2a27f9e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -907,8 +907,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers return end --The active signature. If omitted or the value lies outside the range of - --`signatures` the value defaults to zero or is ignored if `signatures.length - --=== 0`. Whenever possible implementors should make an active decision about + --`signatures` the value defaults to zero or is ignored if `signatures.length == 0`. + --Whenever possible implementors should make an active decision about --the active signature and shouldn't rely on a default value. local contents = {} local active_hl -- cgit From 94718e479da786cf75f9805470a6a108e8097dab Mon Sep 17 00:00:00 2001 From: Sergey Berezhnoy Date: Thu, 29 Sep 2022 17:25:07 +0300 Subject: fix(query): fix unnatural order for inherits in treesitter queries (#20298) close #20297 --- runtime/lua/vim/treesitter/query.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 1e46daaccf..7ca7384a88 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -125,11 +125,12 @@ function M.get_query_files(lang, query_name, is_included) io.close(file) end - local query_files = { base_query } + local query_files = {} for _, base_lang in ipairs(base_langs) do local base_files = M.get_query_files(base_lang, query_name, true) vim.list_extend(query_files, base_files) end + vim.list_extend(query_files, { base_query }) vim.list_extend(query_files, extensions) return query_files -- cgit From 33dd917d7fdc40483b3d18d0c7bcf5994b26fe86 Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Thu, 29 Sep 2022 16:26:19 +0200 Subject: fix(filetype): add missing return to changelog detection function (#20403) --- 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 d584d2659d..b61fc528f5 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1734,7 +1734,7 @@ local pattern = { { priority = -1 }, }, ['[cC]hange[lL]og.*'] = starsetf(function(path, bufnr) - require('vim.filetype.detect').changelog(bufnr) + return require('vim.filetype.detect').changelog(bufnr) end), ['.*%.%.ch'] = 'chill', ['.*%.cmake%.in'] = 'cmake', -- cgit From df646572c53f55268a5dbb61628d7c3b302d5663 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Fri, 30 Sep 2022 09:53:52 +0200 Subject: docs: fix typos (#20394) Co-authored-by: Raphael Co-authored-by: smjonas Co-authored-by: zeertzjq --- 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 aea2a27f9e..88667caf68 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -476,7 +476,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- If the replacement is over the end of a line (i.e. e.end_col is out of bounds and the -- replacement text ends with a newline We can likely assume that the replacement is assumed -- to be meant to replace the newline with another newline and we need to make sure this - -- doens't add an extra empty line. E.g. when the last line to be replaced contains a '\r' + -- doesn't add an extra empty line. E.g. when the last line to be replaced contains a '\r' -- in the file some servers (clangd on windows) will include that character in the line -- while nvim_buf_set_text doesn't count it as part of the line. if -- cgit From e54541f7f941427b408f8d5a7bab446c141b5f76 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 1 Oct 2022 11:35:36 +0200 Subject: refactor(lsp): remove deprecated lsp functions (#20421) --- runtime/lua/vim/lsp/buf.lua | 193 -------------------------------------------- 1 file changed, 193 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index b9aaacb437..8550fe253b 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -118,38 +118,6 @@ function M.completion(context) return request('textDocument/completion', params) end ----@private ---- If there is more than one client that supports the given method, ---- asks the user to select one. --- ----@returns The client that the user selected or nil -local function select_client(method, on_choice) - validate({ - on_choice = { on_choice, 'function', false }, - }) - local clients = vim.tbl_values(vim.lsp.buf_get_clients()) - clients = vim.tbl_filter(function(client) - return client.supports_method(method) - end, clients) - -- better UX when choices are always in the same order (between restarts) - table.sort(clients, function(a, b) - return a.name < b.name - end) - - if #clients > 1 then - vim.ui.select(clients, { - prompt = 'Select a language server:', - format_item = function(client) - return client.name - end, - }, on_choice) - elseif #clients < 1 then - on_choice(nil) - else - on_choice(clients[1]) - end -end - ---@private ---@return table {start={row, col}, end={row, col}} using (1, 0) indexing local function range_from_selection() @@ -283,139 +251,6 @@ function M.format(options) end end ---- Formats the current buffer. ---- ----@param options (table|nil) Can be used to specify FormattingOptions. ---- Some unspecified options will be automatically derived from the current ---- Neovim options. --- ----@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting -function M.formatting(options) - vim.notify_once( - 'vim.lsp.buf.formatting is deprecated. Use vim.lsp.buf.format { async = true } instead', - vim.log.levels.WARN - ) - local params = util.make_formatting_params(options) - local bufnr = api.nvim_get_current_buf() - select_client('textDocument/formatting', function(client) - if client == nil then - return - end - - return client.request('textDocument/formatting', params, nil, bufnr) - end) -end - ---- Performs |vim.lsp.buf.formatting()| synchronously. ---- ---- Useful for running on save, to make sure buffer is formatted prior to being ---- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|. Example: ---- ----
---- autocmd BufWritePre  lua vim.lsp.buf.formatting_sync()
---- 
---- ----@param options table|nil with valid `FormattingOptions` entries ----@param timeout_ms (number) Request timeout ----@see |vim.lsp.buf.format()| -function M.formatting_sync(options, timeout_ms) - vim.notify_once( - 'vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead', - vim.log.levels.WARN - ) - local params = util.make_formatting_params(options) - local bufnr = api.nvim_get_current_buf() - select_client('textDocument/formatting', function(client) - if client == nil then - return - end - - local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr) - if result and result.result then - util.apply_text_edits(result.result, bufnr, client.offset_encoding) - elseif err then - vim.notify('vim.lsp.buf.formatting_sync: ' .. err, vim.log.levels.WARN) - end - end) -end - ---- Formats the current buffer by sequentially requesting formatting from attached clients. ---- ---- Useful when multiple clients with formatting capability are attached. ---- ---- Since it's synchronous, can be used for running on save, to make sure buffer is formatted ---- prior to being saved. {timeout_ms} is passed on to the |vim.lsp.client| `request_sync` method. ---- Example: ----
---- vim.api.nvim_command[[autocmd BufWritePre  lua vim.lsp.buf.formatting_seq_sync()]]
---- 
---- ----@param options (table|nil) `FormattingOptions` entries ----@param timeout_ms (number|nil) Request timeout ----@param order (table|nil) List of client names. Formatting is requested from clients ----in the following order: first all clients that are not in the `order` list, then ----the remaining clients in the order as they occur in the `order` list. -function M.formatting_seq_sync(options, timeout_ms, order) - vim.notify_once( - 'vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead', - vim.log.levels.WARN - ) - local clients = vim.tbl_values(vim.lsp.buf_get_clients()) - local bufnr = api.nvim_get_current_buf() - - -- sort the clients according to `order` - for _, client_name in pairs(order or {}) do - -- if the client exists, move to the end of the list - for i, client in pairs(clients) do - if client.name == client_name then - table.insert(clients, table.remove(clients, i)) - break - end - end - end - - -- loop through the clients and make synchronous formatting requests - for _, client in pairs(clients) do - if vim.tbl_get(client.server_capabilities, 'documentFormattingProvider') then - local params = util.make_formatting_params(options) - local result, err = client.request_sync( - 'textDocument/formatting', - params, - timeout_ms, - api.nvim_get_current_buf() - ) - if result and result.result then - util.apply_text_edits(result.result, bufnr, client.offset_encoding) - elseif err then - vim.notify( - string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err), - vim.log.levels.WARN - ) - end - end - end -end - ---- Formats a given range. ---- ----@param options Table with valid `FormattingOptions` entries. ----@param start_pos ({number, number}, optional) mark-indexed position. ----Defaults to the start of the last visual selection. ----@param end_pos ({number, number}, optional) mark-indexed position. ----Defaults to the end of the last visual selection. -function M.range_formatting(options, start_pos, end_pos) - vim.deprecate('vim.lsp.buf.range_formatting', 'vim.lsp.formatexpr or vim.lsp.buf.format', '0.9.0') - local params = util.make_given_range_params(start_pos, end_pos) - params.options = util.make_formatting_params(options).options - select_client('textDocument/rangeFormatting', function(client) - if client == nil then - return - end - - return client.request('textDocument/rangeFormatting', params) - end) -end - --- Renames all references to the symbol under the cursor. --- ---@param new_name string|nil If not provided, the user will be prompted for a new @@ -943,34 +778,6 @@ function M.code_action(options) code_action_request(params, options) end ---- Performs |vim.lsp.buf.code_action()| for a given range. ---- ---- ----@param context table|nil `CodeActionContext` of the LSP specification: ---- - diagnostics: (table|nil) ---- LSP `Diagnostic[]`. Inferred from the current ---- position if not provided. ---- - only: (table|nil) ---- List of LSP `CodeActionKind`s used to filter the code actions. ---- Most language servers support values like `refactor` ---- or `quickfix`. ----@param start_pos ({number, number}, optional) mark-indexed position. ----Defaults to the start of the last visual selection. ----@param end_pos ({number, number}, optional) mark-indexed position. ----Defaults to the end of the last visual selection. -function M.range_code_action(context, start_pos, end_pos) - vim.deprecate('vim.lsp.buf.range_code_action', 'vim.lsp.buf.code_action', '0.9.0') - validate({ context = { context, 't', true } }) - context = context or {} - if not context.diagnostics then - local bufnr = api.nvim_get_current_buf() - context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) - end - local params = util.make_given_range_params(start_pos, end_pos) - params.context = context - code_action_request(params) -end - --- Executes an LSP server command. --- ---@param command_params table A valid `ExecuteCommandParams` object -- cgit From a5e347ce1df65be37c56e84c2a243eeaeaeeea5f Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 1 Oct 2022 23:36:11 +0800 Subject: refactor(diagnostic): remove deprecated function (#20423) --- runtime/lua/vim/diagnostic.lua | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 98dbe0779b..84091fbf0e 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -357,19 +357,6 @@ local function execute_scheduled_display(namespace, bufnr) M.show(namespace, bufnr, nil, args) end ---- @deprecated ---- Callback scheduled when leaving Insert mode. ---- ---- called from the Vimscript autocommand. ---- ---- See @ref schedule_display() ---- ----@private -function M._execute_scheduled_display(namespace, bufnr) - vim.deprecate('vim.diagnostic._execute_scheduled_display', nil, '0.9') - execute_scheduled_display(namespace, bufnr) -end - --- Table of autocmd events to fire the update for displaying new diagnostic information local insert_leave_auto_cmds = { 'InsertLeave', 'CursorHoldI' } -- cgit From b075f49d9229b3e58a4d6677ed8e01db60687fa3 Mon Sep 17 00:00:00 2001 From: August Masquelier <31262046+levouh@users.noreply.github.com> Date: Tue, 4 Oct 2022 12:44:19 -0600 Subject: feat(lsp): add bufnr option to lsp.start (#20473) --- runtime/lua/vim/lsp.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a64facf214..349c662258 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -860,6 +860,9 @@ end --- Used on all running clients. --- The default implementation re-uses a client if name --- and root_dir matches. +--- - bufnr (number) +--- Buffer handle to attach to if starting or re-using a +--- client (0 for current). ---@return number|nil client_id function lsp.start(config, opts) opts = opts or {} @@ -871,7 +874,10 @@ function lsp.start(config, opts) if not config.name and type(config.cmd) == 'table' then config.name = config.cmd[1] and vim.fs.basename(config.cmd[1]) or nil end - local bufnr = api.nvim_get_current_buf() + local bufnr = opts.bufnr + if bufnr == nil or bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end for _, clients in ipairs({ uninitialized_clients, lsp.get_active_clients() }) do for _, client in pairs(clients) do if reuse_client(client, config) then -- cgit From 548a4e258777a405cc2b1225cab9a8292924407b Mon Sep 17 00:00:00 2001 From: Elizabeth Paź Date: Wed, 5 Oct 2022 13:21:45 +0200 Subject: docs(docstrings): fix runtime type annotations --- runtime/lua/vim/F.lua | 8 ++++++-- runtime/lua/vim/_editor.lua | 25 ++++++++++++++----------- runtime/lua/vim/shared.lua | 4 +++- 3 files changed, 23 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua index bca5ddf68b..3e370c0a84 100644 --- a/runtime/lua/vim/F.lua +++ b/runtime/lua/vim/F.lua @@ -2,8 +2,12 @@ local F = {} --- Returns {a} if it is not nil, otherwise returns {b}. --- ----@param a ----@param b +---@generic A +---@generic B +--- +---@param a A +---@param b B +---@return A | B function F.if_nil(a, b) if a == nil then return b diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 96a87b8bb9..75df31004f 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -152,14 +152,15 @@ do --- --- ---@see |paste| + ---@alias paste_phase -1 | 1 | 2 | 3 --- - ---@param lines |readfile()|-style list of lines to paste. |channel-lines| - ---@param phase -1: "non-streaming" paste: the call contains all lines. + ---@param lines string[] # |readfile()|-style list of lines to paste. |channel-lines| + ---@param phase paste_phase -1: "non-streaming" paste: the call contains all lines. --- If paste is "streamed", `phase` indicates the stream state: --- - 1: starts the paste (exactly once) --- - 2: continues the paste (zero or more times) --- - 3: ends the paste (exactly once) - ---@returns false if client should cancel the paste. + ---@returns boolean # false if client should cancel the paste. function vim.paste(lines, phase) local now = vim.loop.now() local is_first_chunk = phase < 2 @@ -255,6 +256,8 @@ end ---@see |lua-loop-callbacks| ---@see |vim.schedule()| ---@see |vim.in_fast_event()| +---@param cb function +---@return function function vim.schedule_wrap(cb) return function(...) local args = vim.F.pack_len(...) @@ -399,11 +402,11 @@ end --- Get a table of lines with start, end columns for a region marked by two points --- ---@param bufnr number of buffer ----@param pos1 (line, column) tuple marking beginning of region ----@param pos2 (line, column) tuple marking end of region ----@param regtype type of selection, see |setreg()| +---@param pos1 integer[] (line, column) tuple marking beginning of region +---@param pos2 integer[] (line, column) tuple marking end of region +---@param regtype string type of selection, see |setreg()| ---@param inclusive boolean indicating whether the selection is end-inclusive ----@return region lua table of the form {linenr = {startcol,endcol}} +---@return table region lua table of the form {linenr = {startcol,endcol}} function vim.region(bufnr, pos1, pos2, regtype, inclusive) if not vim.api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) @@ -450,9 +453,9 @@ end --- Use to do a one-shot timer that calls `fn` --- Note: The {fn} is |vim.schedule_wrap()|ped automatically, so API functions are --- safe to call. ----@param fn Callback to call once `timeout` expires ----@param timeout Number of milliseconds to wait before calling `fn` ----@return timer luv timer object +---@param fn function Callback to call once `timeout` expires +---@param timeout integer Number of milliseconds to wait before calling `fn` +---@return table timer luv timer object function vim.defer_fn(fn, timeout) vim.validate({ fn = { fn, 'c', true } }) local timer = vim.loop.new_timer() @@ -758,7 +761,7 @@ end --- local hl_normal = vim.pretty_print(vim.api.nvim_get_hl_by_name("Normal", true)) --- ---@see |vim.inspect()| ----@return given arguments. +---@return any # given arguments. function vim.pretty_print(...) local objects = {} for i = 1, select('#', ...) do diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index c5c31b6ddf..63a932479e 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -107,9 +107,11 @@ end --- ---@see |vim.gsplit()| --- +---@alias split_kwargs {plain: boolean, trimempty: boolean} | boolean | nil +--- ---@param s string String to split ---@param sep string Separator or pattern ----@param kwargs table Keyword arguments: +---@param kwargs split_kwargs Keyword arguments: --- - plain: (boolean) If `true` use `sep` literally (passed to string.find) --- - trimempty: (boolean) If `true` remove empty items from the front --- and back of the list -- cgit From c8d1b9a2d6ef910a9c1993875723387d522e6c4a Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Tue, 27 Sep 2022 22:44:01 +0200 Subject: docs: added proper annotations to functions in shared.lua --- runtime/lua/vim/shared.lua | 55 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 23 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 63a932479e..1d0a6d5406 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -14,8 +14,9 @@ local vim = vim or {} --- same functions as those in the input table. Userdata and threads are not --- copied and will throw an error. --- ----@param orig table Table to copy ----@return table Table of copied keys and (nested) values. +---@generic T: table +---@param orig T Table to copy +---@return T Table of copied keys and (nested) values. function vim.deepcopy(orig) end -- luacheck: no unused vim.deepcopy = (function() local function _id(v) @@ -62,7 +63,7 @@ end)() ---@param s string String to split ---@param sep string Separator or pattern ---@param plain boolean If `true` use `sep` literally (passed to string.find) ----@return function Iterator over the split components +---@return fun():string Iterator over the split components function vim.gsplit(s, sep, plain) vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } }) @@ -111,11 +112,11 @@ end --- ---@param s string String to split ---@param sep string Separator or pattern ----@param kwargs split_kwargs Keyword arguments: +---@param kwargs {plain: boolean, trimempty: boolean}|nil Keyword arguments: --- - plain: (boolean) If `true` use `sep` literally (passed to string.find) --- - trimempty: (boolean) If `true` remove empty items from the front --- and back of the list ----@return table List of split components +---@return string[] List of split components function vim.split(s, sep, kwargs) local plain local trimempty = false @@ -158,8 +159,9 @@ end --- ---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua --- ----@param t table Table ----@return table List of keys +---@param t table Table +---@generic T: table +---@return T[] List of keys function vim.tbl_keys(t) assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) @@ -173,8 +175,9 @@ end --- Return a list of all values used in a table. --- However, the order of the return table of values is not guaranteed. --- ----@param t table Table ----@return table List of values +---@generic T +---@param t table Table +---@return T[] List of values function vim.tbl_values(t) assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) @@ -187,8 +190,9 @@ end --- Apply a function to all values of a table. --- ----@param func function|table Function or callable table ----@param t table Table +---@generic T +---@param func fun(value: T): any Function +---@param t table Table ---@return table Table of transformed values function vim.tbl_map(func, t) vim.validate({ func = { func, 'c' }, t = { t, 't' } }) @@ -202,9 +206,10 @@ end --- Filter a table using a predicate function --- ----@param func function|table Function or callable table ----@param t table Table ----@return table Table of filtered values +---@generic T +---@param func fun(value: T): boolean Function +---@param t table Table +---@return T[] Table of filtered values function vim.tbl_filter(func, t) vim.validate({ func = { func, 'c' }, t = { t, 't' } }) @@ -306,12 +311,14 @@ end --- ---@see |vim.tbl_extend()| --- ----@param behavior string Decides what to do if a key is found in more than one map: +---@generic T1: table +---@generic T2: table +---@param behavior "error"|"keep"|"force" Decides what to do if a key is found in more than one map: --- - "error": raise an error --- - "keep": use value from the leftmost map --- - "force": use value from the rightmost map ----@param ... table Two or more map-like tables ----@return table Merged table +---@param ... T2 Two or more map-like tables +---@return T1|T2 Merged table function vim.tbl_deep_extend(behavior, ...) return tbl_extend(behavior, true, ...) end @@ -407,11 +414,12 @@ end --- ---@see |vim.tbl_extend()| --- ----@param dst table List which will be modified and appended to +---@generic T: table +---@param dst T List which will be modified and appended to ---@param src table List from which values will be inserted ----@param start number Start index on src. Defaults to 1 ----@param finish number Final index on src. Defaults to `#src` ----@return table dst +---@param start number|nil Start index on src. Defaults to 1 +---@param finish number|nil Final index on src. Defaults to `#src` +---@return T dst function vim.list_extend(dst, src, start, finish) vim.validate({ dst = { dst, 't' }, @@ -506,10 +514,11 @@ end --- Creates a copy of a table containing only elements from start to end (inclusive) --- ----@param list table Table +---@generic T +---@param list T[] Table ---@param start number Start range of slice ---@param finish number End range of slice ----@return table Copy of table sliced from start to finish (inclusive) +---@return T[] Copy of table sliced from start to finish (inclusive) function vim.list_slice(list, start, finish) local new_list = {} for i = start or 1, finish or #list do -- cgit From 24a1c7f556bba35a9c31c2fdd19cf4b8c00a4395 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 28 Sep 2022 09:11:21 +0200 Subject: feat: added support for optional params to lua2dox --- runtime/lua/vim/shared.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 1d0a6d5406..5566bd5e35 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -417,8 +417,8 @@ end ---@generic T: table ---@param dst T List which will be modified and appended to ---@param src table List from which values will be inserted ----@param start number|nil Start index on src. Defaults to 1 ----@param finish number|nil Final index on src. Defaults to `#src` +---@param start? number Start index on src. Defaults to 1 +---@param finish? number Final index on src. Defaults to `#src` ---@return T dst function vim.list_extend(dst, src, start, finish) vim.validate({ -- cgit From 1da7b4eb699fb04cc97dec389470fd0fbd64091d Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 28 Sep 2022 13:22:08 +0200 Subject: feat: added support for specifying types for lua2dox --- runtime/lua/vim/shared.lua | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 5566bd5e35..fa2af57cfc 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -63,7 +63,7 @@ end)() ---@param s string String to split ---@param sep string Separator or pattern ---@param plain boolean If `true` use `sep` literally (passed to string.find) ----@return fun():string Iterator over the split components +---@return fun():string (function) Iterator over the split components function vim.gsplit(s, sep, plain) vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } }) @@ -112,7 +112,7 @@ end --- ---@param s string String to split ---@param sep string Separator or pattern ----@param kwargs {plain: boolean, trimempty: boolean}|nil Keyword arguments: +---@param kwargs? {plain: boolean, trimempty: boolean} (table|nil) Keyword arguments: --- - plain: (boolean) If `true` use `sep` literally (passed to string.find) --- - trimempty: (boolean) If `true` remove empty items from the front --- and back of the list @@ -159,9 +159,9 @@ end --- ---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua --- ----@param t table Table +---@param t table (table) Table ---@generic T: table ----@return T[] List of keys +---@return T[] (list) List of keys function vim.tbl_keys(t) assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) @@ -176,8 +176,8 @@ end --- However, the order of the return table of values is not guaranteed. --- ---@generic T ----@param t table Table ----@return T[] List of values +---@param t table (table) Table +---@return T[] (list) List of values function vim.tbl_values(t) assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) @@ -191,8 +191,8 @@ end --- Apply a function to all values of a table. --- ---@generic T ----@param func fun(value: T): any Function ----@param t table Table +---@param func fun(value: T): any (function) Function +---@param t table (table) Table ---@return table Table of transformed values function vim.tbl_map(func, t) vim.validate({ func = { func, 'c' }, t = { t, 't' } }) @@ -207,9 +207,9 @@ end --- Filter a table using a predicate function --- ---@generic T ----@param func fun(value: T): boolean Function ----@param t table Table ----@return T[] Table of filtered values +---@param func fun(value: T): boolean (function) Function +---@param t table (table) Table +---@return T[] (table) Table of filtered values function vim.tbl_filter(func, t) vim.validate({ func = { func, 'c' }, t = { t, 't' } }) @@ -313,12 +313,12 @@ end --- ---@generic T1: table ---@generic T2: table ----@param behavior "error"|"keep"|"force" Decides what to do if a key is found in more than one map: +---@param behavior "error"|"keep"|"force" (string) Decides what to do if a key is found in more than one map: --- - "error": raise an error --- - "keep": use value from the leftmost map --- - "force": use value from the rightmost map ---@param ... T2 Two or more map-like tables ----@return T1|T2 Merged table +---@return T1|T2 (table) Merged table function vim.tbl_deep_extend(behavior, ...) return tbl_extend(behavior, true, ...) end @@ -515,10 +515,10 @@ end --- Creates a copy of a table containing only elements from start to end (inclusive) --- ---@generic T ----@param list T[] Table +---@param list T[] (list) Table ---@param start number Start range of slice ---@param finish number End range of slice ----@return T[] Copy of table sliced from start to finish (inclusive) +---@return T[] (list) Copy of table sliced from start to finish (inclusive) function vim.list_slice(list, start, finish) local new_list = {} for i = start or 1, finish or #list do -- cgit From 0773a9ee3a21db54cd6b2376dd2e087bc09d5ea1 Mon Sep 17 00:00:00 2001 From: lvimuser <109605931+lvimuser@users.noreply.github.com> Date: Sat, 8 Oct 2022 05:22:25 -0300 Subject: feat(lsp): support window/showDocument (#19977) --- runtime/lua/vim/lsp/handlers.lua | 46 ++++++++++++++++++++ runtime/lua/vim/lsp/protocol.lua | 2 +- runtime/lua/vim/lsp/util.lua | 93 ++++++++++++++++++++++++++++------------ 3 files changed, 113 insertions(+), 28 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index a32c59dd17..d7d9ca7ce9 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -512,6 +512,52 @@ M['window/showMessage'] = function(_, result, ctx, _) return result end +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showDocument +M['window/showDocument'] = function(_, result, ctx, _) + local uri = result.uri + + if result.external then + -- TODO(lvimuser): ask the user for confirmation + local cmd + if vim.fn.has('win32') == 1 then + cmd = { 'cmd.exe', '/c', 'start', '""', vim.fn.shellescape(uri) } + elseif vim.fn.has('macunix') == 1 then + cmd = { 'open', vim.fn.shellescape(uri) } + else + cmd = { 'xdg-open', vim.fn.shellescape(uri) } + end + + local ret = vim.fn.system(cmd) + if vim.v.shellerror ~= 0 then + return { + success = false, + error = { + code = protocol.ErrorCodes.UnknownErrorCode, + message = ret, + }, + } + end + + return { success = true } + end + + local client_id = ctx.client_id + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format('id=%d', client_id) + if not client then + err_message({ 'LSP[', client_name, '] client has shut down after sending ', ctx.method }) + return vim.NIL + end + + local location = { + uri = uri, + range = result.selection, + } + + local success = util.show_document(location, client.offset_encoding, true, result.takeFocus) + return { success = success or false } +end + -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do M[k] = function(err, result, ctx, config) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 27da60b4ae..4034753322 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -778,7 +778,7 @@ function protocol.make_client_capabilities() }, }, showDocument = { - support = false, + support = true, }, }, } diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 88667caf68..617d33f88c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -110,6 +110,15 @@ local function split_lines(value) return split(value, '\n', true) end +---@private +local function create_window_without_focus() + local prev = vim.api.nvim_get_current_win() + vim.cmd.new() + local new = vim.api.nvim_get_current_win() + vim.api.nvim_set_current_win(prev) + return new +end + --- Convert byte index to `encoding` index. --- Convenience wrapper around vim.str_utfindex ---@param line string line to be indexed @@ -1056,50 +1065,80 @@ function M.make_floating_popup_options(width, height, opts) } end ---- Jumps to a location. +--- Shows document and optionally jumps to the location. --- ---@param location table (`Location`|`LocationLink`) ----@param offset_encoding string utf-8|utf-16|utf-32 (required) ----@param reuse_win boolean Jump to existing window if buffer is already opened. ----@returns `true` if the jump succeeded -function M.jump_to_location(location, offset_encoding, reuse_win) +---@param offset_encoding "utf-8" | "utf-16" | "utf-32" +---@param opts table options +--- - reuse_win (boolean) Jump to existing window if buffer is already open. +--- - focus (boolean) Whether to focus/jump to location if possible. Defaults to true. +---@return boolean `true` if succeeded +function M.show_document(location, offset_encoding, opts) -- location may be Location or LocationLink local uri = location.uri or location.targetUri if uri == nil then - return + return false end if offset_encoding == nil then - vim.notify_once( - 'jump_to_location must be called with valid offset encoding', - vim.log.levels.WARN - ) + vim.notify_once('show_document must be called with valid offset encoding', vim.log.levels.WARN) end local bufnr = vim.uri_to_bufnr(uri) - -- Save position in jumplist - vim.cmd("normal! m'") - -- Push a new item into tagstack - local from = { vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0 } - local items = { { tagname = vim.fn.expand(''), from = from } } - vim.fn.settagstack(vim.fn.win_getid(), { items = items }, 't') + opts = opts or {} + local focus = vim.F.if_nil(opts.focus, true) + if focus then + -- Save position in jumplist + vim.cmd("normal! m'") - --- Jump to new location (adjusting for UTF-16 encoding of characters) - local win = reuse_win and bufwinid(bufnr) - if win then + -- Push a new item into tagstack + local from = { vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0 } + local items = { { tagname = vim.fn.expand(''), from = from } } + vim.fn.settagstack(vim.fn.win_getid(), { items = items }, 't') + end + + local win = opts.reuse_win and bufwinid(bufnr) + or focus and api.nvim_get_current_win() + or create_window_without_focus() + + api.nvim_buf_set_option(bufnr, 'buflisted', true) + api.nvim_win_set_buf(win, bufnr) + if focus then api.nvim_set_current_win(win) - else - api.nvim_buf_set_option(bufnr, 'buflisted', true) - api.nvim_set_current_buf(bufnr) end + + -- location may be Location or LocationLink local range = location.range or location.targetSelectionRange - local row = range.start.line - local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) - api.nvim_win_set_cursor(0, { row + 1, col }) - -- Open folds under the cursor - vim.cmd('normal! zv') + 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) + api.nvim_win_set_cursor(win, { row + 1, col }) + api.nvim_win_call(win, function() + -- Open folds under the cursor + vim.cmd('normal! zv') + end) + end + return true end +--- Jumps to a location. +--- +---@param location table (`Location`|`LocationLink`) +---@param offset_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) + if offset_encoding == nil then + vim.notify_once( + 'jump_to_location must be called with valid offset encoding', + vim.log.levels.WARN + ) + end + + return M.show_document(location, offset_encoding, { reuse_win = reuse_win, focus = true }) +end + --- Previews a location in a floating window --- --- behavior depends on type of location: -- cgit From b05e10ef7265c257be79e6888cbf87bb6ba86845 Mon Sep 17 00:00:00 2001 From: ObserverOfTime Date: Sat, 8 Oct 2022 18:46:31 +0300 Subject: vim-patch:9.0.0692: PoE filter files are not recognized (#20542) Problem: PoE filter files are not recognized. Solution: Add a pattern to detect PoE filter files. (closes vim/vim#11305) https://github.com/vim/vim/commit/b7f52f5659c68b61ccc645ef866f8fd82361cd26 --- 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 b61fc528f5..36ea97fb21 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -774,6 +774,7 @@ local extension = { po = 'po', pot = 'po', pod = 'pod', + filter = 'poefilter', pk = 'poke', ps = 'postscr', epsi = 'postscr', -- cgit From 8c2226fc30931690186390d86f963cd43e6947ef Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Sun, 9 Oct 2022 12:40:56 +0200 Subject: fix(lua): properly configure luacheck and remove `local vim = ...` lines (#20551) --- runtime/lua/vim/_editor.lua | 2 -- runtime/lua/vim/_init_packages.lua | 3 --- runtime/lua/vim/_meta.lua | 3 --- runtime/lua/vim/lsp.lua | 1 - runtime/lua/vim/lsp/buf.lua | 1 - runtime/lua/vim/lsp/handlers.lua | 1 - runtime/lua/vim/lsp/rpc.lua | 1 - runtime/lua/vim/lsp/util.lua | 1 - runtime/lua/vim/shared.lua | 2 +- 9 files changed, 1 insertion(+), 14 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 75df31004f..ef1a07b267 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -36,8 +36,6 @@ -- - https://github.com/bakpakin/Fennel (pretty print, repl) -- - https://github.com/howl-editor/howl/tree/master/lib/howl/util -local vim = assert(vim) - -- These are for loading runtime modules lazily since they aren't available in -- the nvim binary as specified in executor.c for k, v in pairs({ diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua index 7e3c73667e..19c8608732 100644 --- a/runtime/lua/vim/_init_packages.lua +++ b/runtime/lua/vim/_init_packages.lua @@ -1,6 +1,3 @@ --- prevents luacheck from making lints for setting things on vim -local vim = assert(vim) - local pathtrails = {} vim._so_trails = {} for s in (package.cpath .. ';'):gmatch('[^;]*;') do diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index dada918d69..9c7972873e 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -1,6 +1,3 @@ --- prevents luacheck from making lints for setting things on vim -local vim = assert(vim) - local a = vim.api -- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded. diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 349c662258..199964e24e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -5,7 +5,6 @@ local protocol = require('vim.lsp.protocol') local util = require('vim.lsp.util') local sync = require('vim.lsp.sync') -local vim = vim local api = vim.api local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option, nvim_exec_autocmds = api.nvim_err_writeln, diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8550fe253b..c593e72d62 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1,4 +1,3 @@ -local vim = vim local api = vim.api local validate = vim.validate local util = require('vim.lsp.util') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index d7d9ca7ce9..93fd621161 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -1,7 +1,6 @@ local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') local util = require('vim.lsp.util') -local vim = vim local api = vim.api local M = {} diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 842a0ce329..ff62623544 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -1,4 +1,3 @@ -local vim = vim local uv = vim.loop local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 617d33f88c..b0f9c1660e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1,6 +1,5 @@ local protocol = require('vim.lsp.protocol') local snippet = require('vim.lsp._snippet') -local vim = vim local validate = vim.validate local api = vim.api local list_extend = vim.list_extend diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index fa2af57cfc..4b3a681eb2 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -6,7 +6,7 @@ -- or the test suite. (Eventually the test suite will be run in a worker process, -- so this wouldn't be a separate case to consider) -local vim = vim or {} +vim = vim or {} --- Returns a deep copy of the given object. Non-table objects are copied as --- in a typical Lua assignment, whereas table objects are copied recursively. -- cgit From 09dffb9db7d16496e55e86f78ab60241533d86f6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 9 Oct 2022 08:21:52 -0400 Subject: docs: various #12823 - increase python line-length limit from 88 => 100. - gen_help_html: fix bug in "tag" case (tbl_count => tbl_contains) ref #15632 fix #18215 fix #18479 fix #20527 fix #20532 Co-authored-by: Ben Weedon --- runtime/lua/vim/_editor.lua | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index ef1a07b267..28e5897aa9 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -12,21 +12,8 @@ -- Guideline: "If in doubt, put it in the runtime". -- -- Most functions should live directly in `vim.`, not in submodules. --- The only "forbidden" names are those claimed by legacy `if_lua`: --- $ vim --- :lua for k,v in pairs(vim) do print(k) end --- buffer --- open --- window --- lastline --- firstline --- type --- line --- eval --- dict --- beep --- list --- command +-- +-- Compatibility with Vim's `if_lua` is explicitly a non-goal. -- -- Reference (#6580): -- - https://github.com/luafun/luafun @@ -120,9 +107,7 @@ function vim._os_proc_children(ppid) return children end --- TODO(ZyX-I): Create compatibility layer. - ---- Return a human-readable representation of the given object. +--- Gets a human-readable representation of the given object. --- ---@see https://github.com/kikito/inspect.lua ---@see https://github.com/mpeterv/vinspect -- cgit From a7a83bc4c25d63f3ae0a7a56e5211df1444699c4 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 9 Oct 2022 18:19:43 +0200 Subject: fix(docs-html): update parser - Improve generated HTML by updating parser which includes fixes for single "'" and single "|": https://github.com/neovim/tree-sitter-vimdoc/pull/31 - Updated parser also fixes the conceal issue for "help" highlight queries https://github.com/neovim/tree-sitter-vimdoc/issues/23 by NOT including whitespace in nodes. - But this means we need to restore the getws() function which scrapes leading whitespace from the original input (buffer). --- runtime/lua/vim/shared.lua | 8 ++++---- 1 file 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 4b3a681eb2..f03d608e56 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -100,10 +100,10 @@ end --- --- Examples: ---
----  split(":aa::b:", ":")     --> {'','aa','','b',''}
----  split("axaby", "ab?")     --> {'','x','y'}
----  split("x*yz*o", "*", {plain=true})  --> {'x','yz','o'}
----  split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
+---  split(":aa::b:", ":")     => {'','aa','','b',''}
+---  split("axaby", "ab?")     => {'','x','y'}
+---  split("x*yz*o", "*", {plain=true})  => {'x','yz','o'}
+---  split("|x|y|z|", "|", {trimempty=true}) => {'x', 'y', 'z'}
 --- 
--- ---@see |vim.gsplit()| -- cgit From 8781213f00a22e20abeb4282204e900db799f4b5 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 10 Oct 2022 17:51:31 +0200 Subject: vim-patch:9.0.0711: SubStation Alpha files are not recognized (#20577) Problem: SubStation Alpha files are not recognized. Solution: Add patterns for SubStation Alpha files. (closes vim/vim#11332) https://github.com/vim/vim/commit/084f2620ec7d08d6043de30436197c002fffe3ec --- 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 36ea97fb21..e204fe8ae2 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -970,6 +970,8 @@ local extension = { mot = 'srec', ['s19'] = 'srec', srt = 'srt', + ssa = 'ssa', + ass = 'ssa', st = 'st', imata = 'stata', ['do'] = 'stata', -- cgit From 4f305fba14ec8c1919ed793b6e09e15ca54a8c1d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 12 Oct 2022 15:23:42 +0200 Subject: vim-patch:9.0.0731: clang-tidy configuration files are not recognized (#20620) Problem: clang-tidy configuration files are not recognized. Solution: Recognize clang-tidy files as yaml. (closes vim/vim#11350) https://github.com/vim/vim/commit/af40f9af335e0c8b167eac31ceace45b6a2e0565 --- 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 e204fe8ae2..ea39f66393 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1666,6 +1666,7 @@ local filename = { fglrxrc = 'xml', ['/etc/blkid.tab'] = 'xml', ['/etc/blkid.tab.old'] = 'xml', + ['.clang-tidy'] = 'yaml', ['/etc/zprofile'] = 'zsh', ['.zlogin'] = 'zsh', ['.zlogout'] = 'zsh', -- cgit From 81986a7349da7b88abde459194078e9893e8ae8b Mon Sep 17 00:00:00 2001 From: Daniel Zhang Date: Fri, 14 Oct 2022 17:12:46 +0800 Subject: fix(lua): on_yank error with blockwise multibyte region #20162 Prevent out of range error when calling `str_byteindex`. Use `vim.str_byteindex(bufline, #bufline)` to cacluate utf length of `bufline`. fix #20161 --- runtime/lua/vim/_editor.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 28e5897aa9..0f312f19f5 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -416,11 +416,16 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) c2 = c1 + regtype:sub(2) -- and adjust for non-ASCII characters bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1] - if c1 < #bufline then + local utflen = vim.str_utfindex(bufline, #bufline) + if c1 <= utflen then c1 = vim.str_byteindex(bufline, c1) + else + c1 = #bufline + 1 end - if c2 < #bufline then + if c2 <= utflen then c2 = vim.str_byteindex(bufline, c2) + else + c2 = #bufline + 1 end else c1 = (l == pos1[1]) and pos1[2] or 0 -- cgit From e5cb3104d07228de4f2614c425355e8f2f99507d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 14 Oct 2022 11:01:13 -0400 Subject: docs: fix/remove invalid URLs #20647 --- runtime/lua/vim/lsp.lua | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 199964e24e..d717275ae4 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -824,23 +824,16 @@ end --- --- See |vim.lsp.start_client()| for all available options. The most important are: --- ---- `name` is an arbitrary name for the LSP client. It should be unique per ---- language server. ---- ---- `cmd` the command as list - used to start the language server. ---- The command must be present in the `$PATH` environment variable or an ---- absolute path to the executable. Shell constructs like `~` are *NOT* expanded. ---- ---- `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.find()| and |vim.fs.dirname()| to detect the ---- root by traversing the file system upwards starting ---- from the current directory until either a `pyproject.toml` or `setup.py` ---- file is found. ---- ---- `workspace_folders` a list of { uri:string, name: string } tables. ---- The project root folders used by the language server. ---- If `nil` the property is derived from the `root_dir` for convenience. +--- - `name` arbitrary name for the LSP client. Should be unique per language server. +--- - `cmd` command (in list form) used to start the language server. Must be absolute, or found on +--- `$PATH`. Shell constructs like `~` are not expanded. +--- - `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.find()| and |vim.fs.dirname()| to detect the +--- root by traversing the file system upwards starting from the current directory until either +--- a `pyproject.toml` or `setup.py` file is found. +--- - `workspace_folders` list of `{ uri:string, name: string }` tables specifying the project root +--- folders used by the language server. If `nil` the property is derived from `root_dir` for +--- convenience. --- --- Language servers use this information to discover metadata like the --- dependencies of your project and they tend to index the contents within the -- cgit From e26b48bde6e116eb288893454d2876cdad2db1f9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 15 Oct 2022 10:12:25 +0200 Subject: vim-patch:9.0.0752: Rprofile files are not recognized (#20658) Problem: Rprofile files are not recognized. Solution: Recognize Rprofile files as "r". (closes vim/vim#11369) https://github.com/vim/vim/commit/7e120ffccbf81ae8acac28f11fbd5eab79a1630d --- 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 ea39f66393..d2840b3a7e 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1540,6 +1540,9 @@ local filename = { ['.pythonstartup'] = 'python', ['.pythonrc'] = 'python', SConstruct = 'python', + ['.Rprofile'] = 'r', + ['Rprofile'] = 'r', + ['Rprofile.site'] = 'r', ratpoisonrc = 'ratpoison', ['.ratpoisonrc'] = 'ratpoison', inputrc = 'readline', -- cgit From 8f31a730c0dd76180648ba3b7c94aa5d059432e5 Mon Sep 17 00:00:00 2001 From: David Hotham Date: Sun, 16 Oct 2022 23:24:39 +0100 Subject: fix(lsp): reporting bogus capabilities in CodeActionKind #20678 Problem: LSP client provides bogus capabilities in CodeActionKind. LSP logs show this in the "initialize" message: codeActionKind = { valueSet = { "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" } Solution: Only the values from the CodeActionKind table should be presented, not also the keys. fix #20657 --- runtime/lua/vim/lsp/protocol.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 4034753322..7442c8f005 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -637,7 +637,7 @@ function protocol.make_client_capabilities() codeActionLiteralSupport = { codeActionKind = { valueSet = (function() - local res = vim.tbl_values(protocol.CodeActionKind) + local res = vim.tbl_values(constants.CodeActionKind) table.sort(res) return res end)(), -- cgit From d44f088834081ee404db4459fdcfba82d14ef157 Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Mon, 17 Oct 2022 08:18:57 +0200 Subject: vim-patch:9.0.0771: cannot always tell the difference beween tex and … (#20687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vim-patch:9.0.0771: cannot always tell the difference beween tex and rexx files Problem: Cannot always tell the difference beween tex and rexx files. Solution: Recognize tex by a leading backslash. (Martin Tournoij, closes vim/vim#11380) https://github.com/vim/vim/commit/bd053f894b0d7652928201faa68c53d1ce2acdc5 --- 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 7fc7f1b7ca..23fa1c5068 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -181,7 +181,7 @@ function M.cls(bufnr) return vim.g.filetype_cls end local line = getlines(bufnr, 1) - if line:find('^%%') then + if line:find('^[%%\\]') then return 'tex' elseif line:find('^#') and line:lower():find('rexx') then return 'rexx' -- cgit From 042eb74ff1ed63d79f8a642649cd6be6ec4b0eb9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 17 Oct 2022 08:52:40 +0200 Subject: feat(runtime)!: remove filetype.vim (#20428) Made obsolete by now graduated `filetype.lua` (enabled by default). Note that changes or additions to the filetype detection still need to be made through a PR to vim/vim as we port the _logic_ as well as tests. --- runtime/lua/vim/filetype.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index d2840b3a7e..fa792422ba 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2285,8 +2285,6 @@ end --- --- See $VIMRUNTIME/lua/vim/filetype.lua for more examples. --- ---- Note that Lua filetype detection is disabled when |g:do_legacy_filetype| is set. ---- --- Example: ---
 ---  vim.filetype.add({
@@ -2323,7 +2321,7 @@ end
 ---  })
 --- 
--- ---- To add a fallback match on contents (see |new-filetype-scripts|), use +--- To add a fallback match on contents, use ---
 --- vim.filetype.add {
 ---   pattern = {
-- 
cgit 


From e4273135455084bca54a484f88fd364af62bf69c Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Mon, 17 Oct 2022 18:36:10 +0200
Subject: vim-patch:9.0.0782: OpenVPN files are not recognized (#20702)

Problem:    OpenVPN files are not recognized.
Solution:   Add patterns for OpenVPN files. (closes vim/vim#11391)
https://github.com/vim/vim/commit/4bf67ec52e938a3edaa4f452adab42a57505f940
---
 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 fa792422ba..28d13cf270 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -735,6 +735,7 @@ local extension = {
   opam = 'opam',
   ['or'] = 'openroad',
   scad = 'openscad',
+  ovpn = 'openvpn',
   ora = 'ora',
   org = 'org',
   org_archive = 'org',
@@ -2043,6 +2044,7 @@ local pattern = {
   ['.*%.ml%.cppo'] = 'ocaml',
   ['.*%.mli%.cppo'] = 'ocaml',
   ['.*%.opam%.template'] = 'opam',
+  ['.*/openvpn/.*/.*%.conf'] = 'openvpn',
   ['.*%.[Oo][Pp][Ll]'] = 'opl',
   ['.*/etc/pam%.conf'] = 'pamconf',
   ['.*/etc/pam%.d/.*'] = starsetf('pamconf'),
-- 
cgit 


From 228a04070e94ca31884f304f3a8d34e67654025d Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Tue, 18 Oct 2022 20:05:50 +0200
Subject: vim-patch:9.0.0779: lsl and lm3 file extensions are not recognized
 (#20704)

Problem:    lsl and lm3 file extensions are not recognized.
Solution:   Add *.lsl and *.lm3 patterns. (Doug Kearns, closes vim/vim#11384)
https://github.com/vim/vim/commit/4ac8e7948cb3e07bc4598ede8b274891d14dfa7c
---
 runtime/lua/vim/filetype.lua        |  8 +++++++-
 runtime/lua/vim/filetype/detect.lua | 13 +++++++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 28d13cf270..7dd1d7cd25 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -611,7 +611,9 @@ local extension = {
   c = function(path, bufnr)
     return require('vim.filetype.detect').lpc(bufnr)
   end,
-  lsl = 'lsl',
+  lsl = function(path, bufnr)
+    return require('vim.filetype.detect').lsl(bufnr)
+  end,
   lss = 'lss',
   nse = 'lua',
   rockspec = 'lua',
@@ -674,6 +676,7 @@ local extension = {
   DEF = 'modula2',
   ['m2'] = 'modula2',
   mi = 'modula2',
+  lm3 = 'modula3',
   ssc = 'monk',
   monk = 'monk',
   tsc = 'monk',
@@ -1459,6 +1462,9 @@ local filename = {
   ['.sawfishrc'] = 'lisp',
   ['/etc/login.access'] = 'loginaccess',
   ['/etc/login.defs'] = 'logindefs',
+  ['.lsl'] = function(path, bufnr)
+    return require('vim.filetype.detect').lsl(bufnr)
+  end,
   ['.luacheckrc'] = 'lua',
   ['lynx.cfg'] = 'lynx',
   ['m3overrides'] = 'm3build',
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 23fa1c5068..51ea1971cb 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -634,6 +634,19 @@ function M.lpc(bufnr)
   return 'c'
 end
 
+function M.lsl(bufnr)
+  if vim.g.filetype_lsl then
+    return vim.g.filetype_lsl
+  end
+
+  local line = nextnonblank(bufnr, 1)
+  if findany(line, { '^%s*%%', ':%s*trait%s*$' }) then
+    return 'larch'
+  else
+    return 'lsl'
+  end
+end
+
 function M.m(bufnr)
   if vim.g.filetype_m then
     return vim.g.filetype_m
-- 
cgit 


From fad558b6affd54075654dd55922348f76a95e338 Mon Sep 17 00:00:00 2001
From: ObserverOfTime 
Date: Wed, 19 Oct 2022 20:08:01 +0300
Subject: vim-patch:9.0.0798: clang format configuration files are not
 recognized (#20741)

Problem:    Clang format configuration files are not recognized.
Solution:   Use yaml for Clang format configuration files. (Marwin Glaser,
            closes vim/vim#11398)
https://github.com/vim/vim/commit/3c708c43908ba44f075bbaa7daf584c6b46d9723
---
 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 7dd1d7cd25..af2617677e 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1676,6 +1676,7 @@ local filename = {
   fglrxrc = 'xml',
   ['/etc/blkid.tab'] = 'xml',
   ['/etc/blkid.tab.old'] = 'xml',
+  ['.clang-format'] = 'yaml',
   ['.clang-tidy'] = 'yaml',
   ['/etc/zprofile'] = 'zsh',
   ['.zlogin'] = 'zsh',
-- 
cgit 


From 45ae5c6dc5af63957eb2009e0425d91a3cc79d66 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Thu, 20 Oct 2022 22:49:22 +0200
Subject: vim-patch:9.0.0808: jsonnet filetype detection has a typo (#20753)

Problem:    jsonnet filetype detection has a typo.
Solution:   Change "libjsonnet" to "libsonnet". (Maxime Brunet, closes vim/vim#11412)
https://github.com/vim/vim/commit/6c8bc37a1083d17447156592f6f52da2d40b4855
---
 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 af2617677e..a574ab1942 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -564,7 +564,7 @@ local extension = {
   json5 = 'json5',
   jsonc = 'jsonc',
   jsonnet = 'jsonnet',
-  libjsonnet = 'jsonnet',
+  libsonnet = 'jsonnet',
   jsp = 'jsp',
   jl = 'julia',
   kv = 'kivy',
-- 
cgit 


From e554f5c545b9ee1195d617b3ac21f24829330a31 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 21 Oct 2022 14:04:53 +0200
Subject: fix(filetype): don't pass empty string to detect (#20766)

Problem: `*.db` files use empty string as default filetype, which is
inconsistent with the rest of the code which uses `nil` instead.
Solution: don't pass a default empty string
---
 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 a574ab1942..2c32f9bf52 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -170,7 +170,7 @@ local extension = {
     return require('vim.filetype.detect').bindzone(bufnr, 'dcl')
   end,
   db = function(path, bufnr)
-    return require('vim.filetype.detect').bindzone(bufnr, '')
+    return require('vim.filetype.detect').bindzone(bufnr)
   end,
   bicep = 'bicep',
   bb = 'bitbake',
-- 
cgit 


From 837190720310deca0231fc42aa3023957ff79a3a Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Fri, 21 Oct 2022 16:36:43 +0200
Subject: vim-patch:9.0.0814: aws config files are not recognized (#20769)

vim-patch:436e5d395fd6 (since upstream tagged the wrong commit)

Problem:    Aws config files are not recognized.
Solution:   Use "confini" for aws config files. (Justin M. Keyes,
            closes vim/vim#11416)
https://github.com/vim/vim/commit/436e5d395fd629c8d33b5cf7b373aad007f16851
---
 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 2c32f9bf52..56659eff04 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1791,6 +1791,8 @@ local pattern = {
   ['.*/etc/DIR_COLORS'] = 'dircolors',
   ['.*/etc/dnsmasq%.conf'] = 'dnsmasq',
   ['php%.ini%-.*'] = 'dosini',
+  ['.*/%.aws/config'] = 'confini',
+  ['.*/%.aws/credentials'] = 'confini',
   ['.*/etc/pacman%.conf'] = 'confini',
   ['.*/etc/yum%.conf'] = 'dosini',
   ['.*lvs'] = 'dracula',
-- 
cgit 


From 4573cfa3adac3a7dbf1b6b032471a1c14adc7427 Mon Sep 17 00:00:00 2001
From: NAKAI Tsuyoshi <82267684+uga-rosa@users.noreply.github.com>
Date: Mon, 24 Oct 2022 21:53:53 +0900
Subject: fix(lua): pesc, tbl_islist result types #20751

Problem:
- pesc() returns multiple results, it should return a single result.
- tbl_islist() returns non-boolean in some branches.
- Docstring: @generic must be declared first

Solution:
Constrain docstring annotations.
Fix return types.

Co-authored-by: Justin M. Keyes 
---
 runtime/lua/vim/shared.lua | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index f03d608e56..f980547ae4 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -62,7 +62,7 @@ end)()
 ---
 ---@param s string String to split
 ---@param sep string Separator or pattern
----@param plain boolean If `true` use `sep` literally (passed to string.find)
+---@param plain (boolean|nil) If `true` use `sep` literally (passed to string.find)
 ---@return fun():string (function) Iterator over the split components
 function vim.gsplit(s, sep, plain)
   vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } })
@@ -108,11 +108,9 @@ end
 ---
 ---@see |vim.gsplit()|
 ---
----@alias split_kwargs {plain: boolean, trimempty: boolean} | boolean | nil
----
 ---@param s string String to split
 ---@param sep string Separator or pattern
----@param kwargs? {plain: boolean, trimempty: boolean} (table|nil) Keyword arguments:
+---@param kwargs ({plain: boolean, trimempty: boolean}|nil) Keyword arguments:
 ---       - plain: (boolean) If `true` use `sep` literally (passed to string.find)
 ---       - trimempty: (boolean) If `true` remove empty items from the front
 ---         and back of the list
@@ -159,8 +157,8 @@ end
 ---
 ---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
 ---
----@param t table (table) Table
 ---@generic T: table
+---@param t table (table) Table
 ---@return T[] (list) List of keys
 function vim.tbl_keys(t)
   assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
@@ -417,8 +415,8 @@ end
 ---@generic T: table
 ---@param dst T List which will be modified and appended to
 ---@param src table List from which values will be inserted
----@param start? number Start index on src. Defaults to 1
----@param finish? number Final index on src. Defaults to `#src`
+---@param start (number|nil) Start index on src. Defaults to 1
+---@param finish (number|nil) Final index on src. Defaults to `#src`
 ---@return T dst
 function vim.list_extend(dst, src, start, finish)
   vim.validate({
@@ -486,7 +484,7 @@ function vim.tbl_islist(t)
     -- TODO(bfredl): in the future, we will always be inside nvim
     -- then this check can be deleted.
     if vim._empty_dict_mt == nil then
-      return nil
+      return false
     end
     return getmetatable(t) ~= vim._empty_dict_mt
   end
@@ -544,7 +542,7 @@ end
 ---@return string %-escaped pattern string
 function vim.pesc(s)
   vim.validate({ s = { s, 's' } })
-  return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
+  return (s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1'))
 end
 
 --- Tests if `s` starts with `prefix`.
-- 
cgit 


From 356244d50ba01c63b3ac33057a69462c4029612c Mon Sep 17 00:00:00 2001
From: Martin Kunz 
Date: Sat, 29 Oct 2022 01:13:27 +0200
Subject: fix(docs): nil as viable argument for goto_prev (#20852)

Added `nil` as a possible option to `vim.diagnostics.goto_prev` in the
docs
---
 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 84091fbf0e..af2d41bf7e 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -797,7 +797,7 @@ function M.get_prev_pos(opts)
 end
 
 --- Move to the previous diagnostic in the current buffer.
----@param opts table See |vim.diagnostic.goto_next()|
+---@param opts table|nil See |vim.diagnostic.goto_next()|
 function M.goto_prev(opts)
   return diagnostic_move_pos(opts, M.get_prev_pos(opts))
 end
-- 
cgit 


From e0dff29adc7690bb693ff05ff8776d07e756d4f9 Mon Sep 17 00:00:00 2001
From: lvimuser <109605931+lvimuser@users.noreply.github.com>
Date: Sun, 30 Oct 2022 06:35:22 -0300
Subject: fix(lsp/window_showDocument): correctly handle external resources
 #20867

- since cmd is a list, it runs directly ('no shell') and we shouldn't
  escape.
- typo: shellerror -> shell_error
---
 runtime/lua/vim/lsp/handlers.lua | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 93fd621161..c648a53555 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -519,15 +519,15 @@ M['window/showDocument'] = function(_, result, ctx, _)
     -- TODO(lvimuser): ask the user for confirmation
     local cmd
     if vim.fn.has('win32') == 1 then
-      cmd = { 'cmd.exe', '/c', 'start', '""', vim.fn.shellescape(uri) }
+      cmd = { 'cmd.exe', '/c', 'start', '""', uri }
     elseif vim.fn.has('macunix') == 1 then
-      cmd = { 'open', vim.fn.shellescape(uri) }
+      cmd = { 'open', uri }
     else
-      cmd = { 'xdg-open', vim.fn.shellescape(uri) }
+      cmd = { 'xdg-open', uri }
     end
 
     local ret = vim.fn.system(cmd)
-    if vim.v.shellerror ~= 0 then
+    if vim.v.shell_error ~= 0 then
       return {
         success = false,
         error = {
-- 
cgit 


From c7a88a470fa78b9e8a925b874752df473e90f6c3 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Sun, 30 Oct 2022 04:44:14 +0100
Subject: feat(checkhealth): improve treesitter report

---
 runtime/lua/vim/treesitter/health.lua | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index 4995c80a02..1c9b58c1a8 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -1,5 +1,6 @@
 local M = {}
 local ts = vim.treesitter
+local health = require('vim.health')
 
 --- Lists the parsers currently installed
 ---
@@ -10,27 +11,21 @@ end
 
 --- Performs a healthcheck for treesitter integration
 function M.check()
-  local report_info = vim.fn['health#report_info']
-  local report_ok = vim.fn['health#report_ok']
-  local report_error = vim.fn['health#report_error']
   local parsers = M.list_parsers()
 
-  report_info(string.format('Runtime ABI version : %d', ts.language_version))
+  health.report_info(string.format('Nvim runtime ABI version: %d', ts.language_version))
 
   for _, parser in pairs(parsers) do
     local parsername = vim.fn.fnamemodify(parser, ':t:r')
-
     local is_loadable, ret = pcall(ts.language.require_language, parsername)
 
-    if not is_loadable then
-      report_error(string.format('Impossible to load parser for %s: %s', parsername, ret))
+    if not is_loadable or not ret then
+      health.report_error(string.format('Parser "%s" failed to load (path: %s): %s', parsername, parser, ret or '?'))
     elseif ret then
       local lang = ts.language.inspect_language(parsername)
-      report_ok(
-        string.format('Loaded parser for %s: ABI version %d', parsername, lang._abi_version)
+      health.report_ok(
+        string.format('Parser: %-10s ABI: %d, path: %s', parsername, lang._abi_version, parser)
       )
-    else
-      report_error(string.format('Unable to load parser for %s', parsername))
     end
   end
 end
-- 
cgit 


From cc7c378bf319d62491ca121ab598397428e4ced4 Mon Sep 17 00:00:00 2001
From: "Justin M. Keyes" 
Date: Sun, 30 Oct 2022 06:41:28 +0100
Subject: feat(checkhealth): check runtime ($VIMRUNTIME)

Move man/health.lua into the "runtime" check.

fix #20696
---
 runtime/lua/vim/treesitter/health.lua | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index 1c9b58c1a8..c0a1eca0ce 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -20,7 +20,9 @@ function M.check()
     local is_loadable, ret = pcall(ts.language.require_language, parsername)
 
     if not is_loadable or not ret then
-      health.report_error(string.format('Parser "%s" failed to load (path: %s): %s', parsername, parser, ret or '?'))
+      health.report_error(
+        string.format('Parser "%s" failed to load (path: %s): %s', parsername, parser, ret or '?')
+      )
     elseif ret then
       local lang = ts.language.inspect_language(parsername)
       health.report_ok(
-- 
cgit 


From 850d7146fc7fb57691641c729d5580b834cd7dd2 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 6 Nov 2022 12:43:05 +0800
Subject: fix(paste): feed keys as typed in cmdline mode (#20959)

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

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 0f312f19f5..0013f38d89 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -167,7 +167,8 @@ do
         local line1 = lines[1]:gsub('(%c)', '\022%1')
         -- nvim_input() is affected by mappings,
         -- so use nvim_feedkeys() with "n" flag to ignore mappings.
-        vim.api.nvim_feedkeys(line1, 'n', true)
+        -- "t" flag is also needed so the pasted text is saved in cmdline history.
+        vim.api.nvim_feedkeys(line1, 'nt', true)
       end
       return true
     end
-- 
cgit 


From 050b0e30b9d8a073a3b421a6cebd878226249ab6 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Mon, 7 Nov 2022 22:28:28 +0100
Subject: vim-patch:9.0.0843: VHS tape files are not recognized (#20995)

Problem:    VHS tape files are not recognized.
Solution:   Add a filetype pattern. (Carlos Alexandro Becker, closes vim/vim#11452)

https://github.com/vim/vim/commit/1756f4b21837e8596241ecd668f4abbbab4bc7e5

Co-authored-by: Carlos A Becker 
---
 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 56659eff04..5aa968e211 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -1057,6 +1057,7 @@ local extension = {
   hdl = 'vhdl',
   vho = 'vhdl',
   vbe = 'vhdl',
+  tape = 'vhs',
   vim = 'vim',
   vba = 'vim',
   mar = 'vmasm',
-- 
cgit 


From 59ff4691f67fc1ddd3d1b7240a2f2eb095e58281 Mon Sep 17 00:00:00 2001
From: Jongwook Choi 
Date: Mon, 7 Nov 2022 19:15:15 -0500
Subject: fix(vim.ui.input): return empty string when inputs nothing (#20883)

fix(vim.ui.input): return empty string when inputs nothing

The previous behavior of `vim.ui.input()` when typing  with
no text input (with an intention of having the empty string as input)
was to execute `on_confirm(nil)`, conflicting with its documentation.

Inputting an empty string should now correctly execute `on_confirm('')`.
This should be clearly distinguished from cancelling or aborting the
input UI, in which case `on_confirm(nil)` is executed as before.
---
 runtime/lua/vim/ui.lua | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index 6f1ce3089d..d9a3963afc 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -73,7 +73,8 @@ end
 ---               user inputs.
 ---@param on_confirm function ((input|nil) -> ())
 ---               Called once the user confirms or abort the input.
----               `input` is what the user typed.
+---               `input` is what the user typed (it might be
+---               an empty string if nothing was entered), or
 ---               `nil` if the user aborted the dialog.
 ---
 --- Example:
@@ -88,11 +89,17 @@ function M.input(opts, on_confirm)
   })
 
   opts = (opts and not vim.tbl_isempty(opts)) and opts or vim.empty_dict()
+
+  -- Note that vim.fn.input({}) returns an empty string when cancelled.
+  -- vim.ui.input() should distinguish aborting from entering an empty string.
+  local _canceled = vim.NIL
+  opts = vim.tbl_extend('keep', opts, { cancelreturn = _canceled })
+
   local input = vim.fn.input(opts)
-  if #input > 0 then
-    on_confirm(input)
-  else
+  if input == _canceled then
     on_confirm(nil)
+  else
+    on_confirm(input)
   end
 end
 
-- 
cgit 


From 07eb4263caa671de63186e9bbd650ec4b1fbce1a Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 28 Oct 2022 13:09:22 +0100
Subject: feat(spell): support nospell in treesitter queries

---
 runtime/lua/vim/treesitter/highlighter.lua | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 83a26aff13..f5e5ca1988 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -164,7 +164,7 @@ function TSHighlighter:get_query(lang)
 end
 
 ---@private
-local function on_line_impl(self, buf, line, spell)
+local function on_line_impl(self, buf, line, is_spell_nav)
   self.tree:for_each_tree(function(tstree, tree)
     if not tstree then
       return
@@ -201,17 +201,26 @@ local function on_line_impl(self, buf, line, spell)
       local start_row, start_col, end_row, end_col = node:range()
       local hl = highlighter_query.hl_cache[capture]
 
-      local is_spell = highlighter_query:query().captures[capture] == 'spell'
+      local capture_name = highlighter_query:query().captures[capture]
+      local spell = nil
+      if capture_name == 'spell' then
+        spell = true
+      elseif capture_name == 'nospell' then
+        spell = false
+      end
+
+      -- Give nospell a higher priority so it always overrides spell captures.
+      local spell_pri_offset = capture_name == 'nospell' and 1 or 0
 
-      if hl and end_row >= line and (not spell or is_spell) then
+      if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
         a.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
           end_line = end_row,
           end_col = end_col,
           hl_group = hl,
           ephemeral = true,
-          priority = tonumber(metadata.priority) or 100, -- Low but leaves room below
+          priority = (tonumber(metadata.priority) or 100) + spell_pri_offset, -- Low but leaves room below
           conceal = metadata.conceal,
-          spell = is_spell,
+          spell = spell,
         })
       end
       if start_row > line then
-- 
cgit 


From b3f781ba912b0d24896d85fc8434faaedfddfeb2 Mon Sep 17 00:00:00 2001
From: Steven Arcangeli <506791+stevearc@users.noreply.github.com>
Date: Sat, 12 Nov 2022 06:57:35 -0800
Subject: fix: vim.ui.input always calls callback #21006

Followup to #20883
Related: #18144

This patch changes the behavior of the default `vim.ui.input` when the user
aborts with ``. Currently, it produces an error message + stack and causes
`on_confirm` to not be called. With this patch, `` will cause `on_confirm`
to be called with `nil`, the same behavior as when the user aborts with ``.
I can think of three good reasons why the behavior should be this way:

1. Easier for the user to understand** It's not intuitive for there to be two
   ways to abort an input dialog that have _different_ outcomes. As a user,
   I would expect any action that cancels the input to leave me in the same
   state. As a plugin author, I see no value in having two possible outcomes for
   aborting the input. I have to handle both cases, but I can't think of
   a situation where I would want to treat one differently than the other.

2. Provides an API that can be overridden by other implementations** The current
   contract of "throw an error upon ``" cannot be replicated by async
   implementations of `vim.ui.input`. If the callsite wants to handle the case
   of the user hitting `` they need to use `pcall(vim.ui.input, ...)`,
   however an async implementation will instantly return and so there will be no
   way for it to produce the same error-throwing behavior when the user inputs
   ``. This makes it impossible to be fully API-compatible with the
   built-in `vim.ui.input`.

3. Provides a useful guarantee to the callsite** As a plugin author, I want the
   guarantee that `on_confirm` will _always_ be called (only catastrophic errors
   should prevent this). If I am in the middle of some async thread of logic,
   I need some way to resume that logic after handing off control to
   `vim.ui.input`. The only way to handle the `` case is with `pcall`,
   which as already mentioned, breaks down if you're using an alternative
   implementation.
---
 runtime/lua/vim/ui.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index d9a3963afc..97dccd83ab 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -95,8 +95,8 @@ function M.input(opts, on_confirm)
   local _canceled = vim.NIL
   opts = vim.tbl_extend('keep', opts, { cancelreturn = _canceled })
 
-  local input = vim.fn.input(opts)
-  if input == _canceled then
+  local ok, input = pcall(vim.fn.input, opts)
+  if not ok or input == _canceled then
     on_confirm(nil)
   else
     on_confirm(input)
-- 
cgit 


From e15f61b1bd60f6a198a0d9969cea407784ff71d0 Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 14 Nov 2022 20:26:27 +0100
Subject: fix(lua): make `vim.deepcopy` work with `vim.NIL`

style: changed double quotes to single quotes

feat: add tests

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

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index f980547ae4..f4a57c13c8 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -49,6 +49,9 @@ vim.deepcopy = (function()
     if f then
       return f(orig, cache or {})
     else
+      if type(orig) == 'userdata' and orig == vim.NIL then
+        return vim.NIL
+      end
       error('Cannot deepcopy object of type ' .. type(orig))
     end
   end
-- 
cgit 


From f1922e78a1df1b1d32779769432fb5586edf5fbb Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Sat, 5 Nov 2022 13:37:05 -0600
Subject: feat: add vim.secure.read()

This function accepts a path to a file and prompts the user if the file
is trusted. If the user confirms that the file is trusted, the contents
of the file are returned. The user's decision is stored in a trust
database at $XDG_STATE_HOME/nvim/trust. When this function is invoked
with a path that is already marked as trusted in the trust database, the
user is not prompted for a response.
---
 runtime/lua/vim/_editor.lua |   1 +
 runtime/lua/vim/secure.lua  | 106 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)
 create mode 100644 runtime/lua/vim/secure.lua

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 0013f38d89..ad4dc20efb 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -36,6 +36,7 @@ for k, v in pairs({
   ui = true,
   health = true,
   fs = true,
+  secure = true,
 }) do
   vim._submodules[k] = v
 end
diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua
new file mode 100644
index 0000000000..341ff8df05
--- /dev/null
+++ b/runtime/lua/vim/secure.lua
@@ -0,0 +1,106 @@
+local M = {}
+
+--- Attempt to read the file at {path} prompting the user if the file should be
+--- trusted. The user's choice is persisted in a trust database at
+--- $XDG_STATE_HOME/nvim/trust.
+---
+---@param path (string) Path to a file to read.
+---
+---@return (string|nil) The contents of the given file if it exists and is
+---        trusted, or nil otherwise.
+function M.read(path)
+  vim.validate({ path = { path, 's' } })
+  local fullpath = vim.loop.fs_realpath(vim.fs.normalize(path))
+  if not fullpath then
+    return nil
+  end
+
+  local trust = {}
+  do
+    local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r')
+    if f then
+      local contents = f:read('*a')
+      if contents then
+        for line in vim.gsplit(contents, '\n') do
+          local hash, file = string.match(line, '^(%S+) (.+)$')
+          if hash and file then
+            trust[file] = hash
+          end
+        end
+      end
+      f:close()
+    end
+  end
+
+  if trust[fullpath] == '!' then
+    -- File is denied
+    return nil
+  end
+
+  local contents
+  do
+    local f = io.open(fullpath, 'r')
+    if not f then
+      return nil
+    end
+    contents = f:read('*a')
+    f:close()
+  end
+
+  local hash = vim.fn.sha256(contents)
+  if trust[fullpath] == hash then
+    -- File already exists in trust database
+    return contents
+  end
+
+  -- File either does not exist in trust database or the hash does not match
+  local choice = vim.fn.confirm(
+    string.format('%s is not trusted.', fullpath),
+    '&ignore\n&view\n&deny\n&allow',
+    1
+  )
+
+  if choice == 0 or choice == 1 then
+    -- Cancelled or ignored
+    return nil
+  elseif choice == 2 then
+    -- View
+    vim.cmd('new')
+    local buf = vim.api.nvim_get_current_buf()
+    local lines = vim.split(string.gsub(contents, '\n$', ''), '\n')
+    vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
+    vim.bo[buf].bufhidden = 'hide'
+    vim.bo[buf].buftype = 'nofile'
+    vim.bo[buf].swapfile = false
+    vim.bo[buf].modeline = false
+    vim.bo[buf].buflisted = false
+    vim.bo[buf].readonly = true
+    vim.bo[buf].modifiable = false
+    return nil
+  elseif choice == 3 then
+    -- Deny
+    trust[fullpath] = '!'
+    contents = nil
+  elseif choice == 4 then
+    -- Allow
+    trust[fullpath] = hash
+  end
+
+  do
+    local f, err = io.open(vim.fn.stdpath('state') .. '/trust', 'w')
+    if not f then
+      error(err)
+    end
+
+    local t = {}
+    for p, h in pairs(trust) do
+      t[#t + 1] = string.format('%s %s\n', h, p)
+    end
+    f:write(table.concat(t))
+    f:close()
+  end
+
+  return contents
+end
+
+return M
-- 
cgit 


From 523b1943c359cf79f29229a3d882c55ea407a237 Mon Sep 17 00:00:00 2001
From: Matthew Gramigna 
Date: Thu, 17 Nov 2022 18:39:31 -0500
Subject: vim-patch:9.0.0897: Clinical Quality Language files are not
 recognized (#21094)

Problem:    Clinical Quality Language files are not recognized.
Solution:   Add the "*.cql" pattern. (Matthew Gramigna, closes vim/vim#11452)

https://github.com/vim/vim/commit/12babe45a389cd1ea8befd5b06239e877b4abbba

Co-authored-by: mgramigna 
---
 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 5aa968e211..f06a5690a0 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -257,6 +257,7 @@ local extension = {
   cc = function(path, bufnr)
     return vim.g.cynlib_syntax_for_cc and 'cynlib' or 'cpp'
   end,
+  cql = 'cqlang',
   crm = 'crm',
   csx = 'cs',
   cs = 'cs',
-- 
cgit 


From af204dd0f193c3cd3154156c9f9fd40199b840c6 Mon Sep 17 00:00:00 2001
From: Mathias Fußenegger 
Date: Sat, 19 Nov 2022 10:48:49 +0100
Subject: feat(lsp): run handler in coroutine to support async response
 (#21026)

To illustrate a use-case this also changes `window/showMessageRequest`
to use `vim.ui.select`
---
 runtime/lua/vim/lsp/handlers.lua | 44 ++++++++++++++++--------
 runtime/lua/vim/lsp/protocol.lua |  9 +++++
 runtime/lua/vim/lsp/rpc.lua      | 72 +++++++++++++++++++++-------------------
 3 files changed, 76 insertions(+), 49 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index c648a53555..c80adc6a87 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -81,22 +81,38 @@ M['window/workDoneProgress/create'] = function(_, result, ctx)
 end
 
 --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
+---@param result lsp.ShowMessageRequestParams
 M['window/showMessageRequest'] = function(_, result)
-  local actions = result.actions
-  print(result.message)
-  local option_strings = { result.message, '\nRequest Actions:' }
-  for i, action in ipairs(actions) do
-    local title = action.title:gsub('\r\n', '\\r\\n')
-    title = title:gsub('\n', '\\n')
-    table.insert(option_strings, string.format('%d. %s', i, title))
-  end
-
-  -- window/showMessageRequest can return either MessageActionItem[] or null.
-  local choice = vim.fn.inputlist(option_strings)
-  if choice < 1 or choice > #actions then
-    return vim.NIL
+  local actions = result.actions or {}
+  local co, is_main = coroutine.running()
+  if co and not is_main then
+    local opts = {
+      prompt = result.message .. ': ',
+      format_item = function(action)
+        return (action.title:gsub('\r\n', '\\r\\n')):gsub('\n', '\\n')
+      end,
+    }
+    vim.ui.select(actions, opts, function(choice)
+      -- schedule to ensure resume doesn't happen _before_ yield with
+      -- default synchronous vim.ui.select
+      vim.schedule(function()
+        coroutine.resume(co, choice or vim.NIL)
+      end)
+    end)
+    return coroutine.yield()
   else
-    return actions[choice]
+    local option_strings = { result.message, '\nRequest Actions:' }
+    for i, action in ipairs(actions) do
+      local title = action.title:gsub('\r\n', '\\r\\n')
+      title = title:gsub('\n', '\\n')
+      table.insert(option_strings, string.format('%d. %s', i, title))
+    end
+    local choice = vim.fn.inputlist(option_strings)
+    if choice < 1 or choice > #actions then
+      return vim.NIL
+    else
+      return actions[choice]
+    end
   end
 end
 
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 7442c8f005..8dc93b3b67 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -20,6 +20,14 @@ function transform_schema_to_table()
 end
 --]=]
 
+---@class lsp.ShowMessageRequestParams
+---@field type lsp.MessageType
+---@field message string
+---@field actions nil|lsp.MessageActionItem[]
+
+---@class lsp.MessageActionItem
+---@field title string
+
 local constants = {
   DiagnosticSeverity = {
     -- Reports an error.
@@ -39,6 +47,7 @@ local constants = {
     Deprecated = 2,
   },
 
+  ---@enum lsp.MessageType
   MessageType = {
     -- An error message.
     Error = 1,
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index ff62623544..b93b227150 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -391,44 +391,46 @@ function Client:handle_body(body)
     -- Schedule here so that the users functions don't trigger an error and
     -- we can still use the result.
     schedule(function()
-      local status, result
-      status, result, err = self:try_call(
-        client_errors.SERVER_REQUEST_HANDLER_ERROR,
-        self.dispatchers.server_request,
-        decoded.method,
-        decoded.params
-      )
-      local _ = log.debug()
-        and log.debug(
-          'server_request: callback result',
-          { status = status, result = result, err = err }
+      coroutine.wrap(function()
+        local status, result
+        status, result, err = self:try_call(
+          client_errors.SERVER_REQUEST_HANDLER_ERROR,
+          self.dispatchers.server_request,
+          decoded.method,
+          decoded.params
         )
-      if status then
-        if result == nil and err == nil then
-          error(
-            string.format(
-              'method %q: either a result or an error must be sent to the server in response',
-              decoded.method
-            )
-          )
-        end
-        if err then
-          assert(
-            type(err) == 'table',
-            'err must be a table. Use rpc_response_error to help format errors.'
-          )
-          local code_name = assert(
-            protocol.ErrorCodes[err.code],
-            'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
+        local _ = log.debug()
+          and log.debug(
+            'server_request: callback result',
+            { status = status, result = result, err = err }
           )
-          err.message = err.message or code_name
+        if status then
+          if result == nil and err == nil then
+            error(
+              string.format(
+                'method %q: either a result or an error must be sent to the server in response',
+                decoded.method
+              )
+            )
+          end
+          if err then
+            assert(
+              type(err) == 'table',
+              'err must be a table. Use rpc_response_error to help format errors.'
+            )
+            local code_name = assert(
+              protocol.ErrorCodes[err.code],
+              'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
+            )
+            err.message = err.message or code_name
+          end
+        else
+          -- On an exception, result will contain the error message.
+          err = rpc_response_error(protocol.ErrorCodes.InternalError, result)
+          result = nil
         end
-      else
-        -- On an exception, result will contain the error message.
-        err = rpc_response_error(protocol.ErrorCodes.InternalError, result)
-        result = nil
-      end
-      self:send_response(decoded.id, err, result)
+        self:send_response(decoded.id, err, result)
+      end)()
     end)
     -- This works because we are expecting vim.NIL here
   elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then
-- 
cgit 


From cfdf5e6f372928c11a2b1459b14c4c2de5f69c51 Mon Sep 17 00:00:00 2001
From: Grzegorz Rozdzialik 
Date: Sat, 19 Nov 2022 12:27:00 +0100
Subject: fix(lsp): ignore hover and signatureHelp responses on buffer change
 (#21121)

Language servers can take some time to respond to the
`textDocument/hover` and `textDocument/signatureHelp` messages. During
that time, the user could have already moved to another buffer. The
popup was always shown in the current buffer, which could be a different
one than the buffer for which the request was sent.

This was particularly annoying when moving to a buffer with a `BufLeave`
autocmd, as that autocmd was triggered when the hover popup was shown
for the original buffer.

Ignoring the response from these 2 messages if they are for a buffer
that is not the current one leads to less noise. The popup will only be
shown for the buffer for which it was requested.

A more robust solution could involve cancelling the hover/signatureHelp
request if the buffer changes so the language server can free its
resources. It could be implemented in the future.
---
 runtime/lua/vim/lsp/handlers.lua | 8 ++++++++
 1 file changed, 8 insertions(+)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index c80adc6a87..8e5e75232f 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -328,6 +328,10 @@ end
 function M.hover(_, result, ctx, config)
   config = config or {}
   config.focus_id = ctx.method
+  if api.nvim_get_current_buf() ~= ctx.bufnr then
+    -- Ignore result since buffer changed. This happens for slow language servers.
+    return
+  end
   if not (result and result.contents) then
     vim.notify('No information available')
     return
@@ -408,6 +412,10 @@ M['textDocument/implementation'] = location_handler
 function M.signature_help(_, result, ctx, config)
   config = config or {}
   config.focus_id = ctx.method
+  if api.nvim_get_current_buf() ~= ctx.bufnr then
+    -- Ignore result since buffer changed. This happens for slow language servers.
+    return
+  end
   -- When use `autocmd CompleteDone  lua vim.lsp.buf.signature_help()` to call signatureHelp handler
   -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `` to ignore
   if not (result and result.signatures and result.signatures[1]) then
-- 
cgit 


From 6e8ed5abaa9c33d1d78ab7ff5b07dd5bac623a1d Mon Sep 17 00:00:00 2001
From: Raphael 
Date: Sat, 19 Nov 2022 21:41:47 +0800
Subject: perf(diagnostic): use api variable and improve validate (#21111)

* fix(diagnostic): use api variable and improve validate

* fix: fix test case
---
 runtime/lua/vim/diagnostic.lua | 96 ++++++++++++++++++++----------------------
 1 file changed, 46 insertions(+), 50 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index af2d41bf7e..09eeb617f5 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -1,4 +1,4 @@
-local if_nil = vim.F.if_nil
+local api, if_nil = vim.api, vim.F.if_nil
 
 local M = {}
 
@@ -47,11 +47,11 @@ local bufnr_and_namespace_cacher_mt = {
 
 local diagnostic_cache
 do
-  local group = vim.api.nvim_create_augroup('DiagnosticBufWipeout', {})
+  local group = api.nvim_create_augroup('DiagnosticBufWipeout', {})
   diagnostic_cache = setmetatable({}, {
     __index = function(t, bufnr)
       assert(bufnr > 0, 'Invalid buffer number')
-      vim.api.nvim_create_autocmd('BufWipeout', {
+      api.nvim_create_autocmd('BufWipeout', {
         group = group,
         buffer = bufnr,
         callback = function()
@@ -245,7 +245,7 @@ end)()
 ---@private
 local function get_bufnr(bufnr)
   if not bufnr or bufnr == 0 then
-    return vim.api.nvim_get_current_buf()
+    return api.nvim_get_current_buf()
   end
   return bufnr
 end
@@ -299,7 +299,7 @@ end
 ---@private
 local function restore_extmarks(bufnr, last)
   for ns, extmarks in pairs(diagnostic_cache_extmarks[bufnr]) do
-    local extmarks_current = vim.api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, { details = true })
+    local extmarks_current = api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, { details = true })
     local found = {}
     for _, extmark in ipairs(extmarks_current) do
       -- nvim_buf_set_lines will move any extmark to the line after the last
@@ -312,7 +312,7 @@ local function restore_extmarks(bufnr, last)
       if not found[extmark[1]] then
         local opts = extmark[4]
         opts.id = extmark[1]
-        pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts)
+        pcall(api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts)
       end
     end
   end
@@ -322,7 +322,7 @@ end
 local function save_extmarks(namespace, bufnr)
   bufnr = get_bufnr(bufnr)
   if not diagnostic_attached_buffers[bufnr] then
-    vim.api.nvim_buf_attach(bufnr, false, {
+    api.nvim_buf_attach(bufnr, false, {
       on_lines = function(_, _, _, _, _, last)
         restore_extmarks(bufnr, last - 1)
       end,
@@ -333,7 +333,7 @@ local function save_extmarks(namespace, bufnr)
     diagnostic_attached_buffers[bufnr] = true
   end
   diagnostic_cache_extmarks[bufnr][namespace] =
-    vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true })
+    api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true })
 end
 
 local registered_autocmds = {}
@@ -366,8 +366,8 @@ local function schedule_display(namespace, bufnr, args)
 
   local key = make_augroup_key(namespace, bufnr)
   if not registered_autocmds[key] then
-    local group = vim.api.nvim_create_augroup(key, { clear = true })
-    vim.api.nvim_create_autocmd(insert_leave_auto_cmds, {
+    local group = api.nvim_create_augroup(key, { clear = true })
+    api.nvim_create_autocmd(insert_leave_auto_cmds, {
       group = group,
       buffer = bufnr,
       callback = function()
@@ -384,7 +384,7 @@ local function clear_scheduled_display(namespace, bufnr)
   local key = make_augroup_key(namespace, bufnr)
 
   if registered_autocmds[key] then
-    vim.api.nvim_del_augroup_by_name(key)
+    api.nvim_del_augroup_by_name(key)
     registered_autocmds[key] = nil
   end
 end
@@ -399,7 +399,7 @@ local function get_diagnostics(bufnr, opts, clamp)
   -- Memoized results of buf_line_count per bufnr
   local buf_line_count = setmetatable({}, {
     __index = function(t, k)
-      t[k] = vim.api.nvim_buf_line_count(k)
+      t[k] = api.nvim_buf_line_count(k)
       return rawget(t, k)
     end,
   })
@@ -407,7 +407,7 @@ local function get_diagnostics(bufnr, opts, clamp)
   ---@private
   local function add(b, d)
     if not opts.lnum or d.lnum == opts.lnum then
-      if clamp and vim.api.nvim_buf_is_loaded(b) then
+      if clamp and api.nvim_buf_is_loaded(b) then
         local line_count = buf_line_count[b] - 1
         if
           d.lnum > line_count
@@ -471,7 +471,7 @@ local function set_list(loclist, opts)
   local winnr = opts.winnr or 0
   local bufnr
   if loclist then
-    bufnr = vim.api.nvim_win_get_buf(winnr)
+    bufnr = api.nvim_win_get_buf(winnr)
   end
   -- Don't clamp line numbers since the quickfix list can already handle line
   -- numbers beyond the end of the buffer
@@ -483,7 +483,7 @@ local function set_list(loclist, opts)
     vim.fn.setqflist({}, ' ', { title = title, items = items })
   end
   if open then
-    vim.api.nvim_command(loclist and 'lopen' or 'botright copen')
+    api.nvim_command(loclist and 'lopen' or 'botright copen')
   end
 end
 
@@ -492,7 +492,7 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
   position[1] = position[1] - 1
   bufnr = get_bufnr(bufnr)
   local wrap = vim.F.if_nil(opts.wrap, true)
-  local line_count = vim.api.nvim_buf_line_count(bufnr)
+  local line_count = api.nvim_buf_line_count(bufnr)
   local diagnostics =
     get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true)
   local line_diagnostics = diagnostic_lines(diagnostics)
@@ -506,7 +506,7 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
       lnum = (lnum + line_count) % line_count
     end
     if line_diagnostics[lnum] and not vim.tbl_isempty(line_diagnostics[lnum]) then
-      local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
+      local line_length = #api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
       local sort_diagnostics, is_next
       if search_forward then
         sort_diagnostics = function(a, b)
@@ -542,17 +542,17 @@ local function diagnostic_move_pos(opts, pos)
   opts = opts or {}
 
   local float = vim.F.if_nil(opts.float, true)
-  local win_id = opts.win_id or vim.api.nvim_get_current_win()
+  local win_id = opts.win_id or api.nvim_get_current_win()
 
   if not pos then
-    vim.api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
+    api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
     return
   end
 
-  vim.api.nvim_win_call(win_id, function()
+  api.nvim_win_call(win_id, function()
     -- Save position in the window's jumplist
     vim.cmd("normal! m'")
-    vim.api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
+    api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
     -- Open folds under the cursor
     vim.cmd('normal! zv')
   end)
@@ -561,7 +561,7 @@ local function diagnostic_move_pos(opts, pos)
     local float_opts = type(float) == 'table' and float or {}
     vim.schedule(function()
       M.open_float(vim.tbl_extend('keep', float_opts, {
-        bufnr = vim.api.nvim_win_get_buf(win_id),
+        bufnr = api.nvim_win_get_buf(win_id),
         scope = 'cursor',
         focus = false,
       }))
@@ -666,13 +666,13 @@ function M.config(opts, namespace)
 
   if namespace then
     for bufnr, v in pairs(diagnostic_cache) do
-      if vim.api.nvim_buf_is_loaded(bufnr) and v[namespace] then
+      if api.nvim_buf_is_loaded(bufnr) and v[namespace] then
         M.show(namespace, bufnr)
       end
     end
   else
     for bufnr, v in pairs(diagnostic_cache) do
-      if vim.api.nvim_buf_is_loaded(bufnr) then
+      if api.nvim_buf_is_loaded(bufnr) then
         for ns in pairs(v) do
           M.show(ns, bufnr)
         end
@@ -707,11 +707,11 @@ function M.set(namespace, bufnr, diagnostics, opts)
     set_diagnostic_cache(namespace, bufnr, diagnostics)
   end
 
-  if vim.api.nvim_buf_is_loaded(bufnr) then
+  if api.nvim_buf_is_loaded(bufnr) then
     M.show(namespace, bufnr, nil, opts)
   end
 
-  vim.api.nvim_exec_autocmds('DiagnosticChanged', {
+  api.nvim_exec_autocmds('DiagnosticChanged', {
     modeline = false,
     buffer = bufnr,
     data = { diagnostics = diagnostics },
@@ -726,7 +726,7 @@ function M.get_namespace(namespace)
   vim.validate({ namespace = { namespace, 'n' } })
   if not all_namespaces[namespace] then
     local name
-    for k, v in pairs(vim.api.nvim_get_namespaces()) do
+    for k, v in pairs(api.nvim_get_namespaces()) do
       if namespace == v then
         name = k
         break
@@ -776,9 +776,9 @@ end
 function M.get_prev(opts)
   opts = opts or {}
 
-  local win_id = opts.win_id or vim.api.nvim_get_current_win()
-  local bufnr = vim.api.nvim_win_get_buf(win_id)
-  local cursor_position = opts.cursor_position or vim.api.nvim_win_get_cursor(win_id)
+  local win_id = opts.win_id or api.nvim_get_current_win()
+  local bufnr = api.nvim_win_get_buf(win_id)
+  local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
 
   return next_diagnostic(cursor_position, false, bufnr, opts, opts.namespace)
 end
@@ -809,9 +809,9 @@ end
 function M.get_next(opts)
   opts = opts or {}
 
-  local win_id = opts.win_id or vim.api.nvim_get_current_win()
-  local bufnr = vim.api.nvim_win_get_buf(win_id)
-  local cursor_position = opts.cursor_position or vim.api.nvim_win_get_cursor(win_id)
+  local win_id = opts.win_id or api.nvim_get_current_win()
+  local bufnr = api.nvim_win_get_buf(win_id)
+  local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
 
   return next_diagnostic(cursor_position, true, bufnr, opts, opts.namespace)
 end
@@ -931,7 +931,7 @@ M.handlers.underline = {
 
     local ns = M.get_namespace(namespace)
     if not ns.user_data.underline_ns then
-      ns.user_data.underline_ns = vim.api.nvim_create_namespace('')
+      ns.user_data.underline_ns = api.nvim_create_namespace('')
     end
 
     local underline_ns = ns.user_data.underline_ns
@@ -958,7 +958,7 @@ M.handlers.underline = {
     local ns = M.get_namespace(namespace)
     if ns.user_data.underline_ns then
       diagnostic_cache_extmarks[bufnr][ns.user_data.underline_ns] = {}
-      vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
+      api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
     end
   end,
 }
@@ -997,7 +997,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 = vim.api.nvim_create_namespace('')
+      ns.user_data.virt_text_ns = api.nvim_create_namespace('')
     end
 
     local virt_text_ns = ns.user_data.virt_text_ns
@@ -1009,7 +1009,7 @@ M.handlers.virtual_text = {
       local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text)
 
       if virt_texts then
-        vim.api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
+        api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
           hl_mode = 'combine',
           virt_text = virt_texts,
         })
@@ -1021,7 +1021,7 @@ M.handlers.virtual_text = {
     local ns = M.get_namespace(namespace)
     if ns.user_data.virt_text_ns then
       diagnostic_cache_extmarks[bufnr][ns.user_data.virt_text_ns] = {}
-      vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1)
+      api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1)
     end
   end,
 }
@@ -1153,7 +1153,7 @@ function M.show(namespace, bufnr, diagnostics, opts)
   if opts.update_in_insert then
     clear_scheduled_display(namespace, bufnr)
   else
-    local mode = vim.api.nvim_get_mode()
+    local mode = api.nvim_get_mode()
     if string.sub(mode.mode, 1, 1) == 'i' then
       schedule_display(namespace, bufnr, opts)
       return
@@ -1251,7 +1251,7 @@ function M.open_float(opts, ...)
   local lnum, col
   if scope == 'line' or scope == 'cursor' then
     if not opts.pos then
-      local pos = vim.api.nvim_win_get_cursor(0)
+      local pos = api.nvim_win_get_cursor(0)
       lnum = pos[1] - 1
       col = pos[2]
     elseif type(opts.pos) == 'number' then
@@ -1273,7 +1273,7 @@ function M.open_float(opts, ...)
     end, diagnostics)
   elseif scope == 'cursor' then
     -- LSP servers can send diagnostics with `end_col` past the length of the line
-    local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
+    local line_length = #api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
     diagnostics = vim.tbl_filter(function(d)
       return d.lnum == lnum
         and math.min(d.col, line_length - 1) <= col
@@ -1305,9 +1305,7 @@ function M.open_float(opts, ...)
     vim.validate({
       header = {
         header,
-        function(v)
-          return type(v) == 'string' or type(v) == 'table'
-        end,
+        { 'string', 'table' },
         "'string' or 'table'",
       },
     })
@@ -1341,9 +1339,7 @@ function M.open_float(opts, ...)
     vim.validate({
       prefix = {
         prefix_opt,
-        function(v)
-          return type(v) == 'string' or type(v) == 'table' or type(v) == 'function'
-        end,
+        { 'string', 'table', 'function' },
         "'string' or 'table' or 'function'",
       },
     })
@@ -1377,9 +1373,9 @@ function M.open_float(opts, ...)
   for i, hi in ipairs(highlights) do
     local prefixlen, hiname, prefix_hiname = unpack(hi)
     if prefix_hiname then
-      vim.api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i - 1, 0, prefixlen)
+      api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i - 1, 0, prefixlen)
     end
-    vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i - 1, prefixlen, -1)
+    api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i - 1, prefixlen, -1)
   end
 
   return float_bufnr, winnr
@@ -1410,7 +1406,7 @@ function M.reset(namespace, bufnr)
       M.hide(iter_namespace, iter_bufnr)
     end
 
-    vim.api.nvim_exec_autocmds('DiagnosticChanged', {
+    api.nvim_exec_autocmds('DiagnosticChanged', {
       modeline = false,
       buffer = iter_bufnr,
       data = { diagnostics = {} },
-- 
cgit 


From fbce9f421ad1ad466126a24524ade9df978486d5 Mon Sep 17 00:00:00 2001
From: beardedsakimonkey <54521218+beardedsakimonkey@users.noreply.github.com>
Date: Sun, 20 Nov 2022 20:09:35 +0000
Subject: feat(diagnostic): add `suffix` option to `open_float()` (#21130)

Closes #18687

This introduces a `suffix` option to `vim.diagnostic.open_float()` (and
consequently `vim.diagnostic.config()`) that appends some text to each
diagnostic in the float.

It accepts the same types as `prefix`. For multiline diagnostics, the suffix is
only appended to the last line. By default, the suffix will render the
diagnostic error code, if any.
---
 runtime/lua/vim/diagnostic.lua | 65 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 53 insertions(+), 12 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 09eeb617f5..008b00a9a0 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -1220,6 +1220,8 @@ end
 ---                      string, it is prepended to each diagnostic in the window with no
 ---                      highlight.
 ---                      Overrides the setting from |vim.diagnostic.config()|.
+---            - suffix: Same as {prefix}, but appends the text to the diagnostic instead of
+---                      prepending it. Overrides the setting from |vim.diagnostic.config()|.
 ---@return tuple ({float_bufnr}, {win_id})
 function M.open_float(opts, ...)
   -- Support old (bufnr, opts) signature
@@ -1313,11 +1315,11 @@ function M.open_float(opts, ...)
       -- Don't insert any lines for an empty string
       if string.len(if_nil(header[1], '')) > 0 then
         table.insert(lines, header[1])
-        table.insert(highlights, { 0, header[2] or 'Bold' })
+        table.insert(highlights, { hlname = header[2] or 'Bold' })
       end
     elseif #header > 0 then
       table.insert(lines, header)
-      table.insert(highlights, { 0, 'Bold' })
+      table.insert(highlights, { hlname = 'Bold' })
     end
   end
 
@@ -1350,18 +1352,52 @@ function M.open_float(opts, ...)
     end
   end
 
+  local suffix_opt = if_nil(opts.suffix, function(diagnostic)
+    return diagnostic.code and string.format(' [%s]', diagnostic.code) or ''
+  end)
+
+  local suffix, suffix_hl_group
+  if suffix_opt then
+    vim.validate({
+      suffix = {
+        suffix_opt,
+        { 'string', 'table', 'function' },
+        "'string' or 'table' or 'function'",
+      },
+    })
+    if type(suffix_opt) == 'string' then
+      suffix, suffix_hl_group = suffix_opt, 'NormalFloat'
+    elseif type(suffix_opt) == 'table' then
+      suffix, suffix_hl_group = suffix_opt[1] or '', suffix_opt[2] or 'NormalFloat'
+    end
+  end
+
   for i, diagnostic in ipairs(diagnostics) do
     if prefix_opt and type(prefix_opt) == 'function' then
       prefix, prefix_hl_group = prefix_opt(diagnostic, i, #diagnostics)
       prefix, prefix_hl_group = prefix or '', prefix_hl_group or 'NormalFloat'
     end
+    if suffix_opt and type(suffix_opt) == 'function' then
+      suffix, suffix_hl_group = suffix_opt(diagnostic, i, #diagnostics)
+      suffix, suffix_hl_group = suffix or '', suffix_hl_group or 'NormalFloat'
+    end
     local hiname = floating_highlight_map[diagnostic.severity]
     local message_lines = vim.split(diagnostic.message, '\n')
-    table.insert(lines, prefix .. message_lines[1])
-    table.insert(highlights, { #prefix, hiname, prefix_hl_group })
-    for j = 2, #message_lines do
-      table.insert(lines, string.rep(' ', #prefix) .. message_lines[j])
-      table.insert(highlights, { 0, hiname })
+    for j = 1, #message_lines do
+      local pre = j == 1 and prefix or string.rep(' ', #prefix)
+      local suf = j == #message_lines and suffix or ''
+      table.insert(lines, pre .. message_lines[j] .. suf)
+      table.insert(highlights, {
+        hlname = hiname,
+        prefix = {
+          length = j == 1 and #prefix or 0,
+          hlname = prefix_hl_group,
+        },
+        suffix = {
+          length = j == #message_lines and #suffix or 0,
+          hlname = suffix_hl_group,
+        },
+      })
     end
   end
 
@@ -1370,12 +1406,17 @@ function M.open_float(opts, ...)
     opts.focus_id = scope
   end
   local float_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts)
-  for i, hi in ipairs(highlights) do
-    local prefixlen, hiname, prefix_hiname = unpack(hi)
-    if prefix_hiname then
-      api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i - 1, 0, prefixlen)
+  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)
+    end
+    api.nvim_buf_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)
     end
-    api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i - 1, prefixlen, -1)
   end
 
   return float_bufnr, winnr
-- 
cgit 


From 126ef65e5b3ff0da68bfe166be0bb0a33664142b Mon Sep 17 00:00:00 2001
From: beardedsakimonkey <54521218+beardedsakimonkey@users.noreply.github.com>
Date: Sun, 20 Nov 2022 23:57:36 +0000
Subject: feat(diagnostic): add `suffix` option to `virt_text` config (#21140)

This introduces a `suffix` option to the `virt_text` config in
`vim.diagnostic.config()`. The suffix can either be a string which is appended
to the diagnostic message or a function returning such. The function receives a
`diagnostic` argument, which is the diagnostic table of the last diagnostic (the
one whose message is rendered as virt text).
---
 runtime/lua/vim/diagnostic.lua | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 008b00a9a0..a7fc47a5a8 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -613,6 +613,10 @@ end
 ---                       * spacing: (number) Amount of empty spaces inserted at the beginning
 ---                                  of the virtual text.
 ---                       * prefix: (string) Prepend diagnostic message with prefix.
+---                       * suffix: (string or function) Append diagnostic message with suffix.
+---                                 If a function, it must have the signature (diagnostic) ->
+---                                 string, where {diagnostic} is of type |diagnostic-structure|.
+---                                 This can be used to render an LSP diagnostic error code.
 ---                       * format: (function) A function that takes a diagnostic as input and
 ---                                 returns a string. The return value is the text used to display
 ---                                 the diagnostic. Example:
@@ -1039,6 +1043,7 @@ function M._get_virt_text_chunks(line_diags, opts)
 
   opts = opts or {}
   local prefix = opts.prefix or '■'
+  local suffix = opts.suffix or ''
   local spacing = opts.spacing or 4
 
   -- Create a little more space between virtual text and contents
@@ -1052,8 +1057,11 @@ function M._get_virt_text_chunks(line_diags, opts)
   -- TODO(tjdevries): Allow different servers to be shown first somehow?
   -- TODO(tjdevries): Display server name associated with these?
   if last.message then
+    if type(suffix) == 'function' then
+      suffix = suffix(last) or ''
+    end
     table.insert(virt_texts, {
-      string.format('%s %s', prefix, last.message:gsub('\r', ''):gsub('\n', '  ')),
+      string.format('%s %s%s', prefix, last.message:gsub('\r', ''):gsub('\n', '  '), suffix),
       virtual_text_highlight_map[last.severity],
     })
 
-- 
cgit 


From 2bb244af314e80afbab30b4db4490c8dae894b85 Mon Sep 17 00:00:00 2001
From: Raphael 
Date: Mon, 21 Nov 2022 18:06:14 +0800
Subject: feat(lsp): support set title in lsp relate floatwindow (#21110)

---
 runtime/lua/vim/lsp/handlers.lua | 4 +++-
 runtime/lua/vim/lsp/util.lua     | 9 +++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 8e5e75232f..39e2577294 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -317,7 +317,9 @@ end
 --- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
 ---   vim.lsp.handlers.hover, {
 ---     -- Use a sharp border with `FloatBorder` highlights
----     border = "single"
+---     border = "single",
+---     -- add the title in hover float window
+---     title = "hover"
 ---   }
 --- )
 --- 
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b0f9c1660e..d89757ef0c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1050,6 +1050,13 @@ function M.make_floating_popup_options(width, height, opts) col = 1 end + local title = (opts.border and opts.title) and opts.title or nil + local title_pos + + if title then + title_pos = opts.title_pos or 'center' + end + return { anchor = anchor, col = col + (opts.offset_x or 0), @@ -1061,6 +1068,8 @@ function M.make_floating_popup_options(width, height, opts) width = width, border = opts.border or default_border, zindex = opts.zindex or 50, + title = title, + title_pos = title_pos, } end -- cgit From 904d0056d520ef01e2873bbaa91a4693e8dfd226 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 21 Nov 2022 22:02:18 +0100 Subject: fix(diagnostic): correct type annotations; add Diagnostic type (#21120) Some functions didn't include the `nil` case in the return type annotation. This corrects those and also adds a Diagnostic class definition for the diagnostic.get return type --- runtime/lua/vim/diagnostic.lua | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index a7fc47a5a8..18df1f1586 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -2,6 +2,7 @@ local api, if_nil = vim.api, vim.F.if_nil local M = {} +---@enum DiagnosticSeverity M.severity = { ERROR = 1, WARN = 2, @@ -755,6 +756,18 @@ function M.get_namespaces() return vim.deepcopy(all_namespaces) end +---@class Diagnostic +---@field buffer number +---@field lnum number 0-indexed +---@field end_lnum nil|number 0-indexed +---@field col number 0-indexed +---@field end_col nil|number 0-indexed +---@field severity DiagnosticSeverity +---@field message string +---@field source nil|string +---@field code nil|string +---@field user_data nil|any arbitrary data plugins can add + --- Get current diagnostics. --- ---@param bufnr number|nil Buffer number to get diagnostics from. Use 0 for @@ -763,7 +776,7 @@ end --- - namespace: (number) Limit diagnostics to the given namespace. --- - lnum: (number) Limit diagnostics to the given line number. --- - severity: See |diagnostic-severity|. ----@return table A list of diagnostic items |diagnostic-structure|. +---@return Diagnostic[] table A list of diagnostic items |diagnostic-structure|. function M.get(bufnr, opts) vim.validate({ bufnr = { bufnr, 'n', true }, @@ -775,8 +788,8 @@ end --- Get the previous diagnostic closest to the cursor position. --- ----@param opts table See |vim.diagnostic.goto_next()| ----@return table Previous diagnostic +---@param opts nil|table See |vim.diagnostic.goto_next()| +---@return Diagnostic|nil Previous diagnostic function M.get_prev(opts) opts = opts or {} @@ -789,8 +802,9 @@ end --- Return the position of the previous diagnostic in the current buffer. --- ----@param opts table See |vim.diagnostic.goto_next()| ----@return table Previous diagnostic position as a (row, col) tuple. +---@param opts table|nil See |vim.diagnostic.goto_next()| +---@return table|false Previous diagnostic position as a (row, col) tuple or false if there is no +--- prior diagnostic function M.get_prev_pos(opts) local prev = M.get_prev(opts) if not prev then @@ -808,8 +822,8 @@ end --- Get the next diagnostic closest to the cursor position. --- ----@param opts table See |vim.diagnostic.goto_next()| ----@return table Next diagnostic +---@param opts table|nil See |vim.diagnostic.goto_next()| +---@return Diagnostic|nil Next diagnostic function M.get_next(opts) opts = opts or {} @@ -822,8 +836,9 @@ end --- Return the position of the next diagnostic in the current buffer. --- ----@param opts table See |vim.diagnostic.goto_next()| ----@return table Next diagnostic position as a (row, col) tuple. +---@param opts table|nil See |vim.diagnostic.goto_next()| +---@return table|false Next diagnostic position as a (row, col) tuple or false if no next +--- diagnostic. function M.get_next_pos(opts) local next = M.get_next(opts) if not next then @@ -1230,7 +1245,7 @@ end --- Overrides the setting from |vim.diagnostic.config()|. --- - suffix: Same as {prefix}, but appends the text to the diagnostic instead of --- prepending it. Overrides the setting from |vim.diagnostic.config()|. ----@return tuple ({float_bufnr}, {win_id}) +---@return number|nil, number|nil: ({float_bufnr}, {win_id}) function M.open_float(opts, ...) -- Support old (bufnr, opts) signature local bufnr @@ -1578,7 +1593,7 @@ end ---@param defaults table|nil Table of default values for any fields not listed in {groups}. --- When omitted, numeric values default to 0 and "severity" defaults to --- ERROR. ----@return diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}. +---@return Diagnostic|nil: |diagnostic-structure| or `nil` if {pat} fails to match {str}. function M.match(str, pat, groups, severity_map, defaults) vim.validate({ str = { str, 's' }, @@ -1625,7 +1640,7 @@ local errlist_type_map = { --- passed to |setqflist()| or |setloclist()|. --- ---@param diagnostics table List of diagnostics |diagnostic-structure|. ----@return array of quickfix list items |setqflist-what| +---@return table[] of quickfix list items |setqflist-what| function M.toqflist(diagnostics) vim.validate({ diagnostics = { @@ -1662,7 +1677,7 @@ end --- ---@param list table A list of quickfix items from |getqflist()| or --- |getloclist()|. ----@return array of diagnostics |diagnostic-structure| +---@return Diagnostic[] array of |diagnostic-structure| function M.fromqflist(list) vim.validate({ list = { -- cgit From d41e93d5a83642f90898cae211e017d99ff97fd9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 22 Nov 2022 23:28:49 +0100 Subject: vim-patch:9.0.0922: Mermaid files are not recognized (#21160) Problem: Mermaid files are not recognized. Solution: Add patterns for Mermaid. (Crag MacEachern) https://github.com/vim/vim/commit/364438d1e817d1d76003695f9ab533df35f8948a Co-authored-by: Bram Moolenaar --- 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 f06a5690a0..1cc9beecc6 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -659,6 +659,9 @@ local extension = { dmt = 'maxima', wxm = 'maxima', mel = 'mel', + mmd = 'mermaid', + mmdc = 'mermaid', + mermaid = 'mermaid', mf = 'mf', mgl = 'mgl', mgp = 'mgp', -- cgit From ddea80ebd66617bfc3a1af0b08d55dd7ed51f2ca Mon Sep 17 00:00:00 2001 From: AzerAfram <97570339+AzerAfram@users.noreply.github.com> Date: Wed, 23 Nov 2022 15:40:07 -0800 Subject: docs(lua): add clarifications for fs.find() and fs.normalize() (#21132) Co-Authored-By: Gregory Anders <8965202+gpanders@users.noreply.github.com> Co-Authored-By: zeertzjq --- runtime/lua/vim/fs.lua | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 7bd635d8b6..c7c053852d 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -79,11 +79,12 @@ end ---@param names (string|table|fun(name: string): boolean) Names of the files --- and directories to find. --- Must be base names, paths and globs are not supported. ---- If a function it is called per file and dir within the ---- traversed directories to test if they match. +--- The function is called per file and directory within the +--- traversed directories to test if they match {names}. +--- ---@param opts (table) Optional keyword arguments: --- - path (string): Path to begin searching from. If ---- omitted, the current working directory is used. +--- omitted, the |current-directory| is used. --- - upward (boolean, default false): If true, search --- upward through parent directories. Otherwise, --- search through child directories @@ -92,12 +93,13 @@ end --- reached. The directory itself is not searched. --- - type (string): Find only files ("file") or --- directories ("directory"). If omitted, both ---- files and directories that match {name} are +--- files and directories that match {names} are --- included. --- - limit (number, default 1): Stop the search after --- finding this many matches. Use `math.huge` to --- place no limit on the number of matches. ----@return (table) The paths of all matching files or directories +--- +---@return (table) The normalized paths |vim.fs.normalize()| of all matching files or directories function M.find(names, opts) opts = opts or {} vim.validate({ @@ -211,16 +213,16 @@ end --- backslash (\\) characters are converted to forward slashes (/). Environment --- variables are also expanded. --- ---- Example: +--- Examples: ---
---- vim.fs.normalize('C:\\Users\\jdoe')
---- => 'C:/Users/jdoe'
+---   vim.fs.normalize('C:\\Users\\jdoe')
+---   => 'C:/Users/jdoe'
 ---
---- vim.fs.normalize('~/src/neovim')
---- => '/home/jdoe/src/neovim'
+---   vim.fs.normalize('~/src/neovim')
+---   => '/home/jdoe/src/neovim'
 ---
---- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
---- => '/Users/jdoe/.config/nvim/init.vim'
+---   vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
+---   => '/Users/jdoe/.config/nvim/init.vim'
 --- 
--- ---@param path (string) Path to normalize -- cgit From 5ca1f48b4021e25d97bc0a29644310be4db5f70d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 25 Nov 2022 09:17:08 +0100 Subject: vim-patch:9.0.0942: Workflow Description Language files are not recognized (#21183) Problem: Workflow Description Language files are not recognized. Solution: Add a pattern for the "wdl" filetype. (Matt Dunford, closes vim/vim#11611) https://github.com/vim/vim/commit/f60bdc3417a56a1f69e001a7ec210b92d5b0f2e1 Co-authored-by: Matt Dunford --- 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 1cc9beecc6..ced5bb97a9 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1071,6 +1071,7 @@ local extension = { vue = 'vue', wat = 'wast', wast = 'wast', + wdl = 'wdl', wm = 'webmacro', wbt = 'winbatch', wml = 'wml', -- cgit From 7875e1377c24699a3773609996c4e21320a09f31 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 25 Nov 2022 16:17:30 +0800 Subject: vim-patch:9.0.0932: Oblivion files are not recognized (#21179) Problem: Oblivion files are not recognized. Solution: Recognize Oblivion files and alike as "obse". (closes vim/vim#11540) https://github.com/vim/vim/commit/ecfd511e8d802068434735dda00db6b783df6922 Co-authored-by: Bram Moolenaar --- runtime/lua/vim/filetype.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index ced5bb97a9..5e086c8abf 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -729,6 +729,10 @@ local extension = { nsi = 'nsis', nsh = 'nsis', obj = 'obj', + obl = 'obse', + obse = 'obse', + oblivion = 'obse', + obscript = 'obse', mlt = 'ocaml', mly = 'ocaml', mll = 'ocaml', -- cgit From 29b80f6f2e9391b5d78390f65d09f00f73829310 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 25 Nov 2022 10:08:15 +0100 Subject: vim-patch:9.0.0935: when using dash it may not be recognize as filetype "sh" (#21174) * vim-patch:9.0.0935: when using dash it may not be recognize as filetype "sh" Problem: When using dash it may not be recognize as filetype "sh". Solution: Add checks for "dash". (Eisuke Kawashima,closes vim/vim#11600) https://github.com/vim/vim/commit/24482fbfd599d2273c48951df7d00d62f3e66c85 Co-authored-by: Eisuke Kawashima Co-authored-by: zeertzjq --- runtime/lua/vim/filetype/detect.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 51ea1971cb..a5f20b61a6 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1166,13 +1166,14 @@ function M.sh(path, contents, name) vim.b[b].is_bash = nil vim.b[b].is_sh = nil end - elseif vim.g.bash_is_sh or matchregex(name, [[\]]) or matchregex(name, [[\]]) then + elseif vim.g.bash_is_sh or matchregex(name, [[\<\(bash\|bash2\)\>]]) then on_detect = function(b) vim.b[b].is_bash = 1 vim.b[b].is_kornshell = nil vim.b[b].is_sh = nil end - elseif matchregex(name, [[\]]) then + -- Ubuntu links sh to dash + elseif matchregex(name, [[\<\(sh\|dash\)\>]]) then on_detect = function(b) vim.b[b].is_sh = 1 vim.b[b].is_kornshell = nil @@ -1460,8 +1461,8 @@ local function match_from_hashbang(contents, path) name = 'wish' end - if matchregex(name, [[^\(bash\d*\|\|ksh\d*\|sh\)\>]]) then - -- Bourne-like shell scripts: bash bash2 ksh ksh93 sh + if matchregex(name, [[^\(bash\d*\|dash\|ksh\d*\|sh\)\>]]) then + -- Bourne-like shell scripts: bash bash2 dash ksh ksh93 sh return require('vim.filetype.detect').sh(path, contents, first_line) elseif matchregex(name, [[^csh\>]]) then return require('vim.filetype.detect').shell(path, contents, vim.g.filetype_csh or 'csh') -- cgit From 9dfbbde240fc095b856d8e0e1c670b1912ec6640 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 26 Nov 2022 00:52:30 +0100 Subject: docs: fix typos (#21168) --- 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 c7c053852d..d128c15233 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -73,7 +73,7 @@ end --- searches are recursive and may search through many directories! If {stop} --- is non-nil, then the search stops when the directory given in {stop} is --- reached. The search terminates when {limit} (default 1) matches are found. ---- The search can be narrowed to find only files or or only directories by +--- The search can be narrowed to find only files or only directories by --- specifying {type} to be "file" or "directory", respectively. --- ---@param names (string|table|fun(name: string): boolean) Names of the files -- cgit From 319fbffc94896dd9cf0a77891d69b7fcada1fad4 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 26 Nov 2022 14:35:40 +0100 Subject: vim-patch:9.0.0952: Eclipse preference files are not recognized (#21199) Problem: Eclipse preference files are not recognized. Solution: Add a pattern to use "jproperties" for Eclipse preference files. (closes vim/vim#11618) https://github.com/vim/vim/commit/f3f198b6349fe252b72975701e2f17d932b19c70 Co-authored-by: ObserverOfTime --- 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 5e086c8abf..dbad747f8f 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1909,6 +1909,7 @@ local pattern = { ['Prl.*%..*'] = starsetf('jam'), ['.*%.properties_..'] = 'jproperties', ['.*%.properties_.._..'] = 'jproperties', + ['org%.eclipse%..*%.prefs'] = 'jproperties', ['.*%.properties_.._.._.*'] = starsetf('jproperties'), ['Kconfig%..*'] = starsetf('kconfig'), ['.*%.[Ss][Uu][Bb]'] = 'krl', -- cgit From f004812b338340e5f5157aa68d09d3f0e5605c6c Mon Sep 17 00:00:00 2001 From: Jlll1 Date: Mon, 28 Nov 2022 20:23:04 +0100 Subject: feat(secure): add `:trust` command and vim.secure.trust() (#21107) Introduce vim.secure.trust() to programmatically manage the trust database. Use this function in a new :trust ex command which can be used as a simple frontend. Resolves: https://github.com/neovim/neovim/issues/21092 Co-authored-by: Gregory Anders Co-authored-by: ii14 --- runtime/lua/vim/secure.lua | 139 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 26 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 341ff8df05..0b4d7d53a2 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -1,9 +1,50 @@ local M = {} +---@private +--- Reads trust database from $XDG_STATE_HOME/nvim/trust. +--- +---@return (table) Contents of trust database, if it exists. Empty table otherwise. +local function read_trust() + local trust = {} + local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r') + if f then + local contents = f:read('*a') + if contents then + for line in vim.gsplit(contents, '\n') do + local hash, file = string.match(line, '^(%S+) (.+)$') + if hash and file then + trust[file] = hash + end + end + end + f:close() + end + return trust +end + +---@private +--- Writes provided {trust} table to trust database at +--- $XDG_STATE_HOME/nvim/trust. +--- +---@param trust (table) Trust table to write +local function write_trust(trust) + vim.validate({ trust = { trust, 't' } }) + local f = assert(io.open(vim.fn.stdpath('state') .. '/trust', 'w')) + + local t = {} + for p, h in pairs(trust) do + t[#t + 1] = string.format('%s %s\n', h, p) + end + f:write(table.concat(t)) + f:close() +end + --- Attempt to read the file at {path} prompting the user if the file should be --- trusted. The user's choice is persisted in a trust database at --- $XDG_STATE_HOME/nvim/trust. --- +---@see |:trust| +--- ---@param path (string) Path to a file to read. --- ---@return (string|nil) The contents of the given file if it exists and is @@ -15,22 +56,7 @@ function M.read(path) return nil end - local trust = {} - do - local f = io.open(vim.fn.stdpath('state') .. '/trust', 'r') - if f then - local contents = f:read('*a') - if contents then - for line in vim.gsplit(contents, '\n') do - local hash, file = string.match(line, '^(%S+) (.+)$') - if hash and file then - trust[file] = hash - end - end - end - f:close() - end - end + local trust = read_trust() if trust[fullpath] == '!' then -- File is denied @@ -86,21 +112,82 @@ function M.read(path) trust[fullpath] = hash end - do - local f, err = io.open(vim.fn.stdpath('state') .. '/trust', 'w') - if not f then - error(err) + write_trust(trust) + + return contents +end + +--- Manage the trust database. +--- +--- The trust database is located at |$XDG_STATE_HOME|/nvim/trust. +--- +---@param opts (table): +--- - action (string): "allow" to add a file to the trust database and trust it, +--- "deny" to add a file to the trust database and deny it, +--- "remove" to remove file from the trust database +--- - path (string|nil): Path to a file to update. Mutually exclusive with {bufnr}. +--- Cannot be used when {action} is "allow". +--- - bufnr (number|nil): Buffer number to update. Mutually exclusive with {path}. +---@return (boolean, string) success, msg: +--- - true and full path of target file if operation was successful +--- - false and error message on failure +function M.trust(opts) + vim.validate({ + path = { opts.path, 's', true }, + bufnr = { opts.bufnr, 'n', true }, + action = { + opts.action, + function(m) + return m == 'allow' or m == 'deny' or m == 'remove' + end, + [["allow" or "deny" or "remove"]], + }, + }) + + local path = opts.path + local bufnr = opts.bufnr + local action = opts.action + + if path and bufnr then + error('path and bufnr are mutually exclusive', 2) + end + + local fullpath + if path then + fullpath = vim.loop.fs_realpath(vim.fs.normalize(path)) + else + local bufname = vim.api.nvim_buf_get_name(bufnr) + if bufname == '' then + return false, 'buffer is not associated with a file' end + fullpath = vim.loop.fs_realpath(vim.fs.normalize(bufname)) + end + + if not fullpath then + return false, string.format('invalid path: %s', path) + end + + local trust = read_trust() + + if action == 'allow' then + assert(bufnr, 'bufnr is required when action is "allow"') - local t = {} - for p, h in pairs(trust) do - t[#t + 1] = string.format('%s %s\n', h, p) + local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n' + local contents = table.concat(vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), newline) + if vim.bo[bufnr].endofline then + contents = contents .. newline end - f:write(table.concat(t)) - f:close() + local hash = vim.fn.sha256(contents) + + trust[fullpath] = hash + elseif action == 'deny' then + trust[fullpath] = '!' + elseif action == 'remove' then + trust[fullpath] = nil end - return contents + write_trust(trust) + return true, fullpath end return M -- cgit From 80b6edabe3e4203ee4bf50261af07a6a0495ef36 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 28 Nov 2022 15:40:50 -0700 Subject: refactor: rework parameter validation in vim.secure.trust() (#21223) --- runtime/lua/vim/secure.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 0b4d7d53a2..08b1ff871c 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -148,19 +148,23 @@ function M.trust(opts) local bufnr = opts.bufnr local action = opts.action - if path and bufnr then - error('path and bufnr are mutually exclusive', 2) + assert(not path or not bufnr, '"path" and "bufnr" are mutually exclusive') + + if action == 'allow' then + assert(not path, '"path" is not valid when action is "allow"') end local fullpath if path then fullpath = vim.loop.fs_realpath(vim.fs.normalize(path)) - else + elseif bufnr then local bufname = vim.api.nvim_buf_get_name(bufnr) if bufname == '' then return false, 'buffer is not associated with a file' end fullpath = vim.loop.fs_realpath(vim.fs.normalize(bufname)) + else + error('one of "path" or "bufnr" is required') end if not fullpath then @@ -170,8 +174,6 @@ function M.trust(opts) local trust = read_trust() if action == 'allow' then - assert(bufnr, 'bufnr is required when action is "allow"') - local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n' local contents = table.concat(vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), newline) if vim.bo[bufnr].endofline then -- cgit From 615f124003376c007442319b31a172360796974c Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 29 Nov 2022 02:45:48 +0100 Subject: docs: fix typos (#21196) Co-authored-by: zeertzjq Co-authored-by: Raphael Co-authored-by: Gregory Anders --- runtime/lua/vim/lsp/sync.lua | 2 +- runtime/lua/vim/lsp/util.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 0d65e86b55..826352f036 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -392,7 +392,7 @@ end ---@param lastline number line to begin search in old_lines for last difference ---@param new_lastline number line to begin search in new_lines for last difference ---@param offset_encoding string encoding requested by language server ----@returns table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent +---@returns table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent function M.compute_diff( prev_lines, curr_lines, diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index d89757ef0c..ba9f145e01 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1679,7 +1679,7 @@ do --[[ References ]] ---@param bufnr number Buffer id ---@param references table List of `DocumentHighlight` objects to highlight ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32". - ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight + ---@see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentContentChangeEvent function M.buf_highlight_references(bufnr, references, offset_encoding) validate({ bufnr = { bufnr, 'n', true }, -- cgit From 61e99217e68498e757b9f8b0c70978a9635ccbfa Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 1 Dec 2022 09:15:05 -0600 Subject: refactor(fs): replace vim.fn/vim.env in vim.fs (#20379) Avoid using vim.env and vim.fn in vim.fs functions so that they can be used in "fast" contexts. --- runtime/lua/vim/fs.lua | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index d128c15233..8ea7f26575 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -1,5 +1,7 @@ local M = {} +local iswin = vim.loop.os_uname().sysname == 'Windows_NT' + --- Iterate over all the parents of the given file or directory. --- --- Example: @@ -40,7 +42,19 @@ function M.dirname(file) if file == nil then return nil end - return vim.fn.fnamemodify(file, ':h') + vim.validate({ file = { file, 's' } }) + if iswin and file:match('^%w:[\\/]?$') then + return (file:gsub('\\', '/')) + elseif not file:match('[\\/]') then + return '.' + elseif file == '/' or file:match('^/[^/]+$') then + return '/' + end + local dir = file:match('[/\\]$') and file:sub(1, #file - 1) or file:match('^([/\\]?.+)[/\\]') + if iswin and dir:match('^%w:$') then + return dir .. '/' + end + return (dir:gsub('\\', '/')) end --- Return the basename of the given file or directory @@ -48,7 +62,14 @@ end ---@param file (string) File or directory ---@return (string) Basename of {file} function M.basename(file) - return vim.fn.fnamemodify(file, ':t') + if file == nil then + return nil + end + vim.validate({ file = { file, 's' } }) + if iswin and file:match('^%w:[\\/]?$') then + return '' + end + return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/')) end --- Return an iterator over the files and directories located in {path} @@ -229,7 +250,12 @@ end ---@return (string) Normalized path function M.normalize(path) vim.validate({ path = { path, 's' } }) - return (path:gsub('^~/', vim.env.HOME .. '/'):gsub('%$([%w_]+)', vim.env):gsub('\\', '/')) + return ( + path + :gsub('^~/', vim.loop.os_homedir() .. '/') + :gsub('%$([%w_]+)', vim.loop.os_getenv) + :gsub('\\', '/') + ) end return M -- cgit From 0b05bd87c04f9cde5c84a062453619349e370795 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 23 Nov 2022 12:31:49 +0100 Subject: docs(gen): support language annotation in docstrings --- runtime/lua/vim/_editor.lua | 6 +++--- runtime/lua/vim/diagnostic.lua | 26 +++++++++++++------------- runtime/lua/vim/filetype.lua | 6 +++--- runtime/lua/vim/fs.lua | 13 ++++++------- runtime/lua/vim/keymap.lua | 8 ++++---- runtime/lua/vim/lsp.lua | 8 +++----- runtime/lua/vim/lsp/buf.lua | 19 +++++++++---------- runtime/lua/vim/lsp/codelens.lua | 3 ++- runtime/lua/vim/lsp/diagnostic.lua | 2 +- runtime/lua/vim/lsp/handlers.lua | 32 ++++++++++++++++---------------- runtime/lua/vim/shared.lua | 36 ++++++++++++++++++------------------ runtime/lua/vim/treesitter.lua | 2 +- runtime/lua/vim/treesitter/query.lua | 4 ++-- runtime/lua/vim/ui.lua | 4 ++-- 14 files changed, 83 insertions(+), 86 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index ad4dc20efb..2913a0ddc6 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -123,7 +123,7 @@ do --- (such as the |TUI|) pastes text into the editor. --- --- Example: To remove ANSI color codes when pasting: - ---
+  --- 
lua
   --- vim.paste = (function(overridden)
   ---   return function(lines, phase)
   ---     for i,line in ipairs(lines) do
@@ -280,7 +280,7 @@ end
 --- command.
 ---
 --- Example:
---- 
+--- 
lua
 ---   vim.cmd('echo 42')
 ---   vim.cmd([[
 ---     augroup My_group
@@ -746,7 +746,7 @@ end
 
 ---Prints given arguments in human-readable format.
 ---Example:
----
+---
lua
 ---  -- Print highlight group Normal and store it's contents in a variable.
 ---  local hl_normal = vim.pretty_print(vim.api.nvim_get_hl_by_name("Normal", true))
 ---
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 18df1f1586..7557bc79a2 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -579,12 +579,12 @@ end --- followed by namespace configuration, and finally global configuration. --- --- For example, if a user enables virtual text globally with ----
+--- 
lua
 ---   vim.diagnostic.config({ virtual_text = true })
 --- 
--- --- and a diagnostic producer sets diagnostics with ----
+--- 
lua
 ---   vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false })
 --- 
--- @@ -621,13 +621,13 @@ end --- * format: (function) A function that takes a diagnostic as input and --- returns a string. The return value is the text used to display --- the diagnostic. Example: ----
----                       function(diagnostic)
----                         if diagnostic.severity == vim.diagnostic.severity.ERROR then
----                           return string.format("E: %s", diagnostic.message)
+---                       
lua
+---                         function(diagnostic)
+---                           if diagnostic.severity == vim.diagnostic.severity.ERROR then
+---                             return string.format("E: %s", diagnostic.message)
+---                           end
+---                           return diagnostic.message
 ---                         end
----                         return diagnostic.message
----                       end
 ---                       
--- - signs: (default true) Use signs for diagnostics. Options: --- * severity: Only show signs for diagnostics matching the given severity @@ -1577,11 +1577,11 @@ end --- --- This can be parsed into a diagnostic |diagnostic-structure| --- with: ----
---- local s = "WARNING filename:27:3: Variable 'foo' does not exist"
---- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
---- local groups = { "severity", "lnum", "col", "message" }
---- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
+--- 
lua
+---   local s = "WARNING filename:27:3: Variable 'foo' does not exist"
+---   local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$"
+---   local groups = { "severity", "lnum", "col", "message" }
+---   vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN })
 --- 
--- ---@param str string String to parse diagnostics from. diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index dbad747f8f..e017843548 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2308,7 +2308,7 @@ end --- See $VIMRUNTIME/lua/vim/filetype.lua for more examples. --- --- Example: ----
+--- 
lua
 ---  vim.filetype.add({
 ---    extension = {
 ---      foo = 'fooscript',
@@ -2344,7 +2344,7 @@ end
 --- 
--- --- To add a fallback match on contents, use ----
+--- 
lua
 --- vim.filetype.add {
 ---   pattern = {
 ---     ['.*'] = {
@@ -2456,7 +2456,7 @@ end
 --- Each of the three options is specified using a key to the single argument of this function.
 --- Example:
 ---
---- 
+--- 
lua
 ---   -- Using a buffer number
 ---   vim.filetype.match({ buf = 42 })
 ---
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index d128c15233..acfe8821ea 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -3,7 +3,7 @@ local M = {}
 --- Iterate over all the parents of the given file or directory.
 ---
 --- Example:
---- 
+--- 
lua
 --- local root_dir
 --- for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
 ---   if vim.fn.isdirectory(dir .. "/.git") == 1 then
@@ -98,8 +98,7 @@ end
 ---                       - limit (number, default 1): Stop the search after
 ---                               finding this many matches. Use `math.huge` to
 ---                               place no limit on the number of matches.
----
----@return (table) The normalized paths |vim.fs.normalize()| of all matching files or directories
+---@return (table) Normalized paths |vim.fs.normalize()| of all matching files or directories
 function M.find(names, opts)
   opts = opts or {}
   vim.validate({
@@ -214,15 +213,15 @@ end
 --- variables are also expanded.
 ---
 --- Examples:
---- 
+--- 
lua
 ---   vim.fs.normalize('C:\\Users\\jdoe')
----   => 'C:/Users/jdoe'
+---   --> 'C:/Users/jdoe'
 ---
 ---   vim.fs.normalize('~/src/neovim')
----   => '/home/jdoe/src/neovim'
+---   --> '/home/jdoe/src/neovim'
 ---
 ---   vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
----   => '/Users/jdoe/.config/nvim/init.vim'
+---   --> '/Users/jdoe/.config/nvim/init.vim'
 --- 
--- ---@param path (string) Path to normalize diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index af41794c53..ef1c66ea20 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -2,7 +2,7 @@ local keymap = {} --- Add a new |mapping|. --- Examples: ----
+--- 
lua
 ---   -- Can add mapping to Lua functions
 ---   vim.keymap.set('n', 'lhs', function() print("real lua function") end)
 ---
@@ -21,13 +21,13 @@ local keymap = {}
 --- 
--- --- Note that in a mapping like: ----
+--- 
lua
 ---    vim.keymap.set('n', 'asdf', require('jkl').my_fun)
 --- 
--- --- the ``require('jkl')`` gets evaluated during this call in order to access the function. --- If you want to avoid this cost at startup you can wrap it in a function, for example: ----
+--- 
lua
 ---    vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end)
 --- 
--- @@ -93,7 +93,7 @@ end --- Remove an existing mapping. --- Examples: ----
+--- 
lua
 ---   vim.keymap.del('n', 'lhs')
 ---
 ---   vim.keymap.del({'n', 'i', 'v'}, 'w', { buffer = 5 })
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index d717275ae4..5bbe4aeba9 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -813,8 +813,7 @@ end
 --- Attaches the current buffer to the client.
 ---
 --- Example:
----
---- 
+--- 
lua
 --- vim.lsp.start({
 ---    name = 'my-server-name',
 ---    cmd = {'name-of-language-server-executable'},
@@ -1754,8 +1753,7 @@ end
 ---
 --- You can also use the `stop()` function on a |vim.lsp.client| object.
 --- To stop all clients:
----
---- 
+--- 
lua
 --- vim.lsp.stop_client(vim.lsp.get_active_clients())
 --- 
--- @@ -2239,7 +2237,7 @@ end ---@param fn function Function to run on each client attached to buffer --- {bufnr}. The function takes the client, client ID, and --- buffer number as arguments. Example: ----
+---             
lua
 ---               vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
 ---                 print(vim.inspect(client))
 ---               end)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index c593e72d62..a5a66fd092 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -162,11 +162,11 @@ end
 ---         Predicate used to filter clients. Receives a client as argument and must return a
 ---         boolean. Clients matching the predicate are included. Example:
 ---
----         
----         -- Never request typescript-language-server for formatting
----         vim.lsp.buf.format {
----           filter = function(client) return client.name ~= "tsserver" end
----         }
+---         
lua
+---           -- Never request typescript-language-server for formatting
+---           vim.lsp.buf.format {
+---             filter = function(client) return client.name ~= "tsserver" end
+---           }
 ---         
--- --- - async boolean|nil @@ -555,11 +555,10 @@ end --- Send request to the server to resolve document highlights for the current --- text document position. This request can be triggered by a key mapping or --- by events such as `CursorHold`, e.g.: ---- ----
---- autocmd CursorHold   lua vim.lsp.buf.document_highlight()
---- autocmd CursorHoldI  lua vim.lsp.buf.document_highlight()
---- autocmd CursorMoved  lua vim.lsp.buf.clear_references()
+--- 
vim
+---   autocmd CursorHold   lua vim.lsp.buf.document_highlight()
+---   autocmd CursorHoldI  lua vim.lsp.buf.document_highlight()
+---   autocmd CursorMoved  lua vim.lsp.buf.clear_references()
 --- 
--- --- Note: Usage of |vim.lsp.buf.document_highlight()| requires the following highlight groups diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 4fa02c8db2..71b6bfae30 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -241,7 +241,8 @@ end --- --- It is recommended to trigger this using an autocmd or via keymap. --- ----
+--- Example:
+--- 
vim
 ---   autocmd BufEnter,CursorHold,InsertLeave  lua vim.lsp.codelens.refresh()
 --- 
--- diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 1f9d084e2b..5e2bf75f1b 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -150,7 +150,7 @@ end --- --- See |vim.diagnostic.config()| for configuration options. Handler-specific --- configuration can be set using |vim.lsp.with()|: ----
+--- 
lua
 --- vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
 ---   vim.lsp.diagnostic.on_publish_diagnostics, {
 ---     -- Enable underline, use default values
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 39e2577294..e0162218f1 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -313,15 +313,15 @@ M['textDocument/completion'] = function(_, result, _, _)
 end
 
 --- |lsp-handler| for the method "textDocument/hover"
---- 
---- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
----   vim.lsp.handlers.hover, {
----     -- Use a sharp border with `FloatBorder` highlights
----     border = "single",
----     -- add the title in hover float window
----     title = "hover"
----   }
---- )
+--- 
lua
+---   vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
+---     vim.lsp.handlers.hover, {
+---       -- Use a sharp border with `FloatBorder` highlights
+---       border = "single",
+---       -- add the title in hover float window
+---       title = "hover"
+---     }
+---   )
 --- 
---@param config table Configuration table. --- - border: (default=nil) @@ -399,13 +399,13 @@ M['textDocument/implementation'] = location_handler --- |lsp-handler| for the method "textDocument/signatureHelp". --- The active parameter is highlighted with |hl-LspSignatureActiveParameter|. ----
---- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
----   vim.lsp.handlers.signature_help, {
----     -- Use a sharp border with `FloatBorder` highlights
----     border = "single"
----   }
---- )
+--- 
lua
+---   vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
+---     vim.lsp.handlers.signature_help, {
+---       -- Use a sharp border with `FloatBorder` highlights
+---       border = "single"
+---     }
+---   )
 --- 
---@param config table Configuration table. --- - border: (default=nil) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index f4a57c13c8..9129500866 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -102,11 +102,11 @@ end --- Splits a string at each instance of a separator. --- --- Examples: ----
----  split(":aa::b:", ":")     => {'','aa','','b',''}
----  split("axaby", "ab?")     => {'','x','y'}
----  split("x*yz*o", "*", {plain=true})  => {'x','yz','o'}
----  split("|x|y|z|", "|", {trimempty=true}) => {'x', 'y', 'z'}
+--- 
lua
+---  split(":aa::b:", ":")                   --> {'','aa','','b',''}
+---  split("axaby", "ab?")                   --> {'','x','y'}
+---  split("x*yz*o", "*", {plain=true})      --> {'x','yz','o'}
+---  split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
 --- 
--- ---@see |vim.gsplit()| @@ -383,7 +383,7 @@ end --- Return `nil` if the key does not exist. --- --- Examples: ----
+--- 
lua
 ---  vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
 ---  vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
 --- 
@@ -495,9 +495,9 @@ end --- Counts the number of non-nil values in table `t`. --- ----
---- vim.tbl_count({ a=1, b=2 }) => 2
---- vim.tbl_count({ 1, 2 }) => 2
+--- 
lua
+--- vim.tbl_count({ a=1, b=2 })  --> 2
+--- vim.tbl_count({ 1, 2 })      --> 2
 --- 
--- ---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua @@ -571,7 +571,7 @@ end --- Validates a parameter specification (types and values). --- --- Usage example: ----
+--- 
lua
 ---  function user.new(name, age, hobbies)
 ---    vim.validate{
 ---      name={name, 'string'},
@@ -583,24 +583,24 @@ end
 --- 
--- --- Examples with explicit argument values (can be run directly): ----
+--- 
lua
 ---  vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
----     => NOP (success)
+---     --> NOP (success)
 ---
 ---  vim.validate{arg1={1, 'table'}}
----     => error('arg1: expected table, got number')
+---     --> error('arg1: expected table, got number')
 ---
 ---  vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
----     => error('arg1: expected even number, got 3')
+---     --> error('arg1: expected even number, got 3')
 --- 
--- --- If multiple types are valid they can be given as a list. ----
+--- 
lua
 ---  vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
----     => NOP (success)
+---     --> NOP (success)
 ---
 ---  vim.validate{arg1={1, {'string', table'}}}
----     => error('arg1: expected string|table, got number')
+---     --> error('arg1: expected string|table, got number')
 ---
 --- 
--- @@ -735,7 +735,7 @@ end --- If {create} is `nil`, this will create a defaulttable whose constructor function is --- this function, effectively allowing to create nested tables on the fly: --- ----
+--- 
lua
 --- local a = vim.defaulttable()
 --- a.b.c = 1
 --- 
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 6cd00516bf..0603ddb421 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -313,7 +313,7 @@ end --- In this case, add ``vim.bo.syntax = 'on'`` after the call to `start`. --- --- Example: ----
+--- 
lua
 --- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
 ---     callback = function(args)
 ---         vim.treesitter.start(args.buf, 'latex')
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 7ca7384a88..4bec5db527 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -549,7 +549,7 @@ end
 --- The iterator returns three values: a numeric id identifying the capture,
 --- the captured node, and metadata from any directives processing the match.
 --- The following example shows how to get captures by name:
---- 
+--- 
lua
 --- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
 ---   local name = query.captures[id] -- name of the capture in the query
 ---   -- typically useful info about the node:
@@ -603,7 +603,7 @@ end
 --- If the query has more than one pattern, the capture table might be sparse
 --- and e.g. `pairs()` method should be used over `ipairs`.
 --- Here is an example iterating over all captures in every match:
---- 
+--- 
lua
 --- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
 ---   for id, node in pairs(match) do
 ---     local name = query.captures[id]
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index 97dccd83ab..8f5be15221 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -21,7 +21,7 @@ local M = {}
 ---
 ---
 --- Example:
---- 
+--- 
lua
 --- vim.ui.select({ 'tabs', 'spaces' }, {
 ---     prompt = 'Select tabs or spaces:',
 ---     format_item = function(item)
@@ -78,7 +78,7 @@ end
 ---               `nil` if the user aborted the dialog.
 ---
 --- Example:
---- 
+--- 
lua
 --- vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
 ---     vim.o.shiftwidth = tonumber(input)
 --- end)
-- 
cgit 


From c768b578faba671beab435954dc4e5a321c94728 Mon Sep 17 00:00:00 2001
From: Folke Lemaitre 
Date: Sat, 3 Dec 2022 13:51:57 +0100
Subject: fix(lsp): render 
{lang} code blocks and set separator default to
 false (#21271)

---
 runtime/lua/vim/lsp/util.lua | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

(limited to 'runtime/lua/vim')

diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index ba9f145e01..e96e98f23c 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1261,7 +1261,7 @@ function M.stylize_markdown(bufnr, contents, opts)
   -- when ft is nil, we get the ft from the regex match
   local matchers = {
     block = { nil, '```+([a-zA-Z0-9_]*)', '```+' },
-    pre = { '', '
', '
' }, + pre = { nil, '
([a-z0-9]*)', '
' }, code = { '', '', '' }, text = { 'text', '', '' }, } @@ -1286,8 +1286,6 @@ function M.stylize_markdown(bufnr, contents, opts) -- Clean up contents = M._trim(contents, opts) - -- Insert blank line separator after code block? - local add_sep = opts.separator == nil and true or opts.separator local stripped = {} local highlights = {} -- keep track of lnums that contain markdown @@ -1315,7 +1313,7 @@ function M.stylize_markdown(bufnr, contents, opts) finish = #stripped, }) -- add a separator, but not on the last line - if add_sep and i < #contents then + if opts.separator and i < #contents then table.insert(stripped, '---') markdown_lines[#stripped] = true end -- cgit From 01a8cd0432a272c41e882af96ee8488a4105bd32 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sun, 4 Dec 2022 21:56:04 +0800 Subject: fix(lsp): remove workspaceFolders field (#21284) --- runtime/lua/vim/lsp.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5bbe4aeba9..9595f0b12c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1285,8 +1285,6 @@ function lsp.start_client(config) client.initialized = true uninitialized_clients[client_id] = nil client.workspace_folders = workspace_folders - -- TODO(mjlbach): Backwards compatibility, to be removed in 0.7 - client.workspaceFolders = client.workspace_folders -- These are the cleaned up capabilities we use for dynamically deciding -- when to send certain events to clients. -- cgit From 67e1390dc8eb584d26ae9c9634c05acb3b7e37ca Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Sun, 4 Dec 2022 15:57:46 +0100 Subject: fix(lsp): call show_document with correct args Closes https://github.com/neovim/neovim/issues/21177 --- runtime/lua/vim/lsp/handlers.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index e0162218f1..80df83732e 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -579,7 +579,10 @@ M['window/showDocument'] = function(_, result, ctx, _) range = result.selection, } - local success = util.show_document(location, client.offset_encoding, true, result.takeFocus) + local success = util.show_document(location, client.offset_encoding, { + reuse_win = true, + focus = result.takeFocus, + }) return { success = success or false } end -- cgit From b098e7971fdf8ed3f7d0c52aff0ce126c34ff3c8 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 4 Dec 2022 18:02:24 +0100 Subject: fix(lsp): ensure open_logfile is safe for fast events (#21288) Closes https://github.com/neovim/neovim/issues/21052 --- runtime/lua/vim/lsp/log.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 6c6ba0f206..dd9f7d42a4 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -20,6 +20,17 @@ local format_func = function(arg) end do + ---@private + local function notify(msg, level) + if vim.in_fast_event() then + vim.schedule(function() + vim.notify(msg, level) + end) + else + vim.notify(msg, level) + end + end + local path_sep = vim.loop.os_uname().version:match('Windows') and '\\' or '/' ---@private local function path_join(...) @@ -53,7 +64,7 @@ do logfile, openerr = io.open(logfilename, 'a+') if not logfile then local err_msg = string.format('Failed to open LSP client log file: %s', openerr) - vim.notify(err_msg, vim.log.levels.ERROR) + notify(err_msg, vim.log.levels.ERROR) return false end @@ -64,7 +75,7 @@ do log_info.size / (1000 * 1000), logfilename ) - vim.notify(warn_msg) + notify(warn_msg) end -- Start message for logging -- cgit From f3bf1fbf600050fde155e6a1a766b6f848012208 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Mon, 5 Dec 2022 19:59:04 +0100 Subject: fix(secure): crash when hitting escape in prompt (#21283) - use pcall when calling vim.secure.read from C - catch keyboard interrupts in vim.secure.read, rethrow other errors - selecting "view" in prompt runs :view command - simplify lua stack cleanup with lua_gettop and lua_settop Co-authored-by: ii14 --- runtime/lua/vim/secure.lua | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 08b1ff871c..443b152273 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -80,34 +80,27 @@ function M.read(path) end -- File either does not exist in trust database or the hash does not match - local choice = vim.fn.confirm( + local ok, result = pcall( + vim.fn.confirm, string.format('%s is not trusted.', fullpath), '&ignore\n&view\n&deny\n&allow', 1 ) - if choice == 0 or choice == 1 then + if not ok and result ~= 'Keyboard interrupt' then + error(result) + elseif not ok or result == 0 or result == 1 then -- Cancelled or ignored return nil - elseif choice == 2 then + elseif result == 2 then -- View - vim.cmd('new') - local buf = vim.api.nvim_get_current_buf() - local lines = vim.split(string.gsub(contents, '\n$', ''), '\n') - vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) - vim.bo[buf].bufhidden = 'hide' - vim.bo[buf].buftype = 'nofile' - vim.bo[buf].swapfile = false - vim.bo[buf].modeline = false - vim.bo[buf].buflisted = false - vim.bo[buf].readonly = true - vim.bo[buf].modifiable = false + vim.cmd('sview ' .. fullpath) return nil - elseif choice == 3 then + elseif result == 3 then -- Deny trust[fullpath] = '!' contents = nil - elseif choice == 4 then + elseif result == 4 then -- Allow trust[fullpath] = hash end -- cgit From 0ff9131a925dfc94cf0ce787578ee6e3c5e673d7 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 5 Dec 2022 15:54:32 -0700 Subject: vim-patch:9.0.1014: zir files are not recognized (#21301) Problem: Zir files are not recognized. Solution: Add a pattern for Zir files. (closes vim/vim#11664) https://github.com/vim/vim/commit/25201016d5043954689a4c9f7833935294149404 Co-authored-by: Bram Moolenaar --- 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 e017843548..58acca42f7 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1122,6 +1122,7 @@ local extension = { yang = 'yang', ['z8a'] = 'z8a', zig = 'zig', + zir = 'zir', zu = 'zimbu', zut = 'zimbutempl', zsh = 'zsh', -- cgit From 54305443b9cd5ac2c2220f12e01a653e8064c3a4 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 8 Dec 2022 10:55:01 +0100 Subject: feat(lsp): support willSave & willSaveWaitUntil capability (#21315) `willSaveWaitUntil` allows servers to respond with text edits before saving a document. That is used by some language servers to format a document or apply quick fixes like removing unused imports. --- runtime/lua/vim/lsp.lua | 32 ++++++++++++++++++++++++++++++-- runtime/lua/vim/lsp/protocol.lua | 12 +++++------- 2 files changed, 35 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9595f0b12c..9c42e9df52 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1611,9 +1611,37 @@ function lsp.buf_attach_client(bufnr, client_id) all_buffer_active_clients[bufnr] = buffer_client_ids local uri = vim.uri_from_bufnr(bufnr) - local augroup = ('lsp_c_%d_b_%d_did_save'):format(client_id, bufnr) + local augroup = ('lsp_c_%d_b_%d_save'):format(client_id, bufnr) + local group = api.nvim_create_augroup(augroup, { clear = true }) + api.nvim_create_autocmd('BufWritePre', { + group = group, + buffer = bufnr, + desc = 'vim.lsp: textDocument/willSave', + callback = function(ctx) + for_each_buffer_client(ctx.buf, function(client) + local params = { + textDocument = { + uri = uri, + }, + reason = protocol.TextDocumentSaveReason.Manual, + } + if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSave') then + client.notify('textDocument/willSave', params) + end + if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSaveWaitUntil') then + local result, err = + client.request_sync('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 + log.error(vim.inspect(err)) + end + end + end) + end, + }) api.nvim_create_autocmd('BufWritePost', { - group = api.nvim_create_augroup(augroup, { clear = true }), + group = group, buffer = bufnr, desc = 'vim.lsp: textDocument/didSave handler', callback = function(ctx) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 8dc93b3b67..925115d056 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -151,6 +151,7 @@ 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. @@ -631,11 +632,8 @@ function protocol.make_client_capabilities() synchronization = { dynamicRegistration = false, - -- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre) - willSave = false, - - -- TODO(ashkan) Implement textDocument/willSaveWaitUntil - willSaveWaitUntil = false, + willSave = true, + willSaveWaitUntil = true, -- Send textDocument/didSave after saving (BufWritePost) didSave = true, @@ -870,8 +868,8 @@ function protocol._resolve_capabilities_compat(server_capabilities) text_document_sync_properties = { text_document_open_close = if_nil(textDocumentSync.openClose, false), text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None), - text_document_will_save = if_nil(textDocumentSync.willSave, false), - text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false), + text_document_will_save = if_nil(textDocumentSync.willSave, true), + text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, true), text_document_save = if_nil(textDocumentSync.save, false), text_document_save_include_text = if_nil( type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText, -- cgit From 50ffb8d7f4d9e1b080efbf5eb04595f0399db9e5 Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 8 Dec 2022 20:28:28 +0800 Subject: refactor(lsp): remove deprecated vim.lsp.buf_get_clients calls (#21337) --- runtime/lua/vim/lsp/buf.lua | 6 +++--- runtime/lua/vim/lsp/util.lua | 2 +- 2 files changed, 4 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 a5a66fd092..c476f2754f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -464,7 +464,7 @@ end --- function M.list_workspace_folders() local workspace_folders = {} - for _, client in pairs(vim.lsp.buf_get_clients()) do + for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do for _, folder in pairs(client.workspace_folders or {}) do table.insert(workspace_folders, folder.name) end @@ -489,7 +489,7 @@ function M.add_workspace_folder(workspace_folder) { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, { {} } ) - for _, client in pairs(vim.lsp.buf_get_clients()) do + for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do local found = false for _, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then @@ -522,7 +522,7 @@ function M.remove_workspace_folder(workspace_folder) { {} }, { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } ) - for _, client in pairs(vim.lsp.buf_get_clients()) do + for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do for idx, folder in pairs(client.workspace_folders) do if folder.name == workspace_folder then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e96e98f23c..ddcdc31c1c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1931,7 +1931,7 @@ function M._get_offset_encoding(bufnr) local offset_encoding - for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do + for _, client in pairs(vim.lsp.get_active_clients({ buffer = bufnr })) do if client.offset_encoding == nil then vim.notify_once( string.format( -- cgit From d44699800cd0dbf14fb45476c13b6cc3c993b5c7 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:22:57 -0700 Subject: feat(treesitter): add vim.treesitter.show_tree() (#21322) Add a "show_tree" function to view a textual representation of the nodes in a language tree in a window. Moving the cursor in the window highlights the corresponding text in the source buffer, and moving the cursor in the source buffer highlights the corresponding nodes in the window. --- runtime/lua/vim/treesitter.lua | 195 +++++++++++++++++++++++++++- runtime/lua/vim/treesitter/languagetree.lua | 4 +- runtime/lua/vim/treesitter/playground.lua | 184 ++++++++++++++++++++++++++ 3 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 runtime/lua/vim/treesitter/playground.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 0603ddb421..5c9210cc2d 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -277,7 +277,7 @@ end ---@param opts table Optional keyword arguments: --- - ignore_injections boolean Ignore injected languages (default true) --- ----@return userdata |tsnode| under the cursor +---@return userdata|nil |tsnode| under the cursor function M.get_node_at_pos(bufnr, row, col, opts) if bufnr == 0 then bufnr = a.nvim_get_current_buf() @@ -347,4 +347,197 @@ function M.stop(bufnr) vim.bo[bufnr].syntax = 'on' end +--- Open a window that displays a textual representation of the nodes in the language tree. +--- +--- While in the window, press "a" to toggle display of anonymous nodes, "I" to toggle the +--- display of the source language of each node, and press to jump to the node under the +--- cursor in the source buffer. +--- +---@param opts table|nil Optional options table with the following possible keys: +--- - bufnr (number|nil): Buffer to draw the tree into. If omitted, a new +--- buffer is created. +--- - winid (number|nil): Window id to display the tree buffer in. If omitted, +--- a new window is created with {command}. +--- - command (string|nil): Vimscript command to create the window. Default +--- value is "topleft 60vnew". Only used when {winid} is nil. +--- - title (string|fun(bufnr:number):string|nil): Title of the window. If a +--- function, it accepts the buffer number of the source buffer as its only +--- argument and should return a string. +function M.show_tree(opts) + vim.validate({ + opts = { opts, 't', true }, + }) + + local Playground = require('vim.treesitter.playground') + local buf = a.nvim_get_current_buf() + local win = a.nvim_get_current_win() + local pg = assert(Playground:new(buf)) + + opts = opts or {} + + -- Close any existing playground window + if vim.b[buf].playground then + local w = vim.b[buf].playground + if a.nvim_win_is_valid(w) then + a.nvim_win_close(w, true) + end + end + + local w = opts.winid + if not w then + vim.cmd(opts.command or 'topleft 60vnew') + w = a.nvim_get_current_win() + end + + local b = opts.bufnr + if b then + a.nvim_win_set_buf(w, b) + else + b = a.nvim_win_get_buf(w) + end + + vim.b[buf].playground = w + + vim.wo[w].scrolloff = 5 + vim.wo[w].wrap = false + vim.bo[b].buflisted = false + vim.bo[b].buftype = 'nofile' + vim.bo[b].bufhidden = 'wipe' + + local title = opts.title + if not title then + local bufname = a.nvim_buf_get_name(buf) + title = string.format('Syntax tree for %s', vim.fn.fnamemodify(bufname, ':.')) + elseif type(title) == 'function' then + title = title(buf) + end + + assert(type(title) == 'string', 'Window title must be a string') + a.nvim_buf_set_name(b, title) + + pg:draw(b) + + vim.fn.matchadd('Comment', '\\[[0-9:-]\\+\\]') + vim.fn.matchadd('String', '".*"') + + a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + a.nvim_buf_set_keymap(b, 'n', '', '', { + desc = 'Jump to the node under the cursor in the source buffer', + callback = function() + local row = a.nvim_win_get_cursor(w)[1] + local pos = pg:get(row) + a.nvim_set_current_win(win) + a.nvim_win_set_cursor(win, { pos.lnum + 1, pos.col }) + end, + }) + a.nvim_buf_set_keymap(b, 'n', 'a', '', { + desc = 'Toggle anonymous nodes', + callback = function() + pg.opts.anon = not pg.opts.anon + pg:draw(b) + end, + }) + a.nvim_buf_set_keymap(b, 'n', 'I', '', { + desc = 'Toggle language display', + callback = function() + pg.opts.lang = not pg.opts.lang + pg:draw(b) + end, + }) + + local group = a.nvim_create_augroup('treesitter/playground', {}) + + a.nvim_create_autocmd('CursorMoved', { + group = group, + buffer = b, + callback = function() + a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + local row = a.nvim_win_get_cursor(w)[1] + local pos = pg:get(row) + a.nvim_buf_set_extmark(buf, pg.ns, pos.lnum, pos.col, { + end_row = pos.end_lnum, + end_col = math.max(0, pos.end_col), + hl_group = 'Visual', + }) + end, + }) + + a.nvim_create_autocmd('CursorMoved', { + group = group, + buffer = buf, + callback = function() + if not a.nvim_buf_is_loaded(b) then + return true + end + + a.nvim_buf_clear_namespace(b, pg.ns, 0, -1) + + local cursor = a.nvim_win_get_cursor(win) + local cursor_node = + M.get_node_at_pos(buf, cursor[1] - 1, cursor[2], { ignore_injections = false }) + if not cursor_node then + return + end + + local cursor_node_id = cursor_node:id() + for i, v in pg:iter() do + if v.id == cursor_node_id then + local start = v.depth + local end_col = start + #v.text + a.nvim_buf_set_extmark(b, pg.ns, i - 1, start, { + end_col = end_col, + hl_group = 'Visual', + }) + a.nvim_win_set_cursor(w, { i, 0 }) + break + end + end + end, + }) + + a.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, { + group = group, + buffer = buf, + callback = function() + if not a.nvim_buf_is_loaded(b) then + return true + end + + pg = assert(Playground:new(buf)) + pg:draw(b) + end, + }) + + a.nvim_create_autocmd('BufLeave', { + group = group, + buffer = b, + callback = function() + a.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + end, + }) + + a.nvim_create_autocmd('BufLeave', { + group = group, + buffer = buf, + callback = function() + if not a.nvim_buf_is_loaded(b) then + return true + end + + a.nvim_buf_clear_namespace(b, pg.ns, 0, -1) + end, + }) + + a.nvim_create_autocmd('BufHidden', { + group = group, + buffer = buf, + once = true, + callback = function() + if a.nvim_win_is_valid(w) then + a.nvim_win_close(w, true) + end + end, + }) +end + return M diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index e9d70c4204..a1e96f8ef2 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -608,7 +608,9 @@ end ---@return userdata|nil Found |tsnode| function LanguageTree:named_node_for_range(range, opts) local tree = self:tree_for_range(range, opts) - return tree:root():named_descendant_for_range(unpack(range)) + if tree then + return tree:root():named_descendant_for_range(unpack(range)) + end end --- Gets the appropriate language that contains {range}. diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua new file mode 100644 index 0000000000..325d303df5 --- /dev/null +++ b/runtime/lua/vim/treesitter/playground.lua @@ -0,0 +1,184 @@ +local api = vim.api + +local M = {} + +---@class Playground +---@field opts table Options table with the following keys: +--- - anon (boolean): If true, display anonymous nodes +--- - lang (boolean): If true, display the language alongside each node +--- +---@class Node +---@field id number Node id +---@field text string Node text +---@field named boolean True if this is a named (non-anonymous) node +---@field depth number Depth of the node within the tree +---@field lnum number Beginning line number of this node in the source buffer +---@field col number Beginning column number of this node in the source buffer +---@field end_lnum number Final line number of this node in the source buffer +---@field end_col number Final column number of this node in the source buffer +---@field lang string Source language of this node + +--- Traverse all child nodes starting at {node}. +--- +--- This is a recursive function. The {depth} parameter indicates the current recursion level. +--- {lang} is a string indicating the language of the tree currently being traversed. Each traversed +--- node is added to {tree}. When recursion completes, {tree} is an array of all nodes in the order +--- they were visited. +--- +--- {injections} is a table mapping node ids from the primary tree to language tree injections. Each +--- injected language has a series of trees nested within the primary language's tree, and the root +--- node of each of these trees is contained within a node in the primary tree. The {injections} +--- table maps nodes in the primary tree to root nodes of injected trees. +--- +---@param node userdata Starting node to begin traversal |tsnode| +---@param depth number Current recursion depth +---@param lang string Language of the tree currently being traversed +---@param injections table Mapping of node ids to root nodes of injected language trees (see +--- explanation above) +---@param tree Node[] Output table containing a list of tables each representing a node in the tree +---@private +local function traverse(node, depth, lang, injections, tree) + local injection = injections[node:id()] + if injection then + traverse(injection.root, depth, injection.lang, injections, tree) + end + + for child, field in node:iter_children() do + local type = child:type() + local lnum, col, end_lnum, end_col = child:range() + local named = child:named() + local text + if named then + if field then + text = string.format('%s: (%s)', field, type) + else + text = string.format('(%s)', type) + end + else + text = string.format('"%s"', type:gsub('\n', '\\n')) + end + + table.insert(tree, { + id = child:id(), + text = text, + named = named, + depth = depth, + lnum = lnum, + col = col, + end_lnum = end_lnum, + end_col = end_col, + lang = lang, + }) + + traverse(child, depth + 1, lang, injections, tree) + end + + return tree +end + +--- Create a new Playground object. +--- +---@param bufnr number Source buffer number +--- +---@return Playground|nil +---@return string|nil Error message, if any +--- +---@private +function M.new(self, bufnr) + local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0) + if not ok then + return nil, 'No parser available for the given buffer' + end + + -- For each child tree (injected language), find the root of the tree and locate the node within + -- the primary tree that contains that root. Add a mapping from the node in the primary tree to + -- the root in the child tree to the {injections} table. + local root = parser:parse()[1]:root() + local injections = {} + parser:for_each_child(function(child, lang) + child:for_each_tree(function(tree) + local r = tree:root() + local node = root:named_descendant_for_range(r:range()) + if node then + injections[node:id()] = { + lang = lang, + root = r, + } + end + end) + end) + + local nodes = traverse(root, 0, parser:lang(), injections, {}) + + local named = {} + for _, v in ipairs(nodes) do + if v.named then + named[#named + 1] = v + end + end + + local t = { + ns = api.nvim_create_namespace(''), + nodes = nodes, + named = named, + opts = { + anon = false, + lang = false, + }, + } + + setmetatable(t, self) + self.__index = self + return t +end + +--- Write the contents of this Playground into {bufnr}. +--- +---@param bufnr number Buffer number to write into. +---@private +function M.draw(self, bufnr) + vim.bo[bufnr].modifiable = true + local lines = {} + for _, item in self:iter() do + lines[#lines + 1] = table.concat({ + string.rep(' ', item.depth), + item.text, + item.lnum == item.end_lnum + and string.format(' [%d:%d-%d]', item.lnum + 1, item.col + 1, item.end_col) + or string.format( + ' [%d:%d-%d:%d]', + item.lnum + 1, + item.col + 1, + item.end_lnum + 1, + item.end_col + ), + self.opts.lang and string.format(' %s', item.lang) or '', + }) + end + api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + vim.bo[bufnr].modifiable = false +end + +--- Get node {i} from this Playground object. +--- +--- The node number is dependent on whether or not anonymous nodes are displayed. +--- +---@param i number Node number to get +---@return Node +---@private +function M.get(self, i) + local t = self.opts.anon and self.nodes or self.named + return t[i] +end + +--- Iterate over all of the nodes in this Playground object. +--- +---@return function Iterator over all nodes in this Playground +---@return table +---@return number +---@private +function M.iter(self) + return ipairs(self.opts.anon and self.nodes or self.named) +end + +return M -- cgit From 42009ac7df88bfffeea49a83e642fdc6cf9f9447 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:51:46 -0700 Subject: feat(treesitter): add 'lang' option to show_tree() (#21341) This is necessary for now to support filetypes that use a parser with a different name (e.g. the "terraform" filetype uses the "hcl" parser). --- runtime/lua/vim/treesitter.lua | 19 ++++++++++++------- runtime/lua/vim/treesitter/playground.lua | 10 ++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 5c9210cc2d..7813c2edb2 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -275,6 +275,7 @@ end ---@param row number Position row ---@param col number Position column ---@param opts table Optional keyword arguments: +--- - lang string|nil Parser language --- - ignore_injections boolean Ignore injected languages (default true) --- ---@return userdata|nil |tsnode| under the cursor @@ -284,7 +285,7 @@ function M.get_node_at_pos(bufnr, row, col, opts) end local ts_range = { row, col, row, col } - local root_lang_tree = M.get_parser(bufnr) + local root_lang_tree = M.get_parser(bufnr, opts.lang) if not root_lang_tree then return end @@ -354,6 +355,8 @@ end --- cursor in the source buffer. --- ---@param opts table|nil Optional options table with the following possible keys: +--- - lang (string|nil): The language of the source buffer. If omitted, the +--- filetype of the source buffer is used. --- - bufnr (number|nil): Buffer to draw the tree into. If omitted, a new --- buffer is created. --- - winid (number|nil): Window id to display the tree buffer in. If omitted, @@ -368,12 +371,12 @@ function M.show_tree(opts) opts = { opts, 't', true }, }) + opts = opts or {} + local Playground = require('vim.treesitter.playground') local buf = a.nvim_get_current_buf() local win = a.nvim_get_current_win() - local pg = assert(Playground:new(buf)) - - opts = opts or {} + local pg = assert(Playground:new(buf, opts.lang)) -- Close any existing playground window if vim.b[buf].playground then @@ -473,8 +476,10 @@ function M.show_tree(opts) a.nvim_buf_clear_namespace(b, pg.ns, 0, -1) local cursor = a.nvim_win_get_cursor(win) - local cursor_node = - M.get_node_at_pos(buf, cursor[1] - 1, cursor[2], { ignore_injections = false }) + local cursor_node = M.get_node_at_pos(buf, cursor[1] - 1, cursor[2], { + lang = opts.lang, + ignore_injections = false, + }) if not cursor_node then return end @@ -503,7 +508,7 @@ function M.show_tree(opts) return true end - pg = assert(Playground:new(buf)) + pg = assert(Playground:new(buf, opts.lang)) pg:draw(b) end, }) diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index 325d303df5..bb073290c6 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -3,6 +3,7 @@ local api = vim.api local M = {} ---@class Playground +---@field ns number API namespace ---@field opts table Options table with the following keys: --- - anon (boolean): If true, display anonymous nodes --- - lang (boolean): If true, display the language alongside each node @@ -79,13 +80,14 @@ end --- Create a new Playground object. --- ---@param bufnr number Source buffer number +---@param lang string|nil Language of source buffer --- ---@return Playground|nil ---@return string|nil Error message, if any --- ---@private -function M.new(self, bufnr) - local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0) +function M.new(self, bufnr, lang) + local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang) if not ok then return nil, 'No parser available for the given buffer' end @@ -95,13 +97,13 @@ function M.new(self, bufnr) -- the root in the child tree to the {injections} table. local root = parser:parse()[1]:root() local injections = {} - parser:for_each_child(function(child, lang) + parser:for_each_child(function(child, lang_) child:for_each_tree(function(tree) local r = tree:root() local node = root:named_descendant_for_range(r:range()) if node then injections[node:id()] = { - lang = lang, + lang = lang_, root = r, } end -- cgit From 9f035559defd9d575f37fd825954610065d9cf96 Mon Sep 17 00:00:00 2001 From: John Drouhard Date: Wed, 23 Nov 2022 10:06:36 -0600 Subject: feat(lsp): initial support for semantic token highlighting * credit to @smolck and @theHamsta for their contributions in laying the groundwork for this feature and for their work on some of the helper utility functions and tests --- runtime/lua/vim/highlight.lua | 1 + runtime/lua/vim/lsp.lua | 9 + runtime/lua/vim/lsp/protocol.lua | 55 +++ runtime/lua/vim/lsp/semantic_tokens.lua | 644 ++++++++++++++++++++++++++++++++ 4 files changed, 709 insertions(+) create mode 100644 runtime/lua/vim/lsp/semantic_tokens.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index 0fde515bd9..20ad48dd27 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -5,6 +5,7 @@ local M = {} M.priorities = { syntax = 50, treesitter = 100, + semantic_tokens = 125, diagnostics = 150, user = 200, } diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9c42e9df52..3d3c856fcb 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -4,6 +4,7 @@ local lsp_rpc = require('vim.lsp.rpc') local protocol = require('vim.lsp.protocol') local util = require('vim.lsp.util') local sync = require('vim.lsp.sync') +local semantic_tokens = require('vim.lsp.semantic_tokens') local api = vim.api local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option, nvim_exec_autocmds = @@ -25,6 +26,7 @@ local lsp = { buf = require('vim.lsp.buf'), diagnostic = require('vim.lsp.diagnostic'), codelens = require('vim.lsp.codelens'), + semantic_tokens = semantic_tokens, util = util, -- Allow raw RPC access. @@ -56,6 +58,8 @@ lsp._request_name_to_capability = { ['textDocument/formatting'] = { 'documentFormattingProvider' }, ['textDocument/completion'] = { 'completionProvider' }, ['textDocument/documentHighlight'] = { 'documentHighlightProvider' }, + ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' }, + ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' }, } -- TODO improve handling of scratch buffers with LSP attached. @@ -1526,6 +1530,11 @@ function lsp.start_client(config) -- TODO(ashkan) handle errors. pcall(config.on_attach, client, bufnr) end + + if vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then + semantic_tokens.start(bufnr, client.id) + end + client.attached_buffers[bufnr] = true end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 925115d056..dfbd01b8f8 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -629,6 +629,58 @@ export interface WorkspaceClientCapabilities { function protocol.make_client_capabilities() return { textDocument = { + semanticTokens = { + dynamicRegistration = false, + tokenTypes = { + 'namespace', + 'type', + 'class', + 'enum', + 'interface', + 'struct', + 'typeParameter', + 'parameter', + 'variable', + 'property', + 'enumMember', + 'event', + 'function', + 'method', + 'macro', + 'keyword', + 'modifier', + 'comment', + 'string', + 'number', + 'regexp', + 'operator', + 'decorator', + }, + tokenModifiers = { + 'declaration', + 'definition', + 'readonly', + 'static', + 'deprecated', + 'abstract', + 'async', + 'modification', + 'documentation', + 'defaultLibrary', + }, + formats = { 'relative' }, + requests = { + -- TODO(jdrouhard): Add support for this + range = false, + full = { delta = true }, + }, + + overlappingTokenSupport = true, + -- TODO(jdrouhard): Add support for this + multilineTokenSupport = false, + serverCancelSupport = false, + augmentsSyntaxTokens = true, + }, synchronization = { dynamicRegistration = false, @@ -772,6 +824,9 @@ function protocol.make_client_capabilities() workspaceEdit = { resourceOperations = { 'rename', 'create', 'delete' }, }, + semanticTokens = { + refreshSupport = true, + }, }, callHierarchy = { dynamicRegistration = false, diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua new file mode 100644 index 0000000000..99cdc20f54 --- /dev/null +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -0,0 +1,644 @@ +local api = vim.api +local handlers = require('vim.lsp.handlers') +local util = require('vim.lsp.util') + +--- @class STTokenRange +--- @field line number line number 0-based +--- @field start_col number start column 0-based +--- @field end_col number end column 0-based +--- @field type string token type as string +--- @field modifiers string[] token modifiers as strings +--- @field extmark_added boolean whether this extmark has been added to the buffer yet +--- +--- @class STCurrentResult +--- @field version number document version associated with this result +--- @field result_id string resultId from the server; used with delta requests +--- @field highlights STTokenRange[] cache of highlight ranges for this document version +--- @field tokens number[] raw token array as received by the server. used for calculating delta responses +--- @field namespace_cleared boolean whether the namespace was cleared for this result yet +--- +--- @class STActiveRequest +--- @field request_id number the LSP request ID of the most recent request sent to the server +--- @field version number the document version associated with the most recent request +--- +--- @class STClientState +--- @field namespace number +--- @field active_request STActiveRequest +--- @field current_result STCurrentResult + +---@class STHighlighter +---@field active table +---@field bufnr number +---@field augroup number augroup for buffer events +---@field debounce number milliseconds to debounce requests for new tokens +---@field timer table uv_timer for debouncing requests for new tokens +---@field client_state table +local STHighlighter = { active = {} } + +---@private +local function binary_search(tokens, line) + local lo = 1 + local hi = #tokens + while lo < hi do + local mid = math.floor((lo + hi) / 2) + if tokens[mid].line < line then + lo = mid + 1 + else + hi = mid + end + end + return lo +end + +--- Extracts modifier strings from the encoded number in the token array +--- +---@private +---@return string[] +local function modifiers_from_number(x, modifiers_table) + ---@private + local function _get_bit(n, k) + --TODO(jdrouhard): remove once `bit` module is available for non-LuaJIT + if _G.bit then + return _G.bit.band(_G.bit.rshift(n, k), 1) + else + return math.floor((n / math.pow(2, k)) % 2) + end + end + + local modifiers = {} + for i = 0, #modifiers_table - 1 do + local b = _get_bit(x, i) + if b == 1 then + modifiers[#modifiers + 1] = modifiers_table[i + 1] + end + end + + return modifiers +end + +--- Converts a raw token list to a list of highlight ranges used by the on_win callback +--- +---@private +---@return STTokenRange[] +local function tokens_to_ranges(data, bufnr, client) + local legend = client.server_capabilities.semanticTokensProvider.legend + local token_types = legend.tokenTypes + local token_modifiers = legend.tokenModifiers + local ranges = {} + + local line + local start_char = 0 + for i = 1, #data, 5 do + local delta_line = data[i] + line = line and line + delta_line or delta_line + local delta_start = data[i + 1] + start_char = delta_line == 0 and start_char + delta_start or delta_start + + -- data[i+3] +1 because Lua tables are 1-indexed + local token_type = token_types[data[i + 3] + 1] + local modifiers = modifiers_from_number(data[i + 4], token_modifiers) + + ---@private + local function _get_byte_pos(char_pos) + return util._get_line_byte_from_position(bufnr, { + line = line, + character = char_pos, + }, client.offset_encoding) + end + + local start_col = _get_byte_pos(start_char) + local end_col = _get_byte_pos(start_char + data[i + 2]) + + if token_type then + ranges[#ranges + 1] = { + line = line, + start_col = start_col, + end_col = end_col, + type = token_type, + modifiers = modifiers, + extmark_added = false, + } + end + end + + return ranges +end + +--- Construct a new STHighlighter for the buffer +--- +---@private +---@param bufnr number +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.client_state = {} + + STHighlighter.active[bufnr] = self + + api.nvim_buf_attach(bufnr, false, { + on_lines = function(_, buf) + local highlighter = STHighlighter.active[buf] + if not highlighter then + return true + end + highlighter:on_change() + end, + on_reload = function(_, buf) + local highlighter = STHighlighter.active[buf] + if highlighter then + highlighter:reset() + highlighter:send_request() + end + end, + on_detach = function(_, buf) + local highlighter = STHighlighter.active[buf] + if highlighter then + highlighter:destroy() + end + end, + }) + + api.nvim_create_autocmd({ 'BufWinEnter', 'InsertLeave' }, { + buffer = self.bufnr, + group = self.augroup, + callback = function() + self:send_request() + end, + }) + + api.nvim_create_autocmd('LspDetach', { + buffer = self.bufnr, + group = self.augroup, + callback = function(args) + self:detach(args.data.client_id) + if vim.tbl_isempty(self.client_state) then + self:destroy() + end + end, + }) + + return self +end + +---@private +function STHighlighter:destroy() + for client_id, _ in pairs(self.client_state) do + self:detach(client_id) + end + + api.nvim_del_augroup_by_id(self.augroup) + STHighlighter.active[self.bufnr] = nil +end + +---@private +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), + active_request = {}, + current_result = {}, + } + self.client_state[client_id] = state + end +end + +---@private +function STHighlighter:detach(client_id) + local state = self.client_state[client_id] + if state then + --TODO: delete namespace if/when that becomes possible + api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) + self.client_state[client_id] = nil + end +end + +--- This is the entry point for getting all the tokens in a buffer. +--- +--- For the given clients (or all attached, if not provided), this sends a request +--- to ask for semantic tokens. If the server supports delta requests, that will +--- be prioritized if we have a previous requestId and token array. +--- +--- This function will skip servers where there is an already an active request in +--- flight for the same version. If there is a stale request in flight, that is +--- cancelled prior to sending a new one. +--- +--- Finally, if the request was successful, the requestId and document version +--- are saved to facilitate document synchronization in the response. +--- +---@private +function STHighlighter:send_request() + local version = util.buf_versions[self.bufnr] + + self:reset_timer() + + for client_id, state in pairs(self.client_state) do + local client = vim.lsp.get_client_by_id(client_id) + + local current_result = state.current_result + local active_request = state.active_request + + -- Only send a request for this client if the current result is out of date and + -- there isn't a current a request in flight for this version + 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) + active_request = {} + state.active_request = active_request + end + + local spec = client.server_capabilities.semanticTokensProvider.full + local hasEditProvider = type(spec) == 'table' and spec.delta + + local params = { textDocument = util.make_text_document_params(self.bufnr) } + local method = 'textDocument/semanticTokens/full' + + if hasEditProvider and current_result.result_id then + method = method .. '/delta' + params.previousResultId = current_result.result_id + end + 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) + local highlighter = STHighlighter.active[ctx.bufnr] + if not err and c and highlighter then + highlighter:process_response(response, c, version) + end + end, self.bufnr) + + if success then + active_request.request_id = request_id + active_request.version = version + end + end + end +end + +--- This function will parse the semantic token responses and set up the cache +--- (current_result). It also performs document synchronization by checking the +--- version of the document associated with the resulting request_id and only +--- performing work if the response is not out-of-date. +--- +--- Delta edits are applied if necessary, and new highlight ranges are calculated +--- and stored in the buffer state. +--- +--- Finally, a redraw command is issued to force nvim to redraw the screen to +--- pick up changed highlight tokens. +--- +---@private +function STHighlighter:process_response(response, client, version) + local state = self.client_state[client.id] + if not state then + return + end + + -- ignore stale responses + if state.active_request.version and version ~= state.active_request.version then + return + end + + -- reset active request + state.active_request = {} + + -- if we have a response to a delta request, update the state of our tokens + -- appropriately. if it's a full response, just use that + local tokens + local token_edits = response.edits + if token_edits then + table.sort(token_edits, function(a, b) + return a.start < b.start + end) + + ---@private + local function _splice(list, start, remove_count, data) + local ret = vim.list_slice(list, 1, start) + vim.list_extend(ret, data) + vim.list_extend(ret, list, start + remove_count + 1) + return ret + end + + tokens = state.current_result.tokens + for _, token_edit in ipairs(token_edits) do + tokens = _splice(tokens, token_edit.start, token_edit.deleteCount, token_edit.data) + end + else + tokens = response.data + end + + -- Update the state with the new results + local current_result = state.current_result + current_result.version = version + current_result.result_id = response.resultId + current_result.tokens = tokens + current_result.highlights = tokens_to_ranges(tokens, self.bufnr, client) + current_result.namespace_cleared = false + + api.nvim_command('redraw!') +end + +--- on_win handler for the decoration provider (see |nvim_set_decoration_provider|) +--- +--- If there is a current result for the buffer and the version matches the +--- current document version, then the tokens are valid and can be applied. As +--- the buffer is drawn, this function will add extmark highlights for every +--- token in the range of visible lines. Once a highlight has been added, it +--- sticks around until the document changes and there's a new set of matching +--- highlight tokens available. +--- +--- If this is the first time a buffer is being drawn with a new set of +--- highlights for the current document version, the namespace is cleared to +--- remove extmarks from the last version. It's done here instead of the response +--- handler to avoid the "blink" that occurs due to the timing between the +--- response handler and the actual redraw. +--- +---@private +function STHighlighter:on_win(topline, botline) + for _, state in pairs(self.client_state) do + local current_result = state.current_result + if current_result.version and current_result.version == util.buf_versions[self.bufnr] then + if not current_result.namespace_cleared then + api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) + current_result.namespace_cleared = true + end + + -- We can't use ephemeral extmarks because the buffer updates are not in + -- sync with the list of semantic tokens. There's a delay between the + -- buffer changing and when the LSP server can respond with updated + -- tokens, and we don't want to "blink" the token highlights while + -- updates are in flight, and we don't want to use stale tokens because + -- they likely won't line up right with the actual buffer. + -- + -- Instead, we have to use normal extmarks that can attach to locations + -- in the buffer and are persisted between redraws. + local highlights = current_result.highlights + local idx = binary_search(highlights, topline) + + for i = idx, #highlights do + local token = highlights[i] + + if token.line > botline then + break + end + + if not token.extmark_added then + -- `strict = false` is necessary here for the 1% of cases where the + -- current result doesn't actually match the buffer contents. Some + -- LSP servers can respond with stale tokens on requests if they are + -- still processing changes from a didChange notification. + -- + -- LSP servers that do this _should_ follow up known stale responses + -- with a refresh notification once they've finished processing the + -- didChange notification, which would re-synchronize the tokens from + -- our end. + -- + -- The server I know of that does this is clangd when the preamble of + -- a file changes and the token request is processed with a stale + -- preamble while the new one is still being built. Once the preamble + -- finishes, clangd sends a refresh request which lets the client + -- re-synchronize the tokens. + api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, { + hl_group = '@' .. token.type, + end_col = token.end_col, + priority = vim.highlight.priorities.semantic_tokens, + strict = false, + }) + + --TODO(jdrouhard): do something with the modifiers + + token.extmark_added = true + end + end + end + end +end + +--- Reset the buffer's highlighting state and clears the extmark highlights. +--- +---@private +function STHighlighter:reset() + for client_id, state in pairs(self.client_state) do + api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) + state.current_result = {} + 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) + state.active_request = {} + end + end +end + +--- Mark a client's results as dirty. This method will cancel any active +--- requests to the server and pause new highlights from being added +--- in the on_win callback. The rest of the current results are saved +--- in case the server supports delta requests. +--- +---@private +---@param client_id number +function STHighlighter:mark_dirty(client_id) + local state = self.client_state[client_id] + assert(state) + + -- if we clear the version from current_result, it'll cause the + -- next request to be sent and will also pause new highlights + -- from being added in on_win until a new result comes from + -- the server + if state.current_result then + state.current_result.version = nil + end + + 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) + state.active_request = {} + end +end + +---@private +function STHighlighter:on_change() + self:reset_timer() + if self.debounce > 0 then + self.timer = vim.defer_fn(function() + self:send_request() + end, self.debounce) + else + self:send_request() + end +end + +---@private +function STHighlighter:reset_timer() + local timer = self.timer + if timer then + self.timer = nil + if not timer:is_closing() then + timer:stop() + timer:close() + end + end +end + +local M = {} + +--- Start the semantic token highlighting engine for the given buffer with the +--- given client. The client must already be attached to the buffer. +--- +--- NOTE: This is currently called automatically by |vim.lsp.buf_attach_client()|. To +--- opt-out of semantic highlighting with a server that supports it, you can +--- delete the semanticTokensProvider table from the {server_capabilities} of +--- your client in your |LspAttach| callback or your configuration's +--- `on_attach` callback. +--- +---
lua
+---   client.server_capabilities.semanticTokensProvider = nil
+--- 
+--- +---@param bufnr number +---@param client_id number +---@param opts (nil|table) Optional keyword arguments +--- - debounce (number, default: 200): Debounce token requests +--- to the server by the given number in milliseconds +function M.start(bufnr, client_id, opts) + vim.validate({ + bufnr = { bufnr, 'n', false }, + client_id = { client_id, 'n', false }, + }) + + opts = opts or {} + assert( + (not opts.debounce or type(opts.debounce) == 'number'), + 'opts.debounce must be a number with the debounce time in milliseconds' + ) + + local client = vim.lsp.get_client_by_id(client_id) + if not client then + vim.notify('[LSP] No client with id ' .. client_id, vim.log.levels.ERROR) + return + end + + if not vim.lsp.buf_is_attached(bufnr, client_id) then + vim.notify( + '[LSP] Client with id ' .. client_id .. ' not attached to buffer ' .. bufnr, + vim.log.levels.WARN + ) + return + end + + if not vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then + vim.notify('[LSP] Server does not support semantic tokens', vim.log.levels.WARN) + return + end + + local highlighter = STHighlighter.active[bufnr] + + if not highlighter then + highlighter = STHighlighter.new(bufnr) + highlighter.debounce = opts.debounce or 200 + else + highlighter.debounce = math.max(highlighter.debounce, opts.debounce or 200) + end + + highlighter:attach(client_id) + highlighter:send_request() +end + +--- Stop the semantic token highlighting engine for the given buffer with the +--- given client. +--- +--- NOTE: This is automatically called by a |LspDetach| autocmd that is set up as part +--- of `start()`, so you should only need this function to manually disengage the semantic +--- token engine without fully detaching the LSP client from the buffer. +--- +---@param bufnr number +---@param client_id number +function M.stop(bufnr, client_id) + vim.validate({ + bufnr = { bufnr, 'n', false }, + client_id = { client_id, 'n', false }, + }) + + local highlighter = STHighlighter.active[bufnr] + if not highlighter then + return + end + + highlighter:detach(client_id) + + if vim.tbl_isempty(highlighter.client_state) then + highlighter:destroy() + end +end + +--- Force a refresh of all semantic tokens +--- +--- Only has an effect if the buffer is currently active for semantic token +--- highlighting (|vim.lsp.semantic_tokens.start()| has been called for it) +--- +---@param bufnr (nil|number) default: current buffer +function M.force_refresh(bufnr) + vim.validate({ + bufnr = { bufnr, 'n', true }, + }) + + if bufnr == nil or bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + + local highlighter = STHighlighter.active[bufnr] + if not highlighter then + return + end + + highlighter:reset() + highlighter:send_request() +end + +--- |lsp-handler| for the method `workspace/semanticTokens/refresh` +--- +--- Refresh requests are sent by the server to indicate a project-wide change +--- that requires all tokens to be re-requested by the client. This handler will +--- invalidate the current results of all buffers and automatically kick off a +--- new request for buffers that are displayed in a window. For those that aren't, a +--- the BufWinEnter event should take care of it next time it's displayed. +--- +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#semanticTokens_refreshRequest +handlers['workspace/semanticTokens/refresh'] = function(err, _, ctx) + if err then + return vim.NIL + end + + for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do + local highlighter = STHighlighter.active[bufnr] + if highlighter and highlighter.client_state[ctx.client_id] then + highlighter:mark_dirty(ctx.client_id) + + if not vim.tbl_isempty(vim.fn.win_findbuf(bufnr)) then + highlighter:send_request() + end + end + end + + return vim.NIL +end + +local namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens') +api.nvim_set_decoration_provider(namespace, { + on_win = function(_, _, bufnr, topline, botline) + local highlighter = STHighlighter.active[bufnr] + if highlighter then + highlighter:on_win(topline, botline) + end + end, +}) + +--- for testing only! there is no guarantee of API stability with this! +--- +---@private +M.__STHighlighter = STHighlighter + +return M -- cgit From 5e6a288ce7ee079e7695525f2e9e99d071ccdfbf Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Fri, 9 Dec 2022 04:54:09 -0600 Subject: fix(lsp): followup fixes for semantic tokens support (#21357) 1. The algorithm for applying edits was slightly incorrect. It needs to preserve the original token list as the edits are applied instead of mutating it as it iterates. From the spec: Semantic token edits behave conceptually like text edits on documents: if an edit description consists of n edits all n edits are based on the same state Sm of the number array. They will move the number array from state Sm to Sm+1. 2. Schedule the semantic token engine start() call in the client._on_attach() function so that users who schedule_wrap() their config.on_attach() functions (like nvim-lspconfig does) can still disable semantic tokens by deleting the semanticTokensProvider from their server capabilities. --- runtime/lua/vim/lsp.lua | 11 ++++++++--- runtime/lua/vim/lsp/semantic_tokens.lua | 17 +++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 3d3c856fcb..f3ee484024 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1531,9 +1531,14 @@ function lsp.start_client(config) pcall(config.on_attach, client, bufnr) end - if vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then - semantic_tokens.start(bufnr, client.id) - end + -- schedule the initialization of semantic tokens to give the above + -- on_attach and LspAttach callbacks the ability to schedule wrap the + -- opt-out (deleting the semanticTokensProvider from capabilities) + vim.schedule(function() + if vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then + semantic_tokens.start(bufnr, client.id) + end + end) client.attached_buffers[bufnr] = true end diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 99cdc20f54..66e656abb6 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -313,18 +313,15 @@ function STHighlighter:process_response(response, client, version) return a.start < b.start end) - ---@private - local function _splice(list, start, remove_count, data) - local ret = vim.list_slice(list, 1, start) - vim.list_extend(ret, data) - vim.list_extend(ret, list, start + remove_count + 1) - return ret - end - - tokens = state.current_result.tokens + tokens = {} + local old_tokens = state.current_result.tokens + local idx = 1 for _, token_edit in ipairs(token_edits) do - tokens = _splice(tokens, token_edit.start, token_edit.deleteCount, token_edit.data) + vim.list_extend(tokens, old_tokens, idx, token_edit.start) + vim.list_extend(tokens, token_edit.data) + idx = token_edit.start + token_edit.deleteCount + 1 end + vim.list_extend(tokens, old_tokens, idx) else tokens = response.data end -- cgit From 49df92da9459bea9eec356d23cea20a0a2383d68 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 9 Dec 2022 19:18:31 +0100 Subject: fix(lsp): correct some type annotations (#21365) --- runtime/lua/vim/lsp.lua | 66 ++++++++++++++++++++++++++------------------- runtime/lua/vim/lsp/log.lua | 2 +- 2 files changed, 39 insertions(+), 29 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index f3ee484024..dc5008399e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -67,7 +67,7 @@ lsp._request_name_to_capability = { ---@private --- Concatenates and writes a list of strings to the Vim error buffer. --- ----@param {...} (List of strings) List to write to the buffer +---@param {...} table[] List to write to the buffer local function err_message(...) nvim_err_writeln(table.concat(vim.tbl_flatten({ ... }))) nvim_command('redraw') @@ -76,7 +76,7 @@ end ---@private --- Returns the buffer number for the given {bufnr}. --- ----@param bufnr (number) Buffer number to resolve. Defaults to the current +---@param bufnr (number|nil) Buffer number to resolve. Defaults to the current ---buffer if not given. ---@returns bufnr (number) Number of requested buffer local function resolve_bufnr(bufnr) @@ -244,9 +244,9 @@ end ---@private --- Augments a validator function with support for optional (nil) values. --- ----@param fn (function(v)) The original validator function; should return a +---@param fn (fun(v)) The original validator function; should return a ---bool. ----@returns (function(v)) The augmented function. Also returns true if {v} is +---@returns (fun(v)) The augmented function. Also returns true if {v} is ---`nil`. local function optional_validator(fn) return function(v) @@ -1366,7 +1366,7 @@ function lsp.start_client(config) --- ---@param method (string) LSP method name. ---@param params (table) LSP request params. - ---@param handler (function, optional) Response |lsp-handler| for this method. + ---@param handler (function|nil) Response |lsp-handler| for this method. ---@param bufnr (number) Buffer handle (0 for current). ---@returns ({status}, [request_id]): {status} is a bool indicating ---whether the request was successful. If it is `false`, then it will @@ -1377,8 +1377,10 @@ function lsp.start_client(config) ---@see |vim.lsp.buf_request()| function client.request(method, params, handler, bufnr) if not handler then - handler = resolve_handler(method) - or error(string.format('not found: %q request handler for client %q.', method, client.name)) + handler = assert( + resolve_handler(method), + string.format('not found: %q request handler for client %q.', method, client.name) + ) end -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(client, bufnr) @@ -1396,7 +1398,7 @@ function lsp.start_client(config) nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false }) end) - if success then + if success and request_id then client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method } nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false }) end @@ -1411,8 +1413,8 @@ function lsp.start_client(config) --- ---@param method (string) LSP method name. ---@param params (table) LSP request params. - ---@param timeout_ms (number, optional, default=1000) Maximum time in - ---milliseconds to wait for a result. + ---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for + --- a result. Defaults to 1000 ---@param bufnr (number) Buffer handle (0 for current). ---@returns { err=err, result=result }, a dictionary, where `err` and `result` come from the |lsp-handler|. ---On timeout, cancel or error, returns `(nil, err)` where `err` is a @@ -1435,7 +1437,9 @@ function lsp.start_client(config) end, 10) if not wait_result then - client.cancel_request(request_id) + if request_id then + client.cancel_request(request_id) + end return nil, wait_result_reason[reason] end return request_result @@ -1800,7 +1804,7 @@ end --- By default asks the server to shutdown, unless stop was requested --- already for this client, then force-shutdown is attempted. --- ----@param client_id client id or |vim.lsp.client| object, or list thereof +---@param client_id number|table id or |vim.lsp.client| object, or list thereof ---@param force boolean (optional) shutdown forcefully function lsp.stop_client(client_id, force) local ids = type(client_id) == 'table' and client_id or { client_id } @@ -1815,10 +1819,16 @@ function lsp.stop_client(client_id, force) end end +---@class vim.lsp.get_active_clients.filter +---@field id number|nil Match clients by id +---@field bufnr number|nil match clients attached to the given buffer +---@field name number|nil match clients by name + --- Get active clients. --- ----@param filter (table|nil) A table with key-value pairs used to filter the ---- returned clients. The available keys are: +---@param filter vim.lsp.get_active_clients.filter|nil (table|nil) A table with +--- key-value pairs used to filter the returned clients. +--- The available keys are: --- - id (number): Only return clients with the given id --- - bufnr (number): Only return clients attached to this buffer --- - name (string): Only return clients with the given name @@ -1966,7 +1976,7 @@ end --- ---@param bufnr (number) Buffer handle, or 0 for current. ---@param method (string) LSP method name ----@param params (optional, table) Parameters to send to the server +---@param params (table|nil) Parameters to send to the server ---@param callback (function) The callback to call when all requests are finished. -- Unlike `buf_request`, this will collect all the responses from each server instead of handling them. -- A map of client_id:request_result will be provided to the callback @@ -2008,9 +2018,9 @@ end --- ---@param bufnr (number) Buffer handle, or 0 for current. ---@param method (string) LSP method name ----@param params (optional, table) Parameters to send to the server ----@param timeout_ms (optional, number, default=1000) Maximum time in ---- milliseconds to wait for a result. +---@param params (table|nil) Parameters to send to the server +---@param timeout_ms (number|nil) Maximum time in milliseconds to wait for a +--- result. Defaults to 1000 --- ---@returns Map of client_id:request_result. On timeout, cancel or error, --- returns `(nil, err)` where `err` is a string describing the failure @@ -2035,9 +2045,9 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms) end --- Send a notification to a server ----@param bufnr [number] (optional): The number of the buffer ----@param method [string]: Name of the request method ----@param params [string]: Arguments to send to the server +---@param bufnr (number|nil) The number of the buffer +---@param method (string) Name of the request method +---@param params (string) Arguments to send to the server --- ---@returns true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) @@ -2078,8 +2088,8 @@ end ---@see |complete-items| ---@see |CompleteDone| --- ----@param findstart 0 or 1, decides behavior ----@param base If findstart=0, text to match against +---@param findstart number 0 or 1, decides behavior +---@param base number findstart=0, text to match against --- ---@returns (number) Decided by {findstart}: --- - findstart=0: column where the completion starts, or -2 or -3 @@ -2208,8 +2218,8 @@ end --- Otherwise, uses "workspace/symbol". If no results are returned from --- any LSP servers, falls back to using built-in tags. --- ----@param pattern Pattern used to find a workspace symbol ----@param flags See |tag-function| +---@param pattern string Pattern used to find a workspace symbol +---@param flags string See |tag-function| --- ---@returns A list of matching tags function lsp.tagfunc(...) @@ -2218,7 +2228,7 @@ end ---Checks whether a client is stopped. --- ----@param client_id (Number) +---@param client_id (number) ---@returns true if client is stopped, false otherwise. function lsp.client_is_stopped(client_id) return active_clients[client_id] == nil @@ -2227,7 +2237,7 @@ end --- Gets a map of client_id:client pairs for the given buffer, where each value --- is a |vim.lsp.client| object. --- ----@param bufnr (optional, number): Buffer handle, or 0 for current +---@param bufnr (number|nil): Buffer handle, or 0 for current ---@returns (table) Table of (client_id, client) pairs ---@deprecated Use |vim.lsp.get_active_clients()| instead. function lsp.buf_get_clients(bufnr) @@ -2256,7 +2266,7 @@ lsp.log_levels = log.levels --- ---@see |vim.lsp.log_levels| --- ----@param level [number|string] the case insensitive level name or number +---@param level (number|string) the case insensitive level name or number function lsp.set_log_level(level) if type(level) == 'string' or type(level) == 'number' then log.set_level(level) diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index dd9f7d42a4..d1a78572aa 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -141,7 +141,7 @@ end vim.tbl_add_reverse_lookup(log.levels) --- Sets the current log level. ----@param level (string or number) One of `vim.lsp.log.levels` +---@param level (string|number) One of `vim.lsp.log.levels` function log.set_level(level) if type(level) == 'string' then current_log_level = -- cgit From 8b84a10db76ef2bd15bbd3c06ae2d5dfaadc1482 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 9 Dec 2022 22:02:04 +0100 Subject: fix(lsp): fix get_active_clients bufnr parameter (#21366) Follow up to https://github.com/neovim/neovim/pull/21337 --- runtime/lua/vim/lsp/buf.lua | 6 +++--- runtime/lua/vim/lsp/util.lua | 2 +- 2 files changed, 4 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 c476f2754f..226ed980fb 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -464,7 +464,7 @@ end --- function M.list_workspace_folders() local workspace_folders = {} - for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do + for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do for _, folder in pairs(client.workspace_folders or {}) do table.insert(workspace_folders, folder.name) end @@ -489,7 +489,7 @@ function M.add_workspace_folder(workspace_folder) { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, { {} } ) - for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do + for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do local found = false for _, folder in pairs(client.workspace_folders or {}) do if folder.name == workspace_folder then @@ -522,7 +522,7 @@ function M.remove_workspace_folder(workspace_folder) { {} }, { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } } ) - for _, client in pairs(vim.lsp.get_active_clients({ buffer = 0 })) do + for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do for idx, folder in pairs(client.workspace_folders) do if folder.name == workspace_folder then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ddcdc31c1c..2bd15c87d9 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1931,7 +1931,7 @@ function M._get_offset_encoding(bufnr) local offset_encoding - for _, client in pairs(vim.lsp.get_active_clients({ buffer = bufnr })) do + for _, client in pairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do if client.offset_encoding == nil then vim.notify_once( string.format( -- cgit From 6d37d8cb17390419360c1459607beac2d93183b6 Mon Sep 17 00:00:00 2001 From: fsouza <108725+fsouza@users.noreply.github.com> Date: Sat, 10 Dec 2022 06:16:33 -0500 Subject: fix(lsp): ignore null responses for semanticTokens request (#21364) The spec indicates that the response may be `null`, but it doesn't really say what a `null` response means. Since neovim raises an error if the response is `null`, I figured that ignoring it would be the safest bet. Co-authored-by: Mathias Fussenegger --- runtime/lua/vim/lsp/semantic_tokens.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 66e656abb6..83b414bf87 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -304,6 +304,11 @@ function STHighlighter:process_response(response, client, version) -- reset active request state.active_request = {} + -- skip nil responses + if response == nil then + return + end + -- if we have a response to a delta request, update the state of our tokens -- appropriately. if it's a full response, just use that local tokens -- cgit From 1c324cb1927e03b5a3584a8982e3d5029498f14e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 11 Dec 2022 21:41:26 -0500 Subject: docs #20986 - https://github.com/neovim/tree-sitter-vimdoc v1.2.4 eliminates most errors in pi_netrw.txt, so we can remove that workaround from ignore_parse_error(). - improved codeblock --- runtime/lua/vim/lsp/semantic_tokens.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 83b414bf87..11e62ee793 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -495,7 +495,6 @@ local M = {} --- delete the semanticTokensProvider table from the {server_capabilities} of --- your client in your |LspAttach| callback or your configuration's --- `on_attach` callback. ---- ---
lua
 ---   client.server_capabilities.semanticTokensProvider = nil
 --- 
-- cgit From 8b9bf3e3b997f033dcf73d506cceb12231501a22 Mon Sep 17 00:00:00 2001 From: Phelipe Teles <39670535+phelipetls@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:14:50 -0300 Subject: fix: vim.opt_local:append ignoring global option value (#21382) Closes https://github.com/neovim/neovim/issues/18225 --- runtime/lua/vim/_meta.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 9c7972873e..104f29c4c0 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -526,7 +526,7 @@ local function create_option_accessor(scope) return setmetatable({}, { __index = function(_, k) - return make_option(k, a.nvim_get_option_value(k, { scope = scope })) + return make_option(k, a.nvim_get_option_value(k, {})) end, __newindex = function(_, k, v) -- cgit From 3869a2e0cf25323a8e5235840678b147ca908517 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Mon, 12 Dec 2022 11:42:37 -0600 Subject: perf(lsp): update semantic tokens algorithm for parsing modifiers (#21383) Instead of testing for every possible modifier type, only test bits up to the highest set in the token array. Saves many bit ops and comparisons when there are no modifiers or when the highest set bit is a lower bit than the highest possible in the legend on average. Can be further simplified when non-luaJIT gets the full bit module (see #21222) --- runtime/lua/vim/lsp/semantic_tokens.lua | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 11e62ee793..f06d136801 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -55,22 +55,22 @@ end ---@private ---@return string[] local function modifiers_from_number(x, modifiers_table) - ---@private - local function _get_bit(n, k) - --TODO(jdrouhard): remove once `bit` module is available for non-LuaJIT + local modifiers = {} + local idx = 1 + while x > 0 do if _G.bit then - return _G.bit.band(_G.bit.rshift(n, k), 1) + if _G.bit.band(x, 1) == 1 then + modifiers[#modifiers + 1] = modifiers_table[idx] + end + x = _G.bit.rshift(x, 1) else - return math.floor((n / math.pow(2, k)) % 2) - end - end - - local modifiers = {} - for i = 0, #modifiers_table - 1 do - local b = _get_bit(x, i) - if b == 1 then - modifiers[#modifiers + 1] = modifiers_table[i + 1] + --TODO(jdrouhard): remove this branch once `bit` module is available for non-LuaJIT (#21222) + if x % 2 == 1 then + modifiers[#modifiers + 1] = modifiers_table[idx] + end + x = math.floor(x / 2) end + idx = idx + 1 end return modifiers -- cgit From 54d6a32fbdcbd5b26b72f4dca8906e60f5186d2c Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 12 Dec 2022 20:43:14 +0100 Subject: feat(lsp): highlight semantic token modifiers (#21390) Apply semantic token modifiers as separate extmarks with corresponding highlight groups (e.g., `@readonly`). This is a low-effort PR to enable the most common use cases (applying, e.g., italics or backgrounds on top of type highlights; language-specific fallbacks like `@global.lua` are also available). This can be replaced by more complicated selector-style themes later on. --- runtime/lua/vim/lsp/semantic_tokens.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index f06d136801..b7ffedab2b 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -409,7 +409,17 @@ function STHighlighter:on_win(topline, botline) strict = false, }) - --TODO(jdrouhard): do something with the modifiers + -- TODO(bfredl) use single extmark when hl_group supports table + if #token.modifiers > 0 then + for _, modifier in pairs(token.modifiers) do + api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, { + hl_group = '@' .. modifier, + end_col = token.end_col, + priority = vim.highlight.priorities.semantic_tokens, + strict = false, + }) + end + end token.extmark_added = true end @@ -494,7 +504,7 @@ local M = {} --- opt-out of semantic highlighting with a server that supports it, you can --- delete the semanticTokensProvider table from the {server_capabilities} of --- your client in your |LspAttach| callback or your configuration's ---- `on_attach` callback. +--- `on_attach` callback: ---
lua
 ---   client.server_capabilities.semanticTokensProvider = nil
 --- 
-- cgit From 04da0432446fac57e391c31bd4de0a9c06b1626d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 10 Dec 2022 13:17:07 +0100 Subject: feat(lsp): add function to get semantic tokens at cursor --- runtime/lua/vim/lsp/semantic_tokens.lua | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index b7ffedab2b..d4c414675c 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -585,6 +585,50 @@ function M.stop(bufnr, client_id) end end +--- Return the semantic token(s) at the given position. +--- If called without argument, returns the token under the cursor. +--- +---@param bufnr number|nil Buffer number (0 for current buffer, default) +---@param row number|nil Position row (default cursor position) +---@param col number|nil Position column (default cursor position) +--- +---@return table[]|nil tokens Table of tokens at position +function M.get_at_pos(bufnr, row, col) + if bufnr == nil or bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + + local highlighter = STHighlighter.active[bufnr] + if not highlighter then + return + end + + if row == nil or col == nil then + local cursor = api.nvim_win_get_cursor(0) + row, col = cursor[1] - 1, cursor[2] + end + + local tokens = {} + for _, client in pairs(highlighter.client_state) do + local highlights = client.current_result.highlights + if highlights then + local idx = binary_search(highlights, row) + for i = idx, #highlights do + local token = highlights[i] + + if token.line > row then + break + end + + if token.start_col <= col and token.end_col > col then + tokens[#tokens + 1] = token + end + end + end + end + return tokens +end + --- Force a refresh of all semantic tokens --- --- Only has an effect if the buffer is currently active for semantic token -- cgit From d127c684faa9a112575798a81c87babb1c83a7ea Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 14 Dec 2022 15:00:48 +0000 Subject: fix(treesitter): properly restore `'syntax'` (#21358) --- runtime/lua/vim/treesitter.lua | 6 ------ runtime/lua/vim/treesitter/highlighter.lua | 4 ++++ 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 7813c2edb2..25f0e7dc5e 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -327,12 +327,8 @@ end ---@param lang (string|nil) Language of the parser (default: buffer filetype) function M.start(bufnr, lang) bufnr = bufnr or a.nvim_get_current_buf() - local parser = M.get_parser(bufnr, lang) - M.highlighter.new(parser) - - vim.b[bufnr].ts_highlight = true end --- Stops treesitter highlighting for a buffer @@ -344,8 +340,6 @@ function M.stop(bufnr) if M.highlighter.active[bufnr] then M.highlighter.active[bufnr]:destroy() end - - vim.bo[bufnr].syntax = 'on' end --- Open a window that displays a textual representation of the nodes in the language tree. diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index f5e5ca1988..99aedf60c5 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -88,7 +88,9 @@ function TSHighlighter.new(tree, opts) end end + self.orig_syntax = vim.bo[self.bufnr].syntax vim.bo[self.bufnr].syntax = '' + vim.b[self.bufnr].ts_highlight = true TSHighlighter.active[self.bufnr] = self @@ -114,6 +116,8 @@ function TSHighlighter:destroy() if TSHighlighter.active[self.bufnr] then TSHighlighter.active[self.bufnr] = nil end + + vim.bo[self.bufnr].syntax = self.orig_syntax end ---@private -- cgit From 4a78ed60a3acd3a5694cc34debd4d91bed96195a Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 14 Dec 2022 19:46:17 +0100 Subject: vim-patch:9.0.1057: conflict between supercollider and scala filetype detection (#21417) Problem: Conflict between supercollider and scala filetype detection. Solution: Do not check for "Class : Method", it can appear in both filetypes. (Chris Kipp, closes vim/vim#11699) https://github.com/vim/vim/commit/70ef3f546b6ef83e463e91b7e388d9c68ad58894 Co-authored-by: Chris Kipp --- runtime/lua/vim/filetype/detect.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index a5f20b61a6..ee538dc8c7 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1097,11 +1097,10 @@ function M.sc(bufnr) for _, line in ipairs(getlines(bufnr, 1, 25)) do if findany(line, { - '[A-Za-z0-9]*%s:%s[A-Za-z0-9]', 'var%s<', 'classvar%s<', '%^this.*', - '|%w*|', + '|%w+|', '%+%s%w*%s{', '%*ar%s', }) -- cgit From 0887ad1cbb050d2bc6169ad46aa07cf42c90493f Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 14 Dec 2022 22:54:58 +0000 Subject: fix(ts): check buffer is loaded when restoring options (#21419) fix(treesitter): check buffer is loaded when restoring options Also restore spelloptions Fixes #21416 --- runtime/lua/vim/treesitter/highlighter.lua | 7 ++++++- 1 file changed, 6 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 99aedf60c5..e99994c8a9 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -89,6 +89,8 @@ function TSHighlighter.new(tree, opts) end self.orig_syntax = vim.bo[self.bufnr].syntax + self.orig_spelloptions = vim.bo[self.bufnr].spelloptions + vim.bo[self.bufnr].syntax = '' vim.b[self.bufnr].ts_highlight = true @@ -117,7 +119,10 @@ function TSHighlighter:destroy() TSHighlighter.active[self.bufnr] = nil end - vim.bo[self.bufnr].syntax = self.orig_syntax + if vim.api.nvim_buf_is_loaded(self.bufnr) then + vim.bo[self.bufnr].syntax = self.orig_syntax + vim.bo[self.bufnr].spelloptions = self.orig_spelloptions + end end ---@private -- cgit From 26c918d03f5b38df900316c0601065ec1ea96264 Mon Sep 17 00:00:00 2001 From: William Boman Date: Thu, 15 Dec 2022 02:27:23 +0100 Subject: fix(lua): always return nil values in vim.tbl_get when no results While `return` and `return nil` are for most intents and purposes identical, there are situations where they're not. For example, calculating the amount of values via the `select()` function will yield varying results: ```lua local function nothing() return end local function null() return nil end select('#', nothing()) -- 0 select('#', null()) -- 1 ``` `vim.tbl_get` currently returns both nil and no results, which makes it unreliable to use in certain situations without manually accounting for these discrepancies. --- runtime/lua/vim/shared.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 9129500866..5ffd11682c 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -395,15 +395,14 @@ end function vim.tbl_get(o, ...) local keys = { ... } if #keys == 0 then - return + return nil end for i, k in ipairs(keys) do - if type(o[k]) ~= 'table' and next(keys, i) then - return nil - end o = o[k] if o == nil then - return + return nil + elseif type(o) ~= 'table' and next(keys, i) then + return nil end end return o -- cgit From ef91146efcece1b6d97152251e7137d301146189 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 14 Dec 2022 10:46:54 +0100 Subject: feat: `vim.inspect_pos`, `vim.show_pos`, `:Inspect` --- runtime/lua/vim/_init_packages.lua | 3 + runtime/lua/vim/_inspector.lua | 238 ++++++++++++++++++++++++++++++++ runtime/lua/vim/lsp/semantic_tokens.lua | 7 +- runtime/lua/vim/treesitter.lua | 2 +- 4 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 runtime/lua/vim/_inspector.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua index 19c8608732..0c4ee8636d 100644 --- a/runtime/lua/vim/_init_packages.lua +++ b/runtime/lua/vim/_init_packages.lua @@ -56,6 +56,9 @@ setmetatable(vim, { if vim._submodules[key] then t[key] = require('vim.' .. key) return t[key] + elseif key == 'inspect_pos' or key == 'show_pos' then + require('vim._inspector') + return t[key] elseif vim.startswith(key, 'uri_') then local val = require('vim.uri')[key] if val ~= nil then diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua new file mode 100644 index 0000000000..f46a525910 --- /dev/null +++ b/runtime/lua/vim/_inspector.lua @@ -0,0 +1,238 @@ +---@class InspectorFilter +---@field syntax boolean include syntax based highlight groups (defaults to true) +---@field treesitter boolean include treesitter based highlight groups (defaults to true) +---@field extmarks boolean|"all" include extmarks. When `all`, then extmarks without a `hl_group` will also be included (defaults to true) +---@field semantic_tokens boolean include semantic tokens (defaults to true) +local defaults = { + syntax = true, + treesitter = true, + extmarks = true, + semantic_tokens = true, +} + +---Get all the items at a given buffer position. +--- +---Can also be pretty-printed with `:Inspect!`. *:Inspect!* +--- +---@param bufnr? number defaults to the current buffer +---@param row? number row to inspect, 0-based. Defaults to the row of the current cursor +---@param col? number col to inspect, 0-based. Defaults to the col of the current cursor +---@param filter? InspectorFilter (table|nil) a table with key-value pairs to filter the items +--- - syntax (boolean): include syntax based highlight groups (defaults to true) +--- - treesitter (boolean): include treesitter based highlight groups (defaults to true) +--- - extmarks (boolean|"all"): include extmarks. When `all`, then extmarks without a `hl_group` will also be included (defaults to true) +--- - semantic_tokens (boolean): include semantic tokens (defaults to true) +---@return {treesitter:table,syntax:table,extmarks:table,semantic_tokens:table,buffer:number,col:number,row:number} (table) a table with the following key-value pairs. Items are in "traversal order": +--- - treesitter: a list of treesitter captures +--- - syntax: a list of syntax groups +--- - semantic_tokens: a list of semantic tokens +--- - extmarks: a list of extmarks +--- - buffer: the buffer used to get the items +--- - row: the row used to get the items +--- - col: the col used to get the items +function vim.inspect_pos(bufnr, row, col, filter) + filter = vim.tbl_deep_extend('force', defaults, filter or {}) + + bufnr = bufnr or 0 + if row == nil or col == nil then + -- get the row/col from the first window displaying the buffer + local win = bufnr == 0 and vim.api.nvim_get_current_win() or vim.fn.bufwinid(bufnr) + if win == -1 then + error('row/col is required for buffers not visible in a window') + end + 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 + + local results = { + treesitter = {}, + syntax = {}, + extmarks = {}, + semantic_tokens = {}, + buffer = bufnr, + row = row, + col = col, + } + + -- resolve hl links + ---@private + local function resolve_hl(data) + if data.hl_group then + local hlid = vim.api.nvim_get_hl_id_by_name(data.hl_group) + local name = vim.fn.synIDattr(vim.fn.synIDtrans(hlid), 'name') + data.hl_group_link = name + end + return data + end + + -- treesitter + if filter.treesitter then + for _, capture in pairs(vim.treesitter.get_captures_at_pos(bufnr, row, col)) do + capture.hl_group = '@' .. capture.capture + table.insert(results.treesitter, resolve_hl(capture)) + end + end + + -- syntax + if filter.syntax then + for _, i1 in ipairs(vim.fn.synstack(row + 1, col + 1)) do + table.insert(results.syntax, resolve_hl({ hl_group = vim.fn.synIDattr(i1, 'name') })) + end + end + + -- semantic tokens + if filter.semantic_tokens then + for _, token in ipairs(vim.lsp.semantic_tokens.get_at_pos(bufnr, row, col) or {}) do + token.hl_groups = { + type = resolve_hl({ hl_group = '@' .. token.type }), + modifiers = vim.tbl_map(function(modifier) + return resolve_hl({ hl_group = '@' .. modifier }) + end, token.modifiers or {}), + } + table.insert(results.semantic_tokens, token) + end + end + + -- extmarks + if filter.extmarks then + for ns, nsid in pairs(vim.api.nvim_get_namespaces()) do + if ns:find('vim_lsp_semantic_tokens') ~= 1 then + local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, nsid, 0, -1, { details = true }) + for _, extmark in ipairs(extmarks) do + extmark = { + ns_id = nsid, + ns = ns, + id = extmark[1], + row = extmark[2], + col = extmark[3], + opts = resolve_hl(extmark[4]), + } + local end_row = extmark.opts.end_row or extmark.row -- inclusive + local end_col = extmark.opts.end_col or (extmark.col + 1) -- exclusive + if + (filter.extmarks == 'all' or extmark.opts.hl_group) -- filter hl_group + and (row >= extmark.row and row <= end_row) -- within the rows of the extmark + and (row > extmark.row or col >= extmark.col) -- either not the first row, or in range of the col + and (row < end_row or col < end_col) -- either not in the last row or in range of the col + then + table.insert(results.extmarks, extmark) + end + end + end + end + end + return results +end + +---Show all the items at a given buffer position. +--- +---Can also be shown with `:Inspect`. *:Inspect* +--- +---@param bufnr? number defaults to the current buffer +---@param row? number row to inspect, 0-based. Defaults to the row of the current cursor +---@param col? number col to inspect, 0-based. Defaults to the col of the current cursor +---@param filter? InspectorFilter (table|nil) see |vim.inspect_pos()| +function vim.show_pos(bufnr, row, col, filter) + local items = vim.inspect_pos(bufnr, row, col, filter) + + local lines = { {} } + + ---@private + local function append(str, hl) + table.insert(lines[#lines], { str, hl }) + end + + ---@private + local function nl() + table.insert(lines, {}) + end + + ---@private + local function item(data, comment) + append(' - ') + append(data.hl_group, data.hl_group) + append(' ') + if data.hl_group ~= data.hl_group_link then + append('links to ', 'MoreMsg') + append(data.hl_group_link, data.hl_group_link) + append(' ') + end + if comment then + append(comment, 'Comment') + end + nl() + end + + -- treesitter + if #items.treesitter > 0 then + append('Treesitter', 'Title') + nl() + for _, capture in ipairs(items.treesitter) do + item(capture, capture.lang) + end + nl() + end + + if #items.semantic_tokens > 0 then + append('Semantic Tokens', 'Title') + nl() + for _, token in ipairs(items.semantic_tokens) do + local client = vim.lsp.get_client_by_id(token.client_id) + client = client and (' (' .. client.name .. ')') or '' + item(token.hl_groups.type, 'type' .. client) + for _, modifier in ipairs(token.hl_groups.modifiers) do + item(modifier, 'modifier' .. client) + end + end + nl() + end + + -- syntax + if #items.syntax > 0 then + append('Syntax', 'Title') + nl() + for _, syn in ipairs(items.syntax) do + item(syn) + end + nl() + end + -- extmarks + if #items.extmarks > 0 then + append('Extmarks', 'Title') + nl() + for _, extmark in ipairs(items.extmarks) do + if extmark.opts.hl_group then + item(extmark.opts, extmark.ns) + else + append(' - ') + append(extmark.ns, 'Comment') + nl() + end + end + nl() + end + + if #lines[#lines] == 0 then + table.remove(lines) + end + + local chunks = {} + for _, line in ipairs(lines) do + vim.list_extend(chunks, line) + table.insert(chunks, { '\n' }) + end + if #chunks == 0 then + chunks = { + { + 'No items found at position ' + .. items.row + .. ',' + .. items.col + .. ' in buffer ' + .. items.buffer, + }, + } + end + vim.api.nvim_echo(chunks, false, {}) +end diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index d4c414675c..e14d3e51cd 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -586,13 +586,13 @@ function M.stop(bufnr, client_id) end --- Return the semantic token(s) at the given position. ---- If called without argument, returns the token under the cursor. +--- If called without arguments, returns the token under the cursor. --- ---@param bufnr number|nil Buffer number (0 for current buffer, default) ---@param row number|nil Position row (default cursor position) ---@param col number|nil Position column (default cursor position) --- ----@return table[]|nil tokens Table of tokens at position +---@return table|nil (table|nil) List of tokens at position function M.get_at_pos(bufnr, row, col) if bufnr == nil or bufnr == 0 then bufnr = api.nvim_get_current_buf() @@ -609,7 +609,7 @@ function M.get_at_pos(bufnr, row, col) end local tokens = {} - for _, client in pairs(highlighter.client_state) do + for client_id, client in pairs(highlighter.client_state) do local highlights = client.current_result.highlights if highlights then local idx = binary_search(highlights, row) @@ -621,6 +621,7 @@ function M.get_at_pos(bufnr, row, col) end if token.start_col <= col and token.end_col > col then + token.client_id = client_id tokens[#tokens + 1] = token end end diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 7813c2edb2..5031aca378 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -240,7 +240,7 @@ function M.get_captures_at_pos(bufnr, row, col) if M.is_in_node_range(node, row, col) then local c = q._query.captures[capture] -- name of the capture in the query if c ~= nil then - table.insert(matches, { capture = c, metadata = metadata }) + table.insert(matches, { capture = c, metadata = metadata, lang = tree:lang() }) end end end -- cgit From 1743359235206cbd9da2e0589f1caba37cce3b8c Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 18 Dec 2022 03:19:15 +0100 Subject: fix(diagnostic): clear stale cache on reset (#21454) The BufWipeout autocmd is not 100% reliable and may leave stale entries in the cache. This is sort of a hack/workaround to ensure `vim.diagnostic.reset` calls don't fail if there are stale cache entries but instead clears them Fixes errors like Error executing vim.schedule lua callback: /usr/share/nvim/runtime/lua/vim/diagnostic.lua:1458: Invalid buffer id: 22 stack traceback: [C]: in function 'nvim_exec_autocmds' /usr/share/nvim/runtime/lua/vim/diagnostic.lua:1458: in function 'reset' --- runtime/lua/vim/diagnostic.lua | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 7557bc79a2..8ca324d666 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -922,7 +922,7 @@ M.handlers.signs = { end, hide = function(namespace, bufnr) local ns = M.get_namespace(namespace) - if ns.user_data.sign_group then + if ns.user_data.sign_group and api.nvim_buf_is_valid(bufnr) then vim.fn.sign_unplace(ns.user_data.sign_group, { buffer = bufnr }) end end, @@ -977,7 +977,9 @@ M.handlers.underline = { local ns = M.get_namespace(namespace) if ns.user_data.underline_ns then diagnostic_cache_extmarks[bufnr][ns.user_data.underline_ns] = {} - api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1) + if api.nvim_buf_is_valid(bufnr) then + api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1) + end end end, } @@ -1040,7 +1042,9 @@ M.handlers.virtual_text = { local ns = M.get_namespace(namespace) if ns.user_data.virt_text_ns then diagnostic_cache_extmarks[bufnr][ns.user_data.virt_text_ns] = {} - api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1) + if api.nvim_buf_is_valid(bufnr) then + api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1) + end end end, } @@ -1470,11 +1474,15 @@ function M.reset(namespace, bufnr) M.hide(iter_namespace, iter_bufnr) end - api.nvim_exec_autocmds('DiagnosticChanged', { - modeline = false, - buffer = iter_bufnr, - data = { diagnostics = {} }, - }) + if api.nvim_buf_is_valid(iter_bufnr) then + api.nvim_exec_autocmds('DiagnosticChanged', { + modeline = false, + buffer = iter_bufnr, + data = { diagnostics = {} }, + }) + else + diagnostic_cache[iter_bufnr] = nil + end end end -- cgit From bf9ad5db962509f3434726b7ad84d31d002fb8a3 Mon Sep 17 00:00:00 2001 From: tae-soo-kim <117524309+tae-soo-kim@users.noreply.github.com> Date: Sun, 18 Dec 2022 10:17:15 -0500 Subject: fix(diagnostic): sort diagnostics by column (#21457) Sort diagnostics by column number in quickfix list --- runtime/lua/vim/diagnostic.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 8ca324d666..84a8701ac7 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1673,7 +1673,11 @@ function M.toqflist(diagnostics) end table.sort(list, function(a, b) if a.bufnr == b.bufnr then - return a.lnum < b.lnum + if a.lnum == b.lnum then + return a.col < b.col + else + return a.lnum < b.lnum + end else return a.bufnr < b.bufnr end -- cgit From f4d8e992bfcd6e9d0097b9d7a022060bd32f2069 Mon Sep 17 00:00:00 2001 From: tiagovla <30515389+tiagovla@users.noreply.github.com> Date: Mon, 19 Dec 2022 05:24:27 -0300 Subject: fix(lsp): token_edit.data might be null on deletion (#21462) --- runtime/lua/vim/lsp/semantic_tokens.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index e14d3e51cd..48190b03e1 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -323,7 +323,9 @@ function STHighlighter:process_response(response, client, version) local idx = 1 for _, token_edit in ipairs(token_edits) do vim.list_extend(tokens, old_tokens, idx, token_edit.start) - vim.list_extend(tokens, token_edit.data) + if token_edit.data then + vim.list_extend(tokens, token_edit.data) + end idx = token_edit.start + token_edit.deleteCount + 1 end vim.list_extend(tokens, old_tokens, idx) -- cgit From fb5576c2d36464b55c2c639aec9259a6f2461970 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 13 Dec 2022 13:59:31 +0000 Subject: feat(fs): add opts argument to vim.fs.dir() Added option depth to allow recursively searching a directory tree. --- runtime/lua/vim/fs.lua | 64 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index fdf3d29e94..89a1d0d345 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -72,18 +72,65 @@ function M.basename(file) return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/')) end +---@private +local function join_paths(...) + return table.concat({ ... }, '/') +end + --- Return an iterator over the files and directories located in {path} --- ---@param path (string) An absolute or relative path to the directory to iterate --- over. The path is first normalized |vim.fs.normalize()|. +--- @param opts table|nil Optional keyword arguments: +--- - depth: integer|nil How deep the traverse (default 1) +--- - skip: (fun(dir_name: string): boolean)|nil Predicate +--- to control traversal. Return false to stop searching the current directory. +--- Only useful when depth > 1 +--- ---@return Iterator over files and directories in {path}. Each iteration yields --- two values: name and type. Each "name" is the basename of the file or --- directory relative to {path}. Type is one of "file" or "directory". -function M.dir(path) - return function(fs) - return vim.loop.fs_scandir_next(fs) - end, - vim.loop.fs_scandir(M.normalize(path)) +function M.dir(path, opts) + opts = opts or {} + + vim.validate({ + path = { path, { 'string' } }, + depth = { opts.depth, { 'number' }, true }, + skip = { opts.skip, { 'function' }, true }, + }) + + if not opts.depth or opts.depth == 1 then + return function(fs) + return vim.loop.fs_scandir_next(fs) + end, + vim.loop.fs_scandir(M.normalize(path)) + end + + --- @async + return coroutine.wrap(function() + local dirs = { { path, 1 } } + while #dirs > 0 do + local dir0, level = unpack(table.remove(dirs, 1)) + local dir = level == 1 and dir0 or join_paths(path, dir0) + local fs = vim.loop.fs_scandir(M.normalize(dir)) + while fs do + local name, t = vim.loop.fs_scandir_next(fs) + if not name then + break + end + local f = level == 1 and name or join_paths(dir0, name) + coroutine.yield(f, t) + if + opts.depth + and level < opts.depth + and t == 'directory' + and (not opts.skip or opts.skip(f) ~= false) + then + dirs[#dirs + 1] = { f, level + 1 } + end + end + end + end) end --- Find files or directories in the given path. @@ -155,7 +202,7 @@ function M.find(names, opts) local t = {} for name, type in M.dir(p) do if names(name) and (not opts.type or opts.type == type) then - table.insert(t, p .. '/' .. name) + table.insert(t, join_paths(p, name)) end end return t @@ -164,7 +211,7 @@ function M.find(names, opts) test = function(p) local t = {} for _, name in ipairs(names) do - local f = p .. '/' .. name + local f = join_paths(p, name) local stat = vim.loop.fs_stat(f) if stat and (not opts.type or opts.type == stat.type) then t[#t + 1] = f @@ -201,7 +248,7 @@ function M.find(names, opts) end for other, type_ in M.dir(dir) do - local f = dir .. '/' .. other + local f = join_paths(dir, other) if type(names) == 'function' then if names(other) and (not opts.type or opts.type == type_) then if add(f) then @@ -251,6 +298,7 @@ function M.normalize(path) vim.validate({ path = { path, 's' } }) return ( path + :gsub('^~$', vim.loop.os_homedir()) :gsub('^~/', vim.loop.os_homedir() .. '/') :gsub('%$([%w_]+)', vim.loop.os_getenv) :gsub('\\', '/') -- cgit From 45d1b1c6788a5694850361b5ffa95d63eb75b2f5 Mon Sep 17 00:00:00 2001 From: kylo252 <59826753+kylo252@users.noreply.github.com> Date: Tue, 20 Dec 2022 18:04:47 +0100 Subject: vim-patch:9.0.1082: some jsonc files are not recognized (#21483) Problem: Some jsonc files are not recognized. Solution: Add patterns for jsonc and move some from json to jsonc. (closes vim/vim#11711) https://github.com/vim/vim/commit/104b2ff4d0ec9248ba0b979aa3bbccb65fcad422 Co-authored-by: kylo252 <59826753+kylo252@users.noreply.github.com> --- runtime/lua/vim/filetype.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 58acca42f7..b47013122d 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1454,11 +1454,15 @@ local filename = { ['ipf.conf'] = 'ipfilter', ['ipf6.conf'] = 'ipfilter', ['ipf.rules'] = 'ipfilter', - ['.eslintrc'] = 'json', - ['.babelrc'] = 'json', ['Pipfile.lock'] = 'json', ['.firebaserc'] = 'json', ['.prettierrc'] = 'json', + ['.babelrc'] = 'jsonc', + ['.eslintrc'] = 'jsonc', + ['.hintrc'] = 'jsonc', + ['.jsfmtrc'] = 'jsonc', + ['.jshintrc'] = 'jsonc', + ['.swrc'] = 'jsonc', Kconfig = 'kconfig', ['Kconfig.debug'] = 'kconfig', ['lftp.conf'] = 'lftp', @@ -1912,6 +1916,7 @@ local pattern = { ['.*%.properties_.._..'] = 'jproperties', ['org%.eclipse%..*%.prefs'] = 'jproperties', ['.*%.properties_.._.._.*'] = starsetf('jproperties'), + ['[jt]sconfig.*%.json'] = 'jsonc', ['Kconfig%..*'] = starsetf('kconfig'), ['.*%.[Ss][Uu][Bb]'] = 'krl', ['lilo%.conf.*'] = starsetf('lilo'), -- cgit From 3ea1524cf8a7d80d1034011462fd9611fdf385f1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 23 Dec 2022 17:36:41 +0100 Subject: vim-patch:9.0.1090: FHIR Shorthand files are not recognized (#21515) Problem: FHIR Shorthand files are not recognized. Solution: Add a pattern to detect FSH files. (Matthew Gramigna, closes vim/vim#11738) https://github.com/vim/vim/commit/c9207d5d79310bd4628ce46d8db588fac17878a0 Co-authored-by: mgramigna --- 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 b47013122d..9952af821e 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -419,6 +419,7 @@ local extension = { fs = function(path, bufnr) return require('vim.filetype.detect').fs(bufnr) end, + fsh = 'fsh', fsi = 'fsharp', fsx = 'fsharp', fusion = 'fusion', -- cgit From fe5665be3b49c4ebc1257de8b9c6ed81254fd762 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 26 Dec 2022 18:36:51 +0100 Subject: vim-patch:9.0.1103: jq files are not recognized (#21545) Problem: jq files are not recognized. Solution: Add detection of Jq files. (David McDonald, closes vim/vim#11743) https://github.com/vim/vim/commit/b9a1edfc5434f2a3ac50b1a178d3c85aa417b798 Co-authored-by: David McDonald --- 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 9952af821e..fb7bf53007 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -557,6 +557,7 @@ local extension = { jov = 'jovial', jovial = 'jovial', properties = 'jproperties', + jq = 'jq', slnf = 'json', json = 'json', jsonp = 'json', -- cgit From 3b9b43063c76e52b119aaac96e50c708d7d39479 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 28 Dec 2022 11:42:02 +0100 Subject: vim-patch:9.0.1106: not all postfix files are recognized (#21568) Problem: Not all postfix files are recognized. Solution: Recognize main.cf.proto files. (closes vim/vim#11732) https://github.com/vim/vim/commit/09ce0b8e1197c85dacf97e75b9b9ac18e0d192df Co-authored-by: KodeToad <3880336+KodeToad@users.noreply.github.com> --- 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 fb7bf53007..7eec567b66 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1532,6 +1532,7 @@ local filename = { ['.latexmkrc'] = 'perl', ['pf.conf'] = 'pf', ['main.cf'] = 'pfmain', + ['main.cf.proto'] = 'pfmain', pinerc = 'pine', ['.pinercex'] = 'pine', ['.pinerc'] = 'pine', -- cgit From 469f9859238fa179448a31863e0f3894c9bc9c99 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Fri, 30 Dec 2022 09:40:23 -0600 Subject: fix(lsp): adjust gravity of semantic tokens extmarks (#21574) Fixes #21543 This should provide a better user experience when appending or prepending text to a word that has a semantic token extmark. More often than not, the appended/prepended text to the word will end up becoming part of the token anyway, so just use that extmark as the user types. --- runtime/lua/vim/lsp/semantic_tokens.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 48190b03e1..849a8a1b67 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -408,6 +408,8 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. token.type, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens, + right_gravity = false, + end_right_gravity = true, strict = false, }) @@ -417,7 +419,9 @@ function STHighlighter:on_win(topline, botline) api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, { hl_group = '@' .. modifier, end_col = token.end_col, - priority = vim.highlight.priorities.semantic_tokens, + priority = vim.highlight.priorities.semantic_tokens + 1, + right_gravity = false, + end_right_gravity = true, strict = false, }) end -- cgit From f62c30ad0d53fe79bbb00087609d76101371d122 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Dec 2022 23:42:18 +0800 Subject: fix(lsp): fix nil client access in get_active_clients (#21524) Fixes https://github.com/neovim/neovim/issues/21523 --- runtime/lua/vim/lsp.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index dc5008399e..ad0e599f61 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1845,7 +1845,8 @@ function lsp.get_active_clients(filter) for client_id in pairs(t) do local client = active_clients[client_id] if - (filter.id == nil or client.id == filter.id) + client + and (filter.id == nil or client.id == filter.id) and (filter.name == nil or client.name == filter.name) then clients[#clients + 1] = client -- cgit From 4ace9e7e417fe26c8b73ff1d6042e6e4f3df9ebf Mon Sep 17 00:00:00 2001 From: 李晓辉 Date: Sat, 31 Dec 2022 02:23:54 +0800 Subject: feat(diagnostic): don't open quickfix/loclist if no diagnostics #21397 --- runtime/lua/vim/diagnostic.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 84a8701ac7..b9aee7eaf3 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -478,13 +478,17 @@ local function set_list(loclist, opts) -- numbers beyond the end of the buffer local diagnostics = get_diagnostics(bufnr, opts, false) local items = M.toqflist(diagnostics) + if next(items) == nil then + vim.notify('No diagnostics available') + return + end if loclist then vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items }) else vim.fn.setqflist({}, ' ', { title = title, items = items }) end if open then - api.nvim_command(loclist and 'lopen' or 'botright copen') + api.nvim_command(loclist and 'lwindow' or 'botright cwindow') end end -- cgit From 6ba34e21fee2a81677e8261dfeaf24c8cd320500 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 31 Dec 2022 16:16:21 +0100 Subject: feat(lsp): add function to clear codelens (#21504) Currently once you retrieve the lenses you're pretty much stuck with them as saving new lenses is additive. Adding a dedicated method to reset lenses allows users to toggle lenses on/off which can be useful for language servers where they are noisy or expensive and you only want to see them temporary. --- runtime/lua/vim/lsp/codelens.lua | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 71b6bfae30..17489ed84d 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -108,13 +108,36 @@ function M.run() end end +---@private +local function resolve_bufnr(bufnr) + return bufnr == 0 and api.nvim_get_current_buf() or bufnr +end + +--- Clear the lenses +--- +---@param client_id number|nil filter by client_id. All clients if nil +---@param bufnr number|nil filter by buffer. All buffers if nil +function M.clear(client_id, bufnr) + local buffers = bufnr and { resolve_bufnr(bufnr) } or vim.tbl_keys(lens_cache_by_buf) + for _, iter_bufnr in pairs(buffers) do + local client_ids = client_id and { client_id } or vim.tbl_keys(namespaces) + for _, iter_client_id in pairs(client_ids) do + local ns = namespaces[iter_client_id] + lens_cache_by_buf[iter_bufnr][iter_client_id] = {} + api.nvim_buf_clear_namespace(iter_bufnr, ns, 0, -1) + end + end +end + --- Display the lenses using virtual text --- ---@param lenses table of lenses to display (`CodeLens[] | null`) ---@param bufnr number ---@param client_id number function M.display(lenses, bufnr, client_id) + local ns = namespaces[client_id] if not lenses or not next(lenses) then + api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) return end local lenses_by_lnum = {} @@ -126,7 +149,6 @@ function M.display(lenses, bufnr, client_id) end table.insert(line_lenses, lens) end - local ns = namespaces[client_id] local num_lines = api.nvim_buf_line_count(bufnr) for i = 0, num_lines do local line_lenses = lenses_by_lnum[i] or {} -- cgit From 85b7c8b004a36a15731a461e5e95f2c9fca732f1 Mon Sep 17 00:00:00 2001 From: Gautam Iyer Date: Sat, 31 Dec 2022 11:26:05 -0500 Subject: fix(filetype): correctly detect tex files Fixes Issue #21594. --- runtime/lua/vim/filetype/detect.lua | 39 +++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index ee538dc8c7..edffdde9c7 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1256,17 +1256,15 @@ end -- 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords. -- 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc. function M.tex(path, bufnr) - local format = getlines(bufnr, 1):find('^%%&%s*(%a+)') - if format then + local matched, _, format = getlines(bufnr, 1):find('^%%&%s*(%a+)') + if matched then format = format:lower():gsub('pdf', '', 1) - if format == 'tex' then - return 'tex' - elseif format == 'plaintex' then - return 'plaintex' - end elseif path:lower():find('tex/context/.*/.*%.tex') then return 'context' else + -- Default value, may be changed later: + format = vim.g.tex_flavor or 'plaintex' + local lpat = [[documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>]] local cpat = [[start\a\+\|setup\a\+\|usemodule\|enablemode\|enableregime\|setvariables\|useencoding\|usesymbols\|stelle\a\+\|verwende\a\+\|stel\a\+\|gebruik\a\+\|usa\a\+\|imposta\a\+\|regle\a\+\|utilisemodule\>]] @@ -1275,26 +1273,25 @@ function M.tex(path, bufnr) -- Find first non-comment line if not l:find('^%s*%%%S') then -- Check the next thousand lines for a LaTeX or ConTeXt keyword. - for _, line in ipairs(getlines(bufnr, i + 1, i + 1000)) do - local lpat_match, cpat_match = - matchregex(line, [[\c^\s*\\\%(]] .. lpat .. [[\)\|^\s*\\\(]] .. cpat .. [[\)]]) - if lpat_match then + for _, line in ipairs(getlines(bufnr, i, i + 1000)) do + if matchregex(line, [[\c^\s*\\\%(]] .. lpat .. [[\)]]) then return 'tex' - elseif cpat_match then + elseif matchregex(line, [[\c^\s*\\\%(]] .. cpat .. [[\)]]) then return 'context' end end end end - -- TODO: add AMSTeX, RevTex, others? - if not vim.g.tex_flavor or vim.g.tex_flavor == 'plain' then - return 'plaintex' - elseif vim.g.tex_flavor == 'context' then - return 'context' - else - -- Probably LaTeX - return 'tex' - end + end -- if matched + + -- Translation from formats to file types. TODO: add AMSTeX, RevTex, others? + if format == 'plain' then + return 'plaintex' + elseif format == 'plaintex' or format == 'context' then + return format + else + -- Probably LaTeX + return 'tex' end end -- cgit From dfb840970c36056584e9a55d77a2030b4e403e9d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 28 Dec 2022 14:20:42 +0100 Subject: docs(lua): fix treesitter parsing errors --- runtime/lua/vim/_editor.lua | 2 +- runtime/lua/vim/shared.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 2913a0ddc6..da8764fbd4 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -391,7 +391,7 @@ end ---@param pos2 integer[] (line, column) tuple marking end of region ---@param regtype string type of selection, see |setreg()| ---@param inclusive boolean indicating whether the selection is end-inclusive ----@return table region lua table of the form {linenr = {startcol,endcol}} +---@return table region Table of the form `{linenr = {startcol,endcol}}` function vim.region(bufnr, pos1, pos2, regtype, inclusive) if not vim.api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 5ffd11682c..6fc40bb905 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -113,7 +113,7 @@ end --- ---@param s string String to split ---@param sep string Separator or pattern ----@param kwargs ({plain: boolean, trimempty: boolean}|nil) Keyword arguments: +---@param kwargs (table|nil) Keyword arguments: --- - plain: (boolean) If `true` use `sep` literally (passed to string.find) --- - trimempty: (boolean) If `true` remove empty items from the front --- and back of the list -- cgit From 7b76a3e7992bffca758b2e52548d2f483a45eaf6 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jan 2023 18:07:27 +0800 Subject: refactor(diagnostic): DRY for loop #21521 Co-authored-by: Justin M. Keyes --- runtime/lua/vim/diagnostic.lua | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index b9aee7eaf3..e2eb952eca 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -429,32 +429,31 @@ local function get_diagnostics(bufnr, opts, clamp) end end + ---@private + local function add_all_diags(buf, diags) + for _, diagnostic in pairs(diags) do + add(buf, diagnostic) + end + end + if namespace == nil and bufnr == nil then for b, t in pairs(diagnostic_cache) do for _, v in pairs(t) do - for _, diagnostic in pairs(v) do - add(b, diagnostic) - end + add_all_diags(b, v) end end elseif namespace == nil then bufnr = get_bufnr(bufnr) for iter_namespace in pairs(diagnostic_cache[bufnr]) do - for _, diagnostic in pairs(diagnostic_cache[bufnr][iter_namespace]) do - add(bufnr, diagnostic) - end + add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace]) end elseif bufnr == nil then for b, t in pairs(diagnostic_cache) do - for _, diagnostic in pairs(t[namespace] or {}) do - add(b, diagnostic) - end + add_all_diags(b, t[namespace] or {}) end else bufnr = get_bufnr(bufnr) - for _, diagnostic in pairs(diagnostic_cache[bufnr][namespace] or {}) do - add(bufnr, diagnostic) - end + add_all_diags(bufnr, diagnostic_cache[bufnr][namespace] or {}) end if opts.severity then -- cgit From 5b22b32e50858b781eb2658ba099eaffaa5ea13b Mon Sep 17 00:00:00 2001 From: Christian Segundo Date: Tue, 3 Jan 2023 13:44:44 +0100 Subject: fix(lsp): change vim.lsp.get_active_clients.filter name annotation to string (#21624) --- 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 ad0e599f61..1d99283dd9 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1822,7 +1822,7 @@ end ---@class vim.lsp.get_active_clients.filter ---@field id number|nil Match clients by id ---@field bufnr number|nil match clients attached to the given buffer ----@field name number|nil match clients by name +---@field name string|nil match clients by name --- Get active clients. --- -- cgit From 5eed3e741b39a7e52888b0400ba63a2ea70f4dac Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 3 Jan 2023 08:20:20 -0700 Subject: fix(diagnostic): revert notification on missing diagnostics (#21632) This reverts a change introduced in 4ace9e7e417fe26c8b73ff1d6042e6e4f3df9ebf. --- runtime/lua/vim/diagnostic.lua | 4 ---- 1 file changed, 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index e2eb952eca..f41f4a9c68 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -477,10 +477,6 @@ local function set_list(loclist, opts) -- numbers beyond the end of the buffer local diagnostics = get_diagnostics(bufnr, opts, false) local items = M.toqflist(diagnostics) - if next(items) == nil then - vim.notify('No diagnostics available') - return - end if loclist then vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items }) else -- cgit From 1b3c255f608a6c1a4a31bcd4a640ea6696403341 Mon Sep 17 00:00:00 2001 From: Eric Haynes Date: Tue, 3 Jan 2023 12:24:14 -0500 Subject: fix(fs): duplicate path separator #21509 Fixes #21497 --- 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 89a1d0d345..65e6ca677c 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -74,7 +74,7 @@ end ---@private local function join_paths(...) - return table.concat({ ... }, '/') + return (table.concat({ ... }, '/'):gsub('//+', '/')) end --- Return an iterator over the files and directories located in {path} -- cgit From d6510eec4f5e4d1ca08f685f9ce7984ca9ab1c9e Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 3 Jan 2023 09:13:35 -0700 Subject: feat(editorconfig): add editorconfig syntax file This is intentionally _not_ copied from Vim because our syntax file makes use of Lua to dynamically generate a list of valid EditorConfig properties. This requires the builtin editorconfig module, which Vim does not have. --- 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 7eec567b66..c3ab39a1a3 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1378,13 +1378,13 @@ local filename = { npmrc = 'dosini', ['/etc/yum.conf'] = 'dosini', ['.npmrc'] = 'dosini', - ['.editorconfig'] = 'dosini', ['/etc/pacman.conf'] = 'confini', ['mpv.conf'] = 'confini', dune = 'dune', jbuild = 'dune', ['dune-workspace'] = 'dune', ['dune-project'] = 'dune', + ['.editorconfig'] = 'editorconfig', ['elinks.conf'] = 'elinks', ['mix.lock'] = 'elixir', ['filter-rules'] = 'elmfilt', -- cgit From e35b9020b16985eee26e942f9a3f6b045bc3809b Mon Sep 17 00:00:00 2001 From: notomo Date: Wed, 4 Jan 2023 20:48:41 +0900 Subject: docs(lua): adjust some type annotations --- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/buf.lua | 2 +- runtime/lua/vim/lsp/util.lua | 6 +++--- runtime/lua/vim/shared.lua | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1d99283dd9..13f8c81dfb 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1805,7 +1805,7 @@ end --- already for this client, then force-shutdown is attempted. --- ---@param client_id number|table id or |vim.lsp.client| object, or list thereof ----@param force boolean (optional) shutdown forcefully +---@param force boolean|nil shutdown forcefully function lsp.stop_client(client_id, force) local ids = type(client_id) == 'table' and client_id or { client_id } for _, id in ipairs(ids) do diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 226ed980fb..8f4bd15eaa 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -383,7 +383,7 @@ end --- Lists all the references to the symbol under the cursor in the quickfix window. --- ----@param context (table) Context for the request +---@param context (table|nil) Context for the request ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references ---@param options table|nil additional options --- - on_list: (function) handler for list results. See |lsp-on-list-handler| diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 2bd15c87d9..2c6ba823db 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1077,7 +1077,7 @@ end --- ---@param location table (`Location`|`LocationLink`) ---@param offset_encoding "utf-8" | "utf-16" | "utf-32" ----@param opts table options +---@param opts table|nil options --- - reuse_win (boolean) Jump to existing window if buffer is already open. --- - focus (boolean) Whether to focus/jump to location if possible. Defaults to true. ---@return boolean `true` if succeeded @@ -1134,7 +1134,7 @@ end --- ---@param location table (`Location`|`LocationLink`) ---@param offset_encoding "utf-8" | "utf-16" | "utf-32" ----@param reuse_win boolean Jump to existing window if buffer is already open. +---@param reuse_win boolean|nil 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) if offset_encoding == nil then @@ -1908,7 +1908,7 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ---@param window number|nil: window handle or 0 for current, defaults to current ----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` +---@param offset_encoding string|nil utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns `TextDocumentPositionParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 6fc40bb905..b53c66ba63 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -516,8 +516,8 @@ end --- ---@generic T ---@param list T[] (list) Table ----@param start number Start range of slice ----@param finish number End range of slice +---@param start number|nil Start range of slice +---@param finish number|nil End range of slice ---@return T[] (list) Copy of table sliced from start to finish (inclusive) function vim.list_slice(list, start, finish) local new_list = {} -- cgit From f7ad46e69ed407579517694ccf3dc1beffe7acdc Mon Sep 17 00:00:00 2001 From: 周 <1207190489@qq.com> Date: Fri, 6 Jan 2023 22:26:31 +0800 Subject: fix(lsp): correct callHierarchy capability to fix lsp.buf.incoming_calls() (#21665) Co-authored-by: maozhongzhou --- runtime/lua/vim/lsp/protocol.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index dfbd01b8f8..92cda0b34f 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -801,6 +801,9 @@ function protocol.make_client_capabilities() end)(), }, }, + callHierarchy = { + dynamicRegistration = false, + }, }, workspace = { symbol = { @@ -828,9 +831,6 @@ function protocol.make_client_capabilities() refreshSupport = true, }, }, - callHierarchy = { - dynamicRegistration = false, - }, experimental = nil, window = { workDoneProgress = true, -- cgit From 0b136a14dc215d94ceac6d72d9f9fcb1c3ea5bc9 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Sun, 8 Jan 2023 00:43:15 -0600 Subject: fix(lsp): partially revert semantic token gravity change from #21574 (#21680) --- runtime/lua/vim/lsp/semantic_tokens.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 849a8a1b67..b035ac43f4 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -408,7 +408,6 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. token.type, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens, - right_gravity = false, end_right_gravity = true, strict = false, }) @@ -420,7 +419,6 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. modifier, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens + 1, - right_gravity = false, end_right_gravity = true, strict = false, }) -- cgit From d1b3611f03625467500195b38d3c4646a7bc81db Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 8 Jan 2023 09:23:08 +0100 Subject: feat(lsp): show active clients in :checkhealth vim.lsp (#21670) For users using vim.lsp.start it can be useful to get an overview of active client that is less verbose than a full `:lua =vim.lsp.get_active_clients()` --- runtime/lua/vim/lsp/health.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index ba730e3d6d..987707e661 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -2,8 +2,8 @@ local M = {} --- Performs a healthcheck for LSP function M.check() - local report_info = vim.fn['health#report_info'] - local report_warn = vim.fn['health#report_warn'] + local report_info = vim.health.report_info + local report_warn = vim.health.report_warn local log = require('vim.lsp.log') local current_log_level = log.get_level() @@ -27,6 +27,18 @@ function M.check() local report_fn = (log_size / 1000000 > 100 and report_warn or report_info) report_fn(string.format('Log size: %d KB', log_size / 1000)) + + local clients = vim.lsp.get_active_clients() + vim.health.report_start('vim.lsp: Active Clients') + if next(clients) then + for _, client in pairs(clients) do + report_info( + string.format('%s (id=%s, root_dir=%s)', client.name, client.id, client.config.root_dir) + ) + end + else + report_info('No active clients') + end end return M -- cgit From 870ca1de52b240926b88f01afa697cd9b119bdac Mon Sep 17 00:00:00 2001 From: Sebastian Lyng Johansen Date: Tue, 10 Jan 2023 11:22:41 +0100 Subject: feat(float): open float relative to mouse #21531 Problem: No easy way to position a LSP hover window relative to mouse. Solution: Introduce another option to the `relative` key in `nvim_open_win()`. With this PR it should be possible to override the handler and do something similar to this https://github.com/neovim/neovim/pull/19481#issuecomment-1193248674 to have hover information displayed from the mouse. Test case: ```lua local util = require('vim.lsp.util') local function make_position_param(window, offset_encoding) window = window or 0 local buf = vim.api.nvim_win_get_buf(window) local row, col local mouse = vim.fn.getmousepos() row = mouse.line col = mouse.column offset_encoding = offset_encoding or util._get_offset_encoding(buf) row = row - 1 local line = vim.api.nvim_buf_get_lines(buf, row, row + 1, true)[1] if not line then return { line = 0, character = 0 } end if #line < col then return { line = 0, character = 0 } end col = util._str_utfindex_enc(line, col, offset_encoding) return { line = row, character = col } end local make_params = function(window, offset_encoding) window = window or 0 local buf = vim.api.nvim_win_get_buf(window) offset_encoding = offset_encoding or util._get_offset_encoding(buf) return { textDocument = util.make_text_document_params(buf), position = make_position_param(window, offset_encoding), } end local hover_timer = nil vim.o.mousemoveevent = true vim.keymap.set({ '', 'i' }, '', function() if hover_timer then hover_timer:close() end hover_timer = vim.defer_fn(function() hover_timer = nil local params = make_params() vim.lsp.buf_request( 0, 'textDocument/hover', params, vim.lsp.with(vim.lsp.handlers.hover, { silent = true, focusable = false, relative = 'mouse', }) ) end, 500) return '' end, { expr = true }) ``` --- runtime/lua/vim/lsp/handlers.lua | 4 +++- runtime/lua/vim/lsp/util.lua | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 80df83732e..b383ca1c35 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -335,7 +335,9 @@ function M.hover(_, result, ctx, config) return end if not (result and result.contents) then - vim.notify('No information available') + if config.silent ~= true then + vim.notify('No information available') + end return end local markdown_lines = util.convert_input_to_markdown_lines(result.contents) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 2c6ba823db..26f0e180f5 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1015,6 +1015,7 @@ end --- - border (string or table) override `border` --- - focusable (string or table) override `focusable` --- - zindex (string or table) override `zindex`, defaults to 50 +--- - relative ("mouse"|"cursor") defaults to "cursor" ---@returns (table) Options function M.make_floating_popup_options(width, height, opts) validate({ @@ -1029,7 +1030,8 @@ function M.make_floating_popup_options(width, height, opts) local anchor = '' local row, col - local lines_above = vim.fn.winline() - 1 + local lines_above = opts.relative == 'mouse' and vim.fn.getmousepos().line - 1 + or vim.fn.winline() - 1 local lines_below = vim.fn.winheight(0) - lines_above if lines_above < lines_below then @@ -1042,7 +1044,9 @@ function M.make_floating_popup_options(width, height, opts) row = 0 end - if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then + local wincol = opts.relative == 'mouse' and vim.fn.getmousepos().column or vim.fn.wincol() + + if wincol + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then anchor = anchor .. 'W' col = 0 else @@ -1062,7 +1066,7 @@ function M.make_floating_popup_options(width, height, opts) col = col + (opts.offset_x or 0), height = height, focusable = opts.focusable, - relative = 'cursor', + relative = opts.relative == 'mouse' and 'mouse' or 'cursor', row = row + (opts.offset_y or 0), style = 'minimal', width = width, -- cgit From ea2658e1f7a0791f7bf5b1da2417ea0c618121fc Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Tue, 10 Jan 2023 16:29:25 -0500 Subject: vim-patch:9.0.1174: smali files are not recognized (#21734) Problem: Smali files are not recognized. Solution: Add a pattern for Smali files. (Amaan Qureshi, closes vim/vim#11801) --- 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 c3ab39a1a3..7a1b79bba2 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -949,6 +949,7 @@ local extension = { ice = 'slice', score = 'slrnsc', sol = 'solidity', + smali = 'smali', tpl = 'smarty', ihlp = 'smcl', smcl = 'smcl', -- cgit From 8c5c2136fef12e6fe00739c91ff994344ab5d466 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 11 Jan 2023 17:15:54 +0100 Subject: vim-patch:9.0.1176: smithy files are not recognized (#21751) Problem: smithy files are not recognized. Solution: Add a pattern for Smithy files. (Chris Kipp, closes vim/vim#11804) https://github.com/vim/vim/commit/f68cddabffcbc5b8fbfe9003182cb4b55ff8d72c Co-authored-by: Chris Kipp --- runtime/lua/vim/filetype.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 7a1b79bba2..a6f40774d1 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -327,11 +327,7 @@ local extension = { end, eex = 'eelixir', leex = 'eelixir', - am = function(path, bufnr) - if not path:lower():find('makefile%.am$') then - return 'elf' - end - end, + am = 'elf', exs = 'elixir', elm = 'elm', elv = 'elvish', @@ -956,6 +952,7 @@ local extension = { hlp = 'smcl', smith = 'smith', smt = 'smith', + smithy = 'smithy', sml = 'sml', spt = 'snobol4', sno = 'snobol4', -- cgit From a37c686d21c1ad4e50f455e989642d38435d41ba Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Wed, 11 Jan 2023 20:17:10 +0100 Subject: docs(lsp): update buf_notify and rpc.notify params types (#21753) Small, but I was getting warnings about my usage of `vim.lsp.buf_notify(bufnr, method, {example = example})` since the docs say that `params` must be a string, however this can really be anything when it's passed to `rpc.notify` since we just end up calling `vim.json.encode(payload)` on it. This fixes the docs in those two places and regenerates them. --- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/rpc.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 13f8c81dfb..cfd6c938f7 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -2048,7 +2048,7 @@ end --- Send a notification to a server ---@param bufnr (number|nil) The number of the buffer ---@param method (string) Name of the request method ----@param params (string) Arguments to send to the server +---@param params (any) Arguments to send to the server --- ---@returns true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index b93b227150..f1492601ff 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -293,7 +293,7 @@ end ---@private --- Sends a notification to the LSP server. ---@param method (string) The invoked LSP method ----@param params (table|nil): Parameters for the invoked LSP method +---@param params (any): Parameters for the invoked LSP method ---@returns (bool) `true` if notification could be sent, `false` if not function Client:notify(method, params) return self:encode_and_send({ -- cgit From 143d3f1f3224bca02bfef7df0932b9d7524a3ff2 Mon Sep 17 00:00:00 2001 From: jdrouhard Date: Thu, 12 Jan 2023 00:13:52 -0600 Subject: fix(lsp): revert semantic token gravity change from #21574 (#21763) --- runtime/lua/vim/lsp/semantic_tokens.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index b035ac43f4..b1bc48dac6 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -408,7 +408,6 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. token.type, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens, - end_right_gravity = true, strict = false, }) @@ -419,7 +418,6 @@ function STHighlighter:on_win(topline, botline) hl_group = '@' .. modifier, end_col = token.end_col, priority = vim.highlight.priorities.semantic_tokens + 1, - end_right_gravity = true, strict = false, }) end -- cgit From 2aabe9b85870912b9eb8fe2ced0c21544de66f58 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 12 Jan 2023 09:21:58 +0100 Subject: vim-patch:9.0.1182: go checksum files are not recognized (#21758) Problem: go checksum files are not recognized. Solution: Add the name of go checksum files. (Amaan Qureshi, closes vim/vim#11803) https://github.com/vim/vim/commit/043d7b2c84cda275354aa023b5769660ea70a168 Co-authored-by: Amaan Q --- 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 a6f40774d1..169285686a 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1425,6 +1425,7 @@ local filename = { gnashpluginrc = 'gnash', gnashrc = 'gnash', ['.gnuplot'] = 'gnuplot', + ['go.sum'] = 'gosum', ['go.work'] = 'gowork', ['.gprc'] = 'gp', ['/.gnupg/gpg.conf'] = 'gpg', -- cgit From 443bbfd59e2818aeee72d2ed77af1cb24617e46f Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Thu, 12 Jan 2023 15:51:19 +0100 Subject: docs(lsp): fix type annotation on convert_input_to_markdown_lines (#21772) This small changes just ensures that if you're using `convert_input_to_markdown_lines` without `contents` you don't get a warning (when using something like neodev) that there is an expected second param, since it can be nil. --- 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 26f0e180f5..38051e6410 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -856,7 +856,7 @@ end --- `textDocument/signatureHelp`, and potentially others. --- ---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`) ----@param contents (table, optional, default `{}`) List of strings to extend with converted lines +---@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}. ---@returns {contents}, extended with lines of converted markdown. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover function M.convert_input_to_markdown_lines(input, contents) -- cgit From 572cd8031dd7c91ac9e17cbdfab16d87f1fed6b7 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 13 Jan 2023 00:57:39 +0800 Subject: feat(diagnostic): vim.diagnostic.is_disabled() #21527 --- runtime/lua/vim/diagnostic.lua | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index f41f4a9c68..6fd000a029 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -251,19 +251,6 @@ local function get_bufnr(bufnr) return bufnr end ----@private -local function is_disabled(namespace, bufnr) - local ns = M.get_namespace(namespace) - if ns.disabled then - return true - end - - if type(diagnostic_disabled[bufnr]) == 'table' then - return diagnostic_disabled[bufnr][namespace] - end - return diagnostic_disabled[bufnr] -end - ---@private local function diagnostic_lines(diagnostics) if not diagnostics then @@ -1119,6 +1106,27 @@ function M.hide(namespace, bufnr) end end +--- Check whether diagnostics are disabled in a given buffer. +--- +---@param bufnr number|nil Buffer number, or 0 for current buffer. +---@param namespace number|nil Diagnostic namespace. When omitted, checks if +--- all diagnostics are disabled in {bufnr}. +--- Otherwise, only checks if diagnostics from +--- {namespace} are disabled. +---@return boolean +function M.is_disabled(bufnr, namespace) + bufnr = get_bufnr(bufnr) + if namespace and M.get_namespace(namespace).disabled then + return true + end + + if type(diagnostic_disabled[bufnr]) == 'table' then + return diagnostic_disabled[bufnr][namespace] + end + + return diagnostic_disabled[bufnr] ~= nil +end + --- Display diagnostics for the given namespace and buffer. --- ---@param namespace number|nil Diagnostic namespace. When omitted, show @@ -1162,7 +1170,7 @@ function M.show(namespace, bufnr, diagnostics, opts) return end - if is_disabled(namespace, bufnr) then + if M.is_disabled(bufnr, namespace) then return end -- cgit From fd3d30a22c2bcc7acd266214b761b44219aefbe7 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 13 Jan 2023 17:27:35 +0100 Subject: vim-patch:9.0.1191: some Bazel files are not recognized (#21784) Problem: Some Bazel files are not recognized. Solution: Add an extra Bazel pattern. (Keith Smily, closes vim/vim#11807) https://github.com/vim/vim/commit/3213952966896ffb1d8fa186bcf8c43359fca0f0 Co-authored-by: Keith Smiley --- 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 169285686a..b73519f1be 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1321,6 +1321,7 @@ local filename = { ['GNUmakefile.am'] = 'automake', ['named.root'] = 'bindzone', WORKSPACE = 'bzl', + ['WORKSPACE.bzlmod'] = 'bzl', BUILD = 'bzl', ['cabal.project'] = 'cabalproject', ['cabal.config'] = 'cabalconfig', -- cgit From c752c853630898f38bcf46a9a5e9d83e41989eeb Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 15 Jan 2023 16:00:23 +0100 Subject: refactor: format with stylua (#21821) --- runtime/lua/vim/lsp.lua | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index cfd6c938f7..134a009a72 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1557,15 +1557,21 @@ end --- Notify all attached clients that a buffer has changed. local text_document_did_change_handler do - text_document_did_change_handler = - function(_, bufnr, changedtick, firstline, lastline, new_lastline) - -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached - if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then - return true - end - util.buf_versions[bufnr] = changedtick - changetracking.send_changes(bufnr, firstline, lastline, new_lastline) + text_document_did_change_handler = function( + _, + bufnr, + changedtick, + firstline, + lastline, + new_lastline + ) + -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached + if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then + return true end + util.buf_versions[bufnr] = changedtick + changetracking.send_changes(bufnr, firstline, lastline, new_lastline) + end end ---@private -- cgit From 34b973b1d9e3b0c6f546e3aa661c29edd5a1ab87 Mon Sep 17 00:00:00 2001 From: Naru Date: Mon, 16 Jan 2023 06:32:23 +0900 Subject: docs(lua): use luaref tag instead of www.lua.org #21813 --- runtime/lua/vim/shared.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index b53c66ba63..7967d13943 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -60,6 +60,7 @@ end)() --- Splits a string at each instance of a separator. --- ---@see |vim.split()| +---@see |luaref-patterns| ---@see https://www.lua.org/pil/20.2.html ---@see http://lua-users.org/wiki/StringLibraryTutorial --- @@ -529,6 +530,7 @@ end --- Trim whitespace (Lua pattern "%s") from both sides of a string. --- +---@see |luaref-patterns| ---@see https://www.lua.org/pil/20.2.html ---@param s string String to trim ---@return string String with whitespace removed from its beginning and end -- cgit From 307efe4906de9f0b7d8611ea36bddb85493c1447 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Mon, 16 Jan 2023 04:55:24 -0500 Subject: health: migrate to Lua #21661 * refactor: remove all vimscript from nvim/health * fixup: previous method broke if you had a folder named 'x-lua' --- runtime/lua/vim/health.lua | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index b875da0abc..044880e076 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -23,7 +23,20 @@ end local path2name = function(path) if path:match('%.lua$') then -- Lua: transform "../lua/vim/lsp/health.lua" into "vim.lsp" - return path:gsub('.-lua[%\\%/]', '', 1):gsub('[%\\%/]', '.'):gsub('%.health.-$', '') + + -- Get full path, make sure all slashes are '/' + path = vim.fs.normalize(path) + + -- Remove everything up to the last /lua/ folder + path = path:gsub('^.*/lua/', '') + + -- Remove the filename (health.lua) + path = vim.fn.fnamemodify(path, ':h') + + -- Change slashes to dots + path = path:gsub('/', '.') + + return path else -- Vim: transform "../autoload/health/provider.vim" into "provider" return vim.fn.fnamemodify(path, ':t:r') -- cgit From ef89f9fd46ab591183b7f59f31f5a2e55f7a526b Mon Sep 17 00:00:00 2001 From: Ching Pei Yang <59727193+horriblename@users.noreply.github.com> Date: Mon, 16 Jan 2023 13:39:19 +0100 Subject: docs: treesitter.add_directive, add_predicate #21206 --- runtime/lua/vim/treesitter/query.lua | 10 ++++++++-- 1 file changed, 8 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 4bec5db527..dbf134573d 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -419,7 +419,8 @@ local directive_handlers = { --- Adds a new predicate to be used in queries --- ---@param name string Name of the predicate, without leading # ----@param handler function(match:string, pattern:string, bufnr:number, predicate:function) +---@param handler function(match:table, pattern:string, bufnr:number, predicate:string[]) +--- - see |vim.treesitter.query.add_directive()| for argument meanings function M.add_predicate(name, handler, force) if predicate_handlers[name] and not force then error(string.format('Overriding %s', name)) @@ -436,7 +437,12 @@ end --- metadata table `metadata[capture_id].key = value` --- ---@param name string Name of the directive, without leading # ----@param handler function(match:string, pattern:string, bufnr:number, predicate:function, metadata:table) +---@param handler function(match:table, pattern:string, bufnr:number, predicate:string[], metadata:table) +--- - match: see |treesitter-query| +--- - node-level data are accessible via `match[capture_id]` +--- - pattern: see |treesitter-query| +--- - predicate: list of strings containing the full directive being called, e.g. +--- `(node (#set! conceal "-"))` would get the predicate `{ "#set!", "conceal", "-" }` function M.add_directive(name, handler, force) if directive_handlers[name] and not force then error(string.format('Overriding %s', name)) -- cgit From 20b7be2d1024b6e9eac68e0cb92cf663e95e51ca Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 17 Jan 2023 16:56:23 +0000 Subject: fix(treesitter): really restore syntax - also unset b:ts_highlight on stop() Fixes: #21836 --- runtime/lua/vim/treesitter/highlighter.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index e99994c8a9..d77a0d0d03 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -88,7 +88,6 @@ function TSHighlighter.new(tree, opts) end end - self.orig_syntax = vim.bo[self.bufnr].syntax self.orig_spelloptions = vim.bo[self.bufnr].spelloptions vim.bo[self.bufnr].syntax = '' @@ -120,8 +119,11 @@ function TSHighlighter:destroy() end if vim.api.nvim_buf_is_loaded(self.bufnr) then - vim.bo[self.bufnr].syntax = self.orig_syntax vim.bo[self.bufnr].spelloptions = self.orig_spelloptions + vim.b[self.bufnr].ts_highlight = nil + if vim.g.syntax_on == 1 then + a.nvim_exec_autocmds('FileType', { group = 'syntaxset', buffer = self.bufnr }) + end end end -- cgit From d8d39344e38d27acccbab268bef19034ef7cd579 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 20 Jan 2023 14:59:31 +0800 Subject: fix(lsp): fix `removed` param value in add_workspace_folder (#21915) --- 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 8f4bd15eaa..87b3ac6f1e 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -487,7 +487,7 @@ function M.add_workspace_folder(workspace_folder) end local params = util.make_workspace_params( { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }, - { {} } + {} ) for _, client in pairs(vim.lsp.get_active_clients({ bufnr = 0 })) do local found = false -- cgit From 7ef5e363d360f86c5d8d403e90ed256f4de798ec Mon Sep 17 00:00:00 2001 From: kishii <24446852+zhoudaxia2016@users.noreply.github.com> Date: Sat, 21 Jan 2023 15:22:34 +0800 Subject: feat(lsp): add triggerKind option for vim.lsp.buf.code_action (#21905) --- runtime/lua/vim/lsp/buf.lua | 5 +++++ runtime/lua/vim/lsp/protocol.lua | 11 +++++++++++ 2 files changed, 16 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 87b3ac6f1e..be237eb494 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -733,6 +733,7 @@ end --- List of LSP `CodeActionKind`s used to filter the code actions. --- Most language servers support values like `refactor` --- or `quickfix`. +--- - triggerKind (number|nil): The reason why code actions were requested. --- - filter: (function|nil) --- Predicate taking an `CodeAction` and returning a boolean. --- - apply: (boolean|nil) @@ -746,6 +747,7 @@ end --- using mark-like indexing. See |api-indexing| --- ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction +---@see vim.lsp.protocol.constants.CodeActionTriggerKind function M.code_action(options) validate({ options = { options, 't', true } }) options = options or {} @@ -755,6 +757,9 @@ function M.code_action(options) options = { options = options } end local context = options.context or {} + if not context.triggerKind then + context.triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Invoked + end if not context.diagnostics then local bufnr = api.nvim_get_current_buf() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 92cda0b34f..12345b6c8c 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -304,6 +304,17 @@ local constants = { -- Base kind for an organize imports source action 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, + -- Code actions were requested automatically. + -- + -- This typically happens when current selection in a file changes, but can + -- also be triggered when file content changes. + Automatic = 2, + }, } for k, v in pairs(constants) do -- cgit From 80bede1dfc6c2fcd5ec013045585802bb18d9fc9 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Sun, 22 Jan 2023 09:08:33 -0500 Subject: vim-patch:9.0.1229: Cap'n Proto files are not recognized (#21950) Problem: Cap'n Proto files are not recognized. Solution: Add a pattern and the "capnp" filetype. (Amaan Qureshi, closes vim/vim#11862) https://github.com/vim/vim/commit/040e795e8da05ff38cc896528d4dcad100f0b584 --- 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 b73519f1be..34815f89ca 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -190,6 +190,7 @@ local extension = { BUILD = 'bzl', qc = 'c', cabal = 'cabal', + capnp = 'capnp', cdl = 'cdl', toc = 'cdrtoc', cfc = 'cf', -- cgit From 8e52d8a394799ede43ae5e80574f12dede9bc13e Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 22 Jan 2023 18:54:09 +0100 Subject: fix(lsp): assert workspace/applyEdit receives params (#21945) According to the specification `workspace/applyEdit` must be called with `ApplyWorkspaceEditParams`. So far the client just returned, which could lead to a misleading error on the server side because `workspace/applyEdit` must respond with a `ApplyWorkspaceEditResult`. This adds an assertion to clarify that the server is violating the specification. See https://github.com/neovim/neovim/issues/21925 --- runtime/lua/vim/lsp/handlers.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index b383ca1c35..5096100a60 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -131,9 +131,10 @@ end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit M['workspace/applyEdit'] = function(_, workspace_edit, ctx) - if not workspace_edit then - return - end + assert( + workspace_edit, + 'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification' + ) -- TODO(ashkan) Do something more with label? local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) -- cgit From 1b642648f6c94c0bc0db521714a922207ef8bbc5 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Sun, 22 Jan 2023 18:08:08 -0500 Subject: vim-patch:9.0.1230: Apache Thrift files are not recognized (#21955) Problem: Apache thrift files are not recognized. Solution: Add a pattern for thrift files. (Amaan Qureshi, closes vim/vim#11859) https://github.com/vim/vim/commit/f3da4c8427b1b12d7aaffa307ec085ca97ea9ad9 --- 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 34815f89ca..9293c828b8 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1022,6 +1022,7 @@ local extension = { texinfo = 'texinfo', text = 'text', tfvars = 'terraform-vars', + thrift = 'thrift', tla = 'tla', tli = 'tli', toml = 'toml', -- cgit From cb757f2663e6950e655c6306d713338dfa66b18d Mon Sep 17 00:00:00 2001 From: Arnout Engelen Date: Mon, 23 Jan 2023 10:26:46 +0100 Subject: build: make generated source files reproducible #21586 Problem: Build is not reproducible, because generated source files (.c/.h/) are not deterministic, mostly because Lua pairs() is unordered by design (for security). https://github.com/LuaJIT/LuaJIT/issues/626#issuecomment-707005671 https://www.lua.org/manual/5.1/manual.html#pdf-next > The order in which the indices are enumerated is not specified [...] > >> The hardening of the VM deliberately randomizes string hashes. This in >> turn randomizes the iteration order of tables with string keys. Solution: - Update the code generation scripts to be deterministic. - That is only a partial solution: the exported function (funcs_metadata.generated.h) and ui event (ui_events_metadata.generated.h) metadata have some mpack'ed tables, which are not serialized deterministically. - As a workaround, introduce `PRG_GEN_LUA` cmake setting, so you can inject a modified build of luajit (with LUAJIT_SECURITY_PRN=0) that preserves table order. - Longer-term we should change the mpack'ed data structure so it no longer uses tables keyed by strings. Closes #20124 Co-Authored-By: dundargoc Co-Authored-By: Arnout Engelen --- runtime/lua/vim/shared.lua | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 7967d13943..cc48e3f193 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -458,6 +458,33 @@ function vim.tbl_flatten(t) return result end +--- Enumerate a table sorted by its keys. +--- +---@see Based on https://github.com/premake/premake-core/blob/master/src/base/table.lua +--- +---@param t table List-like table +---@return iterator over sorted keys and their values +function vim.spairs(t) + assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) + + -- collect the keys + local keys = {} + for k in pairs(t) do + table.insert(keys, k) + end + table.sort(keys) + + -- Return the iterator function. + -- TODO(justinmk): Return "iterator function, table {t}, and nil", like pairs()? + local i = 0 + return function() + i = i + 1 + if keys[i] then + return keys[i], t[keys[i]] + end + end +end + --- Tests if a Lua table can be treated as an array. --- --- Empty table `{}` is assumed to be an array, unless it was created by -- cgit From bcdbf77537cec4567a334a39fd5965a3f5668a1a Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 24 Jan 2023 06:15:43 +0000 Subject: fix(lsp): check method is supported when range formatting (#21970) `vim.lsp.buf.format()` silently did nothing if no servers supported `textDocument/rangeFormatting` when formatting with a range. Issue found by `@hwrd:matrix.org` in the Matrix chat. --- runtime/lua/vim/lsp/buf.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index be237eb494..6ac885c78f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -197,20 +197,21 @@ function M.format(options) clients = vim.tbl_filter(options.filter, clients) end + local mode = api.nvim_get_mode().mode + local range = options.range + if not range and mode == 'v' or mode == 'V' then + range = range_from_selection() + end + local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' + clients = vim.tbl_filter(function(client) - return client.supports_method('textDocument/formatting') + return client.supports_method(method) end, clients) if #clients == 0 then vim.notify('[LSP] Format request failed, no matching language servers.') end - local mode = api.nvim_get_mode().mode - local range = options.range - if not range and mode == 'v' or mode == 'V' then - range = range_from_selection() - end - ---@private local function set_range(client, params) if range then @@ -221,7 +222,6 @@ function M.format(options) return params end - local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting' if options.async then local do_format do_format = function(idx, client) -- cgit From 9166116c6730ef1687f35b8c58653583d2c301c3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 24 Jan 2023 18:04:15 +0000 Subject: doc(lsp): format arguments to start_client() (#21980) docs(lsp): format arguments to start_client() --- runtime/lua/vim/lsp.lua | 215 ++++++++++++++++++++++++------------------------ 1 file changed, 107 insertions(+), 108 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 134a009a72..c5392ac154 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -897,115 +897,114 @@ end -- --- Starts and initializes a client with the given configuration. --- ---- Parameter `cmd` is required. ---- ---- The following parameters describe fields in the {config} table. ---- ---- ----@param cmd: (table|string|fun(dispatchers: table):table) command string or ---- list treated like |jobstart()|. The command must launch the language server ---- process. `cmd` can also be a function that creates an RPC client. ---- The function receives a dispatchers table and must return a table with the ---- functions `request`, `notify`, `is_closing` and `terminate` ---- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()| ---- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()| ---- ----@param cmd_cwd: (string, default=|getcwd()|) Directory to launch ---- the `cmd` process. Not related to `root_dir`. ---- ----@param cmd_env: (table) Environment flags to pass to the LSP on ---- spawn. Can be specified using keys like a map or as a list with `k=v` ---- pairs or both. Non-string values are coerced to string. ---- Example: ----
---- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
---- 
---- ----@param detached: (boolean, default true) Daemonize the server process so that it runs in a ---- separate process group from Nvim. Nvim will shutdown the process on exit, but if Nvim fails to ---- exit cleanly this could leave behind orphaned server processes. ---- ----@param workspace_folders (table) List of workspace folders passed to the ---- language server. For backwards compatibility rootUri and rootPath will be ---- derived from the first workspace folder in this list. See `workspaceFolders` in ---- the LSP spec. ---- ----@param capabilities Map overriding the default capabilities defined by ---- |vim.lsp.protocol.make_client_capabilities()|, passed to the language ---- server on initialization. Hint: use make_client_capabilities() and modify ---- its result. ---- - Note: To send an empty dictionary use ---- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an ---- array. ---- ----@param handlers Map of language server method names to |lsp-handler| ---- ----@param settings Map with language server specific settings. These are ---- returned to the language server if requested via `workspace/configuration`. ---- Keys are case-sensitive. ---- ----@param commands 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 ---- 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. ---- ----@param init_options Values to pass in the initialization request ---- as `initializationOptions`. See `initialize` in the LSP spec. ---- ----@param name (string, default=client-id) Name in log messages. ---- ----@param get_language_id function(bufnr, filetype) -> language ID as string. ---- Defaults to the filetype. ---- ----@param offset_encoding (default="utf-16") One of "utf-8", "utf-16", ---- or "utf-32" which is the encoding that the LSP server expects. Client does ---- not verify this is correct. ---- ----@param on_error Callback with parameters (code, ...), invoked ---- when the client operation throws an error. `code` is a number describing ---- the error. Other arguments may be passed depending on the error kind. See ---- `vim.lsp.rpc.client_errors` for possible errors. ---- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name. ---- ----@param before_init Callback with parameters (initialize_params, config) ---- 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()|. You can use this to modify parameters before ---- they are sent. ---- ----@param on_init Callback (client, initialize_result) invoked after LSP ---- "initialize", where `result` is a table of `capabilities` and anything else ---- the server may send. For example, clangd sends ---- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was ---- sent to it. You can only modify the `client.offset_encoding` here before ---- any notifications are sent. Most language servers expect to be sent client specified settings after ---- initialization. Neovim does not make this assumption. A ---- `workspace/didChangeConfiguration` notification should be sent ---- to the server during on_init. ---- ----@param on_exit Callback (code, signal, client_id) invoked on client +--- Field `cmd` in {config} is required. +--- +---@param config (table) Configuration for the server: +--- - cmd: (table|string|fun(dispatchers: table):table) command string or +--- list treated like |jobstart()|. The command must launch the language server +--- process. `cmd` can also be a function that creates an RPC client. +--- The function receives a dispatchers table and must return a table with the +--- functions `request`, `notify`, `is_closing` and `terminate` +--- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()| +--- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()| +--- +--- - cmd_cwd: (string, default=|getcwd()|) Directory to launch +--- the `cmd` process. Not related to `root_dir`. +--- +--- - cmd_env: (table) Environment flags to pass to the LSP on +--- spawn. Can be specified using keys like a map or as a list with `k=v` +--- pairs or both. Non-string values are coerced to string. +--- Example: +---
+---                   { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
+---       
+--- +--- - detached: (boolean, default true) Daemonize the server process so that it runs in a +--- separate process group from Nvim. Nvim will shutdown the process on exit, but if Nvim fails to +--- exit cleanly this could leave behind orphaned server processes. +--- +--- - workspace_folders: (table) List of workspace folders passed to the +--- language server. For backwards compatibility rootUri and rootPath will be +--- derived from the first workspace folder in this list. See `workspaceFolders` in +--- the LSP spec. +--- +--- - capabilities: Map overriding the default capabilities defined by +--- |vim.lsp.protocol.make_client_capabilities()|, passed to the language +--- server on initialization. Hint: use make_client_capabilities() and modify +--- its result. +--- - Note: To send an empty dictionary use +--- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an +--- array. +--- +--- - handlers: Map of language server method names to |lsp-handler| +--- +--- - settings: Map with language server specific settings. These are +--- returned to the language server if requested via `workspace/configuration`. +--- Keys are case-sensitive. +--- +--- - commands: 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 +--- 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. +--- +--- - init_options Values to pass in the initialization request +--- as `initializationOptions`. See `initialize` in the LSP spec. +--- +--- - name: (string, default=client-id) Name in log messages. +--- +--- - get_language_id: function(bufnr, filetype) -> language ID as string. +--- Defaults to the filetype. +--- +--- - offset_encoding: (default="utf-16") One of "utf-8", "utf-16", +--- or "utf-32" which is the encoding that the LSP server expects. Client does +--- not verify this is correct. +--- +--- - on_error: Callback with parameters (code, ...), invoked +--- when the client operation throws an error. `code` is a number describing +--- the error. Other arguments may be passed depending on the error kind. See +--- `vim.lsp.rpc.client_errors` for possible errors. +--- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name. +--- +--- - before_init: Callback with parameters (initialize_params, config) +--- 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()|. You can use this to modify parameters before +--- they are sent. +--- +--- - on_init: Callback (client, initialize_result) invoked after LSP +--- "initialize", where `result` is a table of `capabilities` and anything else +--- the server may send. For example, clangd sends +--- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was +--- sent to it. You can only modify the `client.offset_encoding` here before +--- any notifications are sent. Most language servers expect to be sent client specified settings after +--- initialization. Neovim does not make this assumption. A +--- `workspace/didChangeConfiguration` notification should be sent +--- to the server during on_init. +--- +--- - on_exit Callback (code, signal, client_id) invoked on client --- exit. ---- - code: exit code of the process ---- - signal: number describing the signal used to terminate (if any) ---- - client_id: client handle ---- ----@param on_attach Callback (client, bufnr) invoked when client ---- attaches to a buffer. ---- ----@param trace: "off" | "messages" | "verbose" | nil passed directly to the language ---- server in the initialize request. Invalid/empty values will default to "off" ----@param flags: A table with flags for the client. The current (experimental) flags are: ---- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits ---- - debounce_text_changes (number, default 150): Debounce didChange ---- notifications to the server by the given number in milliseconds. No debounce ---- occurs if nil ---- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to ---- exit cleanly after sending the "shutdown" request before sending kill -15. ---- If set to false, nvim exits immediately after sending the "shutdown" request to the server. ---- ----@param root_dir string Directory where the LSP ---- server will base its workspaceFolders, rootUri, and rootPath ---- on initialization. +--- - code: exit code of the process +--- - signal: number describing the signal used to terminate (if any) +--- - client_id: client handle +--- +--- - on_attach: Callback (client, bufnr) invoked when client +--- attaches to a buffer. +--- +--- - trace: ("off" | "messages" | "verbose" | nil) passed directly to the language +--- server in the initialize request. Invalid/empty values will default to "off" +--- +--- - flags: A table with flags for the client. The current (experimental) flags are: +--- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits +--- - debounce_text_changes (number, default 150): Debounce didChange +--- notifications to the server by the given number in milliseconds. No debounce +--- occurs if nil +--- - exit_timeout (number|boolean, default false): Milliseconds to wait for server to +--- exit cleanly after sending the "shutdown" request before sending kill -15. +--- If set to false, nvim exits immediately after sending the "shutdown" request to the server. +--- +--- - root_dir: (string) Directory where the LSP +--- server will base its workspaceFolders, rootUri, and rootPath +--- on initialization. --- ---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be --- fully initialized. Use `on_init` to do any actions once -- cgit From 314d3ce1eb1da284baf9b33f30500473afe73144 Mon Sep 17 00:00:00 2001 From: "C.D. MacEachern" Date: Tue, 24 Jan 2023 17:45:30 -0500 Subject: docs(vim.fs): normalize Windows example was incorrect (#21966) --- 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 65e6ca677c..a0d2c4c339 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -282,7 +282,7 @@ end --- --- Examples: ---
lua
----   vim.fs.normalize('C:\\Users\\jdoe')
+---   vim.fs.normalize('C:\\\\Users\\\\jdoe')
 ---   --> 'C:/Users/jdoe'
 ---
 ---   vim.fs.normalize('~/src/neovim')
-- 
cgit