diff options
Diffstat (limited to 'runtime/lua')
86 files changed, 42476 insertions, 5863 deletions
diff --git a/runtime/lua/_vim9script.lua b/runtime/lua/_vim9script.lua index 363d061451..ca0e913d51 100644 --- a/runtime/lua/_vim9script.lua +++ b/runtime/lua/_vim9script.lua @@ -162,7 +162,7 @@ local vim9 = (function() end end - vim.api.nvim_exec(table.concat(file, '\n'), false) + vim.api.nvim_exec2(table.concat(file, '\n'), { output = false }) end, }) end @@ -312,7 +312,7 @@ vim9['fn'] = (function() -- We do have vim9script ;) that's this plugin ['vim9script'] = true, - -- Include some vim patches that are sometimes required by variuos vim9script plugins + -- Include some vim patches that are sometimes required by various vim9script plugins -- that we implement via vim9jit [ [[patch-8.2.2261]] ] = true, [ [[patch-8.2.4257]] ] = true, @@ -513,7 +513,7 @@ vim9['import'] = (function() imported.absolute = setmetatable({}, { __index = function(self, name) - if vim.loop.fs_stat(name) then + if vim.uv.fs_stat(name) then local result = loadfile(name)() rawset(self, name, result) diff --git a/runtime/lua/coxpcall.lua b/runtime/lua/coxpcall.lua new file mode 100644 index 0000000000..6b179f1ef0 --- /dev/null +++ b/runtime/lua/coxpcall.lua @@ -0,0 +1,108 @@ +------------------------------------------------------------------------------- +-- Coroutine safe xpcall and pcall versions +-- +-- Encapsulates the protected calls with a coroutine based loop, so errors can +-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines +-- yielding inside the call to pcall or xpcall. +-- +-- Authors: Roberto Ierusalimschy and Andre Carregal +-- Contributors: Thomas Harning Jr., Ignacio BurgueƱo, Fabio Mascarenhas +-- +-- Copyright 2005 - Kepler Project +-- +-- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $ +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- Checks if (x)pcall function is coroutine safe +------------------------------------------------------------------------------- +local function isCoroutineSafe(func) + local co = coroutine.create(function() + return func(coroutine.yield, function() end) + end) + + coroutine.resume(co) + return coroutine.resume(co) +end + +-- No need to do anything if pcall and xpcall are already safe. +if isCoroutineSafe(pcall) and isCoroutineSafe(xpcall) then + copcall = pcall + coxpcall = xpcall + return { pcall = pcall, xpcall = xpcall, running = coroutine.running } +end + +------------------------------------------------------------------------------- +-- Implements xpcall with coroutines +------------------------------------------------------------------------------- +local performResume, handleReturnValue +local oldpcall, oldxpcall = pcall, xpcall +local pack = table.pack or function(...) return {n = select("#", ...), ...} end +local unpack = table.unpack or unpack +local running = coroutine.running +local coromap = setmetatable({}, { __mode = "k" }) + +function handleReturnValue(err, co, status, ...) + if not status then + return false, err(debug.traceback(co, (...)), ...) + end + if coroutine.status(co) == 'suspended' then + return performResume(err, co, coroutine.yield(...)) + else + return true, ... + end +end + +function performResume(err, co, ...) + return handleReturnValue(err, co, coroutine.resume(co, ...)) +end + +local function id(trace, ...) + return trace +end + +function coxpcall(f, err, ...) + local current = running() + if not current then + if err == id then + return oldpcall(f, ...) + else + if select("#", ...) > 0 then + local oldf, params = f, pack(...) + f = function() return oldf(unpack(params, 1, params.n)) end + end + return oldxpcall(f, err) + end + else + local res, co = oldpcall(coroutine.create, f) + if not res then + local newf = function(...) return f(...) end + co = coroutine.create(newf) + end + coromap[co] = current + return performResume(err, co, ...) + end +end + +local function corunning(coro) + if coro ~= nil then + assert(type(coro)=="thread", "Bad argument; expected thread, got: "..type(coro)) + else + coro = running() + end + while coromap[coro] do + coro = coromap[coro] + end + if coro == "mainthread" then return nil end + return coro +end + +------------------------------------------------------------------------------- +-- Implements pcall with coroutines +------------------------------------------------------------------------------- + +function copcall(f, ...) + return coxpcall(f, id, ...) +end + +return { pcall = copcall, xpcall = coxpcall, running = corunning } diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua index 2079006234..49d63807a6 100644 --- a/runtime/lua/editorconfig.lua +++ b/runtime/lua/editorconfig.lua @@ -1,5 +1,6 @@ local M = {} +--- @type table<string,fun(bufnr: integer, val: string, opts?: table)> M.properties = {} --- Modified version of the builtin assert that does not include error position information @@ -19,14 +20,14 @@ end --- ---@private local function warn(msg, ...) - vim.notify(string.format(msg, ...), vim.log.levels.WARN, { + vim.notify_once(string.format(msg, ...), vim.log.levels.WARN, { title = 'editorconfig', }) end function M.properties.charset(bufnr, val) assert( - vim.tbl_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val), + vim.list_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val), 'charset must be one of "utf-8", "utf-8-bom", "latin1", "utf-16be", or "utf-16le"' ) if val == 'utf-8' or val == 'utf-8-bom' then @@ -111,7 +112,20 @@ end function M.properties.insert_final_newline(bufnr, val) assert(val == 'true' or val == 'false', 'insert_final_newline must be either "true" or "false"') vim.bo[bufnr].fixendofline = val == 'true' - vim.bo[bufnr].endofline = val == 'true' + + -- 'endofline' can be read to detect if the file contains a final newline, + -- so only change 'endofline' right before writing the file + local endofline = val == 'true' + if vim.bo[bufnr].endofline ~= endofline then + vim.api.nvim_create_autocmd('BufWritePre', { + group = 'editorconfig', + buffer = bufnr, + once = true, + callback = function() + vim.bo[bufnr].endofline = endofline + end, + }) + end end --- Modified version of |glob2regpat()| that does not match path separators on *. @@ -168,12 +182,12 @@ end --- ---@param filepath string File path of the file to apply EditorConfig settings to ---@param dir string Current directory ----@return table Table of options to apply to the given file +---@return table<string,string|boolean> Table of options to apply to the given file --- ---@private local function parse(filepath, dir) - local pat = nil - local opts = {} + local pat --- @type vim.regex? + local opts = {} --- @type table<string,string|boolean> local f = io.open(dir .. '/.editorconfig') if f then for line in f:lines() do @@ -189,6 +203,7 @@ local function parse(filepath, dir) end elseif key ~= nil and val ~= nil then if key == 'root' then + assert(val == 'true' or val == 'false', 'root must be either "true" or "false"') opts.root = val == 'true' elseif pat and pat:match_str(filepath) then opts[key] = val @@ -202,17 +217,21 @@ end --- Configure the given buffer with options from an .editorconfig file --- ----@param bufnr number Buffer number to configure +---@param bufnr integer Buffer number to configure --- ---@private function M.config(bufnr) bufnr = bufnr or vim.api.nvim_get_current_buf() + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + local path = vim.fs.normalize(vim.api.nvim_buf_get_name(bufnr)) if vim.bo[bufnr].buftype ~= '' or not vim.bo[bufnr].modifiable or path == '' then return end - local opts = {} + local opts = {} --- @type table<string,string|boolean> for parent in vim.fs.parents(path) do for k, v in pairs(parse(path, parent)) do if opts[k] == nil then @@ -225,7 +244,7 @@ function M.config(bufnr) end end - local applied = {} + local applied = {} --- @type table<string,string|boolean> for opt, val in pairs(opts) do if val ~= 'unset' then local func = M.properties[opt] diff --git a/runtime/lua/health.lua b/runtime/lua/health.lua deleted file mode 100644 index 40e2b3c3e7..0000000000 --- a/runtime/lua/health.lua +++ /dev/null @@ -1,6 +0,0 @@ -return setmetatable({}, { - __index = function(_, k) - vim.deprecate("require('health')", 'vim.health', '0.9', false) - return vim.health[k] - end, -}) diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index 0956022ac6..dcdfc2b87f 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -2,6 +2,8 @@ local api, fn = vim.api, vim.fn local FIND_ARG = '-w' local localfile_arg = true -- Always use -l if possible. #6683 + +---@type table[] local buf_hls = {} local M = {} @@ -12,83 +14,32 @@ local function man_error(msg) end -- Run a system command and timeout after 30 seconds. -local function system(cmd_, silent, env) - local stdout_data = {} - local stderr_data = {} - local stdout = vim.loop.new_pipe(false) - local stderr = vim.loop.new_pipe(false) - - local done = false - local exit_code - - -- We use the `env` command here rather than the env option to vim.loop.spawn since spawn will - -- completely overwrite the environment when we just want to modify the existing one. - -- - -- Overwriting mainly causes problems NixOS which relies heavily on a non-standard environment. - local cmd - if env then - cmd = { 'env' } - vim.list_extend(cmd, env) - vim.list_extend(cmd, cmd_) - else - cmd = cmd_ - end - - local handle - handle = vim.loop.spawn(cmd[1], { - args = vim.list_slice(cmd, 2), - stdio = { nil, stdout, stderr }, - }, function(code) - exit_code = code - stdout:close() - stderr:close() - handle:close() - done = true - end) - - if handle then - stdout:read_start(function(_, data) - stdout_data[#stdout_data + 1] = data - end) - stderr:read_start(function(_, data) - stderr_data[#stderr_data + 1] = data - end) - else - stdout:close() - stderr:close() - if not silent then - local cmd_str = table.concat(cmd, ' ') - man_error(string.format('command error: %s', cmd_str)) - end - end - - vim.wait(30000, function() - return done - end) - - if not done then - if handle then - handle:close() - stdout:close() - stderr:close() - end - local cmd_str = table.concat(cmd, ' ') - man_error(string.format('command timed out: %s', cmd_str)) - end - - if exit_code ~= 0 and not silent then +---@param cmd string[] +---@param silent boolean? +---@param env? table<string,string|number> +---@return string +local function system(cmd, silent, env) + local r = vim.system(cmd, { env = env, timeout = 10000 }):wait() + + if r.code ~= 0 and not silent then local cmd_str = table.concat(cmd, ' ') - man_error(string.format("command error '%s': %s", cmd_str, table.concat(stderr_data))) + man_error(string.format("command error '%s': %s", cmd_str, r.stderr)) end - return table.concat(stdout_data) + return assert(r.stdout) end +---@param line string +---@param linenr integer local function highlight_line(line, linenr) + ---@type string[] local chars = {} local prev_char = '' local overstrike, escape = false, false + + ---@type table<integer,{attr:integer,start:integer,final:integer}> local hls = {} -- Store highlight groups as { attr, start, final } + local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3 local hl_groups = { [BOLD] = 'manBold', [UNDERLINE] = 'manUnderline', [ITALIC] = 'manItalic' } local attr = NONE @@ -194,11 +145,12 @@ local function highlight_line(line, linenr) -- We only want to match against SGR sequences, which consist of ESC -- followed by '[', then a series of parameter and intermediate bytes in -- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117) + ---@type string? local sgr = prev_char:match('^%[([\032-\063]*)m$') -- Ignore escape sequences with : characters, as specified by ITU's T.416 -- Open Document Architecture and interchange format. if sgr and not string.find(sgr, ':') then - local match + local match ---@type string? while sgr and #sgr > 0 do -- Match against SGR parameters, which may be separated by ';' match, sgr = sgr:match('^(%d*);?(.*)') @@ -261,11 +213,16 @@ end -- intended for PostgreSQL, which has man pages like 'CREATE_TABLE(7)'; -- while editing SQL source code, it's nice to visually select 'CREATE TABLE' -- and hit 'K', which requires this transformation +---@param str string +---@return string local function spaces_to_underscores(str) local res = str:gsub('%s', '_') return res end +---@param sect string|nil +---@param name string|nil +---@param silent boolean local function get_path(sect, name, silent) name = name or '' sect = sect or '' @@ -287,7 +244,7 @@ local function get_path(sect, name, silent) -- -- Finally, we can avoid relying on -S or -s here since they are very -- inconsistently supported. Instead, call -w with a section and a name. - local cmd + local cmd ---@type string[] if sect == '' then cmd = { 'man', FIND_ARG, name } else @@ -295,7 +252,7 @@ local function get_path(sect, name, silent) end local lines = system(cmd, silent) - local results = vim.split(lines or {}, '\n', { trimempty = true }) + local results = vim.split(lines, '\n', { trimempty = true }) if #results == 0 then return @@ -310,12 +267,15 @@ local function get_path(sect, name, silent) end -- find any that match the specified name + ---@param v string local namematches = vim.tbl_filter(function(v) - return fn.fnamemodify(v, ':t'):match(name) + local tail = fn.fnamemodify(v, ':t') + return string.find(tail, name, 1, true) end, results) or {} local sectmatches = {} if #namematches > 0 and sect ~= '' then + ---@param v string sectmatches = vim.tbl_filter(function(v) return fn.fnamemodify(v, ':e') == sect end, namematches) @@ -324,9 +284,12 @@ local function get_path(sect, name, silent) return fn.substitute(sectmatches[1] or namematches[1] or results[1], [[\n\+$]], '', '') end +---@param text string +---@param pat_or_re string local function matchstr(text, pat_or_re) local re = type(pat_or_re) == 'string' and vim.regex(pat_or_re) or pat_or_re |