editor.lua (41201B)
1 -- Nvim-Lua stdlib: the `vim` module (:help lua-stdlib) 2 -- 3 4 -- These are for loading runtime modules lazily since they aren't available in 5 -- the nvim binary as specified in executor.c 6 for k, v in pairs({ 7 treesitter = true, 8 filetype = true, 9 loader = true, 10 func = true, 11 F = true, 12 lsp = true, 13 hl = true, 14 diagnostic = true, 15 keymap = true, 16 ui = true, 17 health = true, 18 secure = true, 19 snippet = true, 20 pack = true, 21 _watch = true, 22 net = true, 23 pos = true, 24 range = true, 25 }) do 26 vim._submodules[k] = v 27 end 28 29 -- There are things which have special rules in vim._init_packages 30 -- for legacy reasons (uri) or for performance (_inspector). 31 -- most new things should go into a submodule namespace ( vim.foobar.do_thing() ) 32 vim._extra = { 33 uri_from_fname = true, 34 uri_from_bufnr = true, 35 uri_to_fname = true, 36 uri_to_bufnr = true, 37 show_pos = true, 38 inspect_pos = true, 39 } 40 41 --- @nodoc 42 vim.log = { 43 --- @enum vim.log.levels 44 levels = { 45 TRACE = 0, 46 DEBUG = 1, 47 INFO = 2, 48 WARN = 3, 49 ERROR = 4, 50 OFF = 5, 51 }, 52 } 53 54 local utfs = { 55 ['utf-8'] = true, 56 ['utf-16'] = true, 57 ['utf-32'] = true, 58 } 59 60 -- Gets process info from the `ps` command. 61 -- Used by nvim_get_proc() as a fallback. 62 function vim._os_proc_info(pid) 63 if pid == nil or pid <= 0 or type(pid) ~= 'number' then 64 error('invalid pid') 65 end 66 local cmd = { 'ps', '-p', pid, '-o', 'comm=' } 67 local r = vim.system(cmd):wait() 68 local name = assert(r.stdout) 69 if r.code == 1 and vim.trim(name) == '' then 70 return {} -- Process not found. 71 elseif r.code ~= 0 then 72 error('command failed: ' .. vim.fn.string(cmd)) 73 end 74 local ppid_string = assert(vim.system({ 'ps', '-p', pid, '-o', 'ppid=' }):wait().stdout) 75 -- Remove trailing whitespace. 76 name = vim.trim(name):gsub('^.*/', '') 77 local ppid = tonumber(ppid_string) or -1 78 return { 79 name = name, 80 pid = pid, 81 ppid = ppid, 82 } 83 end 84 85 -- Gets process children from the `pgrep` command. 86 -- Used by nvim_get_proc_children() as a fallback. 87 function vim._os_proc_children(ppid) 88 if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then 89 error('invalid ppid') 90 end 91 local cmd = { 'pgrep', '-P', ppid } 92 local r = vim.system(cmd):wait() 93 if r.code == 1 and vim.trim(r.stdout) == '' then 94 return {} -- Process not found. 95 elseif r.code ~= 0 then 96 error('command failed: ' .. vim.fn.string(cmd)) 97 end 98 local children = {} 99 for s in r.stdout:gmatch('%S+') do 100 local i = tonumber(s) 101 if i ~= nil then 102 table.insert(children, i) 103 end 104 end 105 return children 106 end 107 108 --- @nodoc 109 --- @class vim.inspect.Opts 110 --- @field depth? integer 111 --- @field newline? string 112 --- @field process? fun(item:any, path: string[]): any 113 114 --- Gets a human-readable representation of the given object. 115 --- 116 ---@see |vim.print()| 117 ---@see https://github.com/kikito/inspect.lua 118 ---@see https://github.com/mpeterv/vinspect 119 ---@return string 120 ---@overload fun(x: any, opts?: vim.inspect.Opts): string 121 vim.inspect = vim.inspect 122 123 do 124 local startpos, tdots, tick, got_line1, undo_started, trailing_nl = nil, 0, 0, false, false, false 125 126 --- Paste handler, invoked by |nvim_paste()|. 127 --- 128 --- Note: This is provided only as a "hook", don't call it directly; call |nvim_paste()| instead, 129 --- which arranges redo (dot-repeat) and invokes `vim.paste`. 130 --- 131 --- Example: To remove ANSI color codes when pasting: 132 --- 133 --- ```lua 134 --- vim.paste = (function(overridden) 135 --- return function(lines, phase) 136 --- for i,line in ipairs(lines) do 137 --- -- Scrub ANSI color codes from paste input. 138 --- lines[i] = line:gsub('\27%[[0-9;mK]+', '') 139 --- end 140 --- return overridden(lines, phase) 141 --- end 142 --- end)(vim.paste) 143 --- ``` 144 --- 145 ---@see |paste| 146 --- 147 ---@param lines string[] # |readfile()|-style list of lines to paste. |channel-lines| 148 ---@param phase (-1|1|2|3) -1: "non-streaming" paste: the call contains all lines. 149 --- If paste is "streamed", `phase` indicates the stream state: 150 --- - 1: starts the paste (exactly once) 151 --- - 2: continues the paste (zero or more times) 152 --- - 3: ends the paste (exactly once) 153 ---@return boolean result false if client should cancel the paste. 154 function vim.paste(lines, phase) 155 local now = vim.uv.now() 156 local is_first_chunk = phase < 2 157 local is_last_chunk = phase == -1 or phase == 3 158 if is_first_chunk then -- Reset flags. 159 tdots, tick, got_line1, undo_started, trailing_nl = now, 0, false, false, false 160 end 161 if #lines == 0 then 162 lines = { '' } 163 end 164 if #lines == 1 and lines[1] == '' and not is_last_chunk then 165 -- An empty chunk can cause some edge cases in streamed pasting, 166 -- so don't do anything unless it is the last chunk. 167 return true 168 end 169 -- Note: mode doesn't always start with "c" in cmdline mode, so use getcmdtype() instead. 170 if vim.fn.getcmdtype() ~= '' then -- cmdline-mode: paste only 1 line. 171 if not got_line1 then 172 got_line1 = (#lines > 1) 173 -- Escape control characters 174 local line1 = lines[1]:gsub('(%c)', '\022%1') 175 -- nvim_input() is affected by mappings, 176 -- so use nvim_feedkeys() with "n" flag to ignore mappings. 177 -- "t" flag is also needed so the pasted text is saved in cmdline history. 178 vim.api.nvim_feedkeys(line1, 'nt', true) 179 end 180 return true 181 end 182 local mode = vim.api.nvim_get_mode().mode 183 if undo_started then 184 vim.api.nvim_command('undojoin') 185 end 186 if mode:find('^i') or mode:find('^n?t') then -- Insert mode or Terminal buffer 187 vim.api.nvim_put(lines, 'c', false, true) 188 elseif phase < 2 and mode:find('^R') and not mode:find('^Rv') then -- Replace mode 189 -- TODO: implement Replace mode streamed pasting 190 -- TODO: support Virtual Replace mode 191 local nchars = 0 192 for _, line in ipairs(lines) do 193 nchars = nchars + line:len() 194 end 195 --- @type integer, integer 196 local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 197 local bufline = vim.api.nvim_buf_get_lines(0, row - 1, row, true)[1] 198 local firstline = lines[1] 199 firstline = bufline:sub(1, col) .. firstline 200 lines[1] = firstline 201 lines[#lines] = lines[#lines] .. bufline:sub(col + nchars + 1, bufline:len()) 202 vim.api.nvim_buf_set_lines(0, row - 1, row, false, lines) 203 elseif mode:find('^[nvV\22sS\19]') then -- Normal or Visual or Select mode 204 if mode:find('^n') then -- Normal mode 205 -- When there was a trailing new line in the previous chunk, 206 -- the cursor is on the first character of the next line, 207 -- so paste before the cursor instead of after it. 208 vim.api.nvim_put(lines, 'c', not trailing_nl, false) 209 else -- Visual or Select mode 210 vim.api.nvim_command([[exe "silent normal! \<Del>"]]) 211 local del_start = vim.fn.getpos("'[") 212 local cursor_pos = vim.fn.getpos('.') 213 if mode:find('^[VS]') then -- linewise 214 if cursor_pos[2] < del_start[2] then -- replacing lines at eof 215 -- create a new line 216 vim.api.nvim_put({ '' }, 'l', true, true) 217 end 218 vim.api.nvim_put(lines, 'c', false, false) 219 else 220 -- paste after cursor when replacing text at eol, otherwise paste before cursor 221 vim.api.nvim_put(lines, 'c', cursor_pos[3] < del_start[3], false) 222 end 223 end 224 -- put cursor at the end of the text instead of one character after it 225 vim.fn.setpos('.', vim.fn.getpos("']")) 226 trailing_nl = lines[#lines] == '' 227 else -- Don't know what to do in other modes 228 return false 229 end 230 undo_started = true 231 if not is_last_chunk and (now - tdots >= 100) then 232 local dots = ('.'):rep(tick % 4) 233 tdots = now 234 tick = tick + 1 235 -- Use :echo because Lua print('') is a no-op, and we want to clear the 236 -- message when there are zero dots. 237 vim.api.nvim_command(('echo "%s"'):format(dots)) 238 end 239 if startpos == nil then 240 startpos = vim.fn.getpos("'[") 241 else 242 vim.fn.setpos("'[", startpos) 243 end 244 if is_last_chunk then 245 startpos = nil 246 vim.api.nvim_command('redraw' .. (tick > 1 and '|echo ""' or '')) 247 end 248 return true -- Paste will not continue if not returning `true`. 249 end 250 end 251 252 --- Returns a function which calls {fn} via |vim.schedule()|. 253 --- 254 --- The returned function passes all arguments to {fn}. 255 --- 256 --- Example: 257 --- 258 --- ```lua 259 --- function notify_readable(_err, readable) 260 --- vim.notify("readable? " .. tostring(readable)) 261 --- end 262 --- vim.uv.fs_access(vim.fn.stdpath("config"), "R", vim.schedule_wrap(notify_readable)) 263 --- ``` 264 --- 265 ---@see |lua-loop-callbacks| 266 ---@see |vim.schedule()| 267 ---@see |vim.in_fast_event()| 268 ---@param fn function 269 ---@return function 270 function vim.schedule_wrap(fn) 271 return function(...) 272 local args = vim.F.pack_len(...) 273 vim.schedule(function() 274 fn(vim.F.unpack_len(args)) 275 end) 276 end 277 end 278 279 -- vim.fn.{func}(...) 280 ---@nodoc 281 vim.fn = setmetatable({}, { 282 --- @param t table<string,function> 283 --- @param key string 284 --- @return function 285 __index = function(t, key) 286 local _fn --- @type function 287 if vim.api[key] ~= nil then 288 _fn = function() 289 error(string.format('Tried to call API function with vim.fn: use vim.api.%s instead', key)) 290 end 291 else 292 _fn = function(...) 293 return vim.call(key, ...) 294 end 295 end 296 t[key] = _fn 297 return _fn 298 end, 299 }) 300 301 --- @private 302 vim.funcref = function(viml_func_name) 303 return vim.fn[viml_func_name] 304 end 305 306 local VIM_CMD_ARG_MAX = 20 307 308 --- Executes Vimscript (|Ex-commands|). 309 --- 310 --- Can be indexed with a command name to get a function, thus you can write `vim.cmd.echo(…)` 311 --- instead of `vim.cmd{cmd='echo',…}`. 312 --- 313 --- Examples: 314 --- 315 --- ```lua 316 --- -- Single command: 317 --- vim.cmd('echo 42') 318 --- -- Multiline script: 319 --- vim.cmd([[ 320 --- augroup my.group 321 --- autocmd! 322 --- autocmd FileType c setlocal cindent 323 --- augroup END 324 --- ]]) 325 --- 326 --- -- Ex command :echo "foo". Note: string literals must be double-quoted. 327 --- vim.cmd('echo "foo"') 328 --- vim.cmd { cmd = 'echo', args = { '"foo"' } } 329 --- vim.cmd.echo({ args = { '"foo"' } }) 330 --- vim.cmd.echo('"foo"') 331 --- 332 --- -- Ex command :write! myfile.txt 333 --- vim.cmd('write! myfile.txt') 334 --- vim.cmd { cmd = 'write', args = { 'myfile.txt' }, bang = true } 335 --- vim.cmd.write { args = { 'myfile.txt' }, bang = true } 336 --- vim.cmd.write { 'myfile.txt', bang = true } 337 --- 338 --- -- Ex command :vertical resize +2 339 --- vim.cmd.resize({ '+2', mods = { vertical = true } }) 340 --- ``` 341 --- 342 ---@diagnostic disable-next-line: undefined-doc-param 343 ---@param command string|table Command(s) to execute. 344 --- - The string form supports multiline Vimscript (alias to |nvim_exec2()|, behaves 345 --- like |:source|). 346 --- - The table form executes a single command (alias to |nvim_cmd()|). 347 ---@see |ex-cmd-index| 348 vim.cmd = setmetatable({}, { 349 __call = function(_, command) 350 if type(command) == 'table' then 351 return vim.api.nvim_cmd(command, {}) 352 else 353 vim.api.nvim_exec2(command, {}) 354 return '' 355 end 356 end, 357 --- @param t table<string,function> 358 __index = function(t, command) 359 t[command] = function(...) 360 local opts --- @type vim.api.keyset.cmd 361 if select('#', ...) == 1 and type(select(1, ...)) == 'table' then 362 --- @type vim.api.keyset.cmd 363 opts = select(1, ...) 364 365 -- Move indexed positions in opts to opt.args 366 if opts[1] and not opts.args then 367 opts.args = {} 368 for i = 1, VIM_CMD_ARG_MAX do 369 if not opts[i] then 370 break 371 end 372 opts.args[i] = opts[i] 373 --- @diagnostic disable-next-line: no-unknown 374 opts[i] = nil 375 end 376 end 377 else 378 opts = { args = { ... } } 379 end 380 opts.cmd = command 381 return vim.api.nvim_cmd(opts, {}) 382 end 383 return t[command] 384 end, 385 }) 386 387 --- @class (private) vim.var_accessor 388 --- @field [string] any 389 --- @field [integer] vim.var_accessor 390 391 --- @class (private) vim.g: { [string]: any } 392 --- @class (private) vim.b: vim.var_accessor 393 --- @class (private) vim.w: vim.var_accessor 394 --- @class (private) vim.t: vim.var_accessor 395 396 -- These are the vim.env/v/g/o/bo/wo variable magic accessors. 397 do 398 --- @param scope string 399 --- @param handle? false|integer 400 --- @return vim.var_accessor 401 local function make_dict_accessor(scope, handle) 402 vim.validate('scope', scope, 'string') 403 local mt = {} 404 function mt:__newindex(k, v) 405 return vim._setvar(scope, handle or 0, k, v) 406 end 407 function mt:__index(k) 408 if handle == nil and type(k) == 'number' then 409 return make_dict_accessor(scope, k) 410 end 411 return vim._getvar(scope, handle or 0, k) 412 end 413 return setmetatable({}, mt) 414 end 415 416 vim.g = make_dict_accessor('g', false) --[[@as vim.g]] 417 vim.v = make_dict_accessor('v', false) --[[@as vim.v]] 418 vim.b = make_dict_accessor('b') --[[@as vim.b]] 419 vim.w = make_dict_accessor('w') --[[@as vim.w]] 420 vim.t = make_dict_accessor('t') --[[@as vim.t]] 421 end 422 423 --- @deprecated 424 --- Gets a dict of line segment ("chunk") positions for the region from `pos1` to `pos2`. 425 --- 426 --- Input and output positions are byte positions, (0,0)-indexed. "End of line" column 427 --- position (for example, |linewise| visual selection) is returned as |v:maxcol| (big number). 428 --- 429 ---@param bufnr integer Buffer number, or 0 for current buffer 430 ---@param pos1 integer[]|string Start of region as a (line, column) tuple or |getpos()|-compatible string 431 ---@param pos2 integer[]|string End of region as a (line, column) tuple or |getpos()|-compatible string 432 ---@param regtype string [setreg()]-style selection type 433 ---@param inclusive boolean Controls whether the ending column is inclusive (see also 'selection'). 434 ---@return table region Dict of the form `{linenr = {startcol,endcol}}`. `endcol` is exclusive, and 435 ---whole lines are returned as `{startcol,endcol} = {0,-1}`. 436 function vim.region(bufnr, pos1, pos2, regtype, inclusive) 437 vim.deprecate('vim.region', 'vim.fn.getregionpos()', '0.13') 438 439 if not vim.api.nvim_buf_is_loaded(bufnr) then 440 vim.fn.bufload(bufnr) 441 end 442 443 if type(pos1) == 'string' then 444 local pos = vim.fn.getpos(pos1) 445 pos1 = { pos[2] - 1, pos[3] - 1 } 446 end 447 if type(pos2) == 'string' then 448 local pos = vim.fn.getpos(pos2) 449 pos2 = { pos[2] - 1, pos[3] - 1 } 450 end 451 452 if pos1[1] > pos2[1] or (pos1[1] == pos2[1] and pos1[2] > pos2[2]) then 453 pos1, pos2 = pos2, pos1 --- @type [integer, integer], [integer, integer] 454 end 455 456 -- getpos() may return {0,0,0,0} 457 if pos1[1] < 0 or pos1[2] < 0 then 458 return {} 459 end 460 461 -- check that region falls within current buffer 462 local buf_line_count = vim.api.nvim_buf_line_count(bufnr) 463 pos1[1] = math.min(pos1[1], buf_line_count - 1) 464 pos2[1] = math.min(pos2[1], buf_line_count - 1) 465 466 -- in case of block selection, columns need to be adjusted for non-ASCII characters 467 -- TODO: handle double-width characters 468 if regtype:byte() == 22 then 469 local bufline = vim.api.nvim_buf_get_lines(bufnr, pos1[1], pos1[1] + 1, true)[1] 470 pos1[2] = vim.str_utfindex(bufline, 'utf-32', pos1[2]) 471 end 472 473 local region = {} 474 for l = pos1[1], pos2[1] do 475 local c1 --- @type number 476 local c2 --- @type number 477 if regtype:byte() == 22 then -- block selection: take width from regtype 478 c1 = pos1[2] 479 c2 = c1 + tonumber(regtype:sub(2)) 480 -- and adjust for non-ASCII characters 481 local bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1] 482 local utflen = vim.str_utfindex(bufline, 'utf-32', #bufline) 483 if c1 <= utflen then 484 c1 = assert(tonumber(vim.str_byteindex(bufline, 'utf-32', c1))) 485 else 486 c1 = #bufline + 1 487 end 488 if c2 <= utflen then 489 c2 = assert(tonumber(vim.str_byteindex(bufline, 'utf-32', c2))) 490 else 491 c2 = #bufline + 1 492 end 493 elseif regtype == 'V' then -- linewise selection, always return whole line 494 c1 = 0 495 c2 = -1 496 else 497 c1 = (l == pos1[1]) and pos1[2] or 0 498 if inclusive and l == pos2[1] then 499 local bufline = vim.api.nvim_buf_get_lines(bufnr, pos2[1], pos2[1] + 1, true)[1] 500 pos2[2] = vim.fn.byteidx(bufline, vim.fn.charidx(bufline, pos2[2]) + 1) 501 end 502 c2 = (l == pos2[1]) and pos2[2] or -1 503 end 504 table.insert(region, l, { c1, c2 }) 505 end 506 return region 507 end 508 509 --- Defers calling {fn} until {timeout} ms passes. 510 --- 511 --- Use to do a one-shot timer that calls {fn} 512 --- Note: The {fn} is |vim.schedule()|d automatically, so API functions are 513 --- safe to call. 514 ---@param fn function Callback to call once `timeout` expires 515 ---@param timeout integer Number of milliseconds to wait before calling `fn` 516 ---@return uv.uv_timer_t timer luv timer object 517 function vim.defer_fn(fn, timeout) 518 vim.validate('fn', fn, 'callable', true) 519 520 local timer = assert(vim.uv.new_timer()) 521 timer:start(timeout, 0, function() 522 local _, err = vim.schedule(function() 523 if not timer:is_closing() then 524 timer:close() 525 end 526 527 fn() 528 end) 529 530 if err then 531 timer:close() 532 end 533 end) 534 535 return timer 536 end 537 538 --- Displays a notification to the user. 539 --- 540 --- This function can be overridden by plugins to display notifications using 541 --- a custom provider (such as the system notification provider). By default, 542 --- writes to |:messages|. 543 ---@param msg string Content of the notification to show to the user. 544 ---@param level integer|nil One of the values from |vim.log.levels|. 545 ---@param opts table|nil Optional parameters. Unused by default. 546 ---@diagnostic disable-next-line: unused-local 547 function vim.notify(msg, level, opts) -- luacheck: no unused args 548 local chunks = { { msg, level == vim.log.levels.WARN and 'WarningMsg' or nil } } 549 vim.api.nvim_echo(chunks, true, { err = level == vim.log.levels.ERROR }) 550 end 551 552 do 553 local notified = {} --- @type table<string,true> 554 555 --- Displays a notification only one time. 556 --- 557 --- Like |vim.notify()|, but subsequent calls with the same message will not 558 --- display a notification. 559 --- 560 ---@param msg string Content of the notification to show to the user. 561 ---@param level integer|nil One of the values from |vim.log.levels|. 562 ---@param opts table|nil Optional parameters. Unused by default. 563 ---@return boolean true if message was displayed, else false 564 function vim.notify_once(msg, level, opts) 565 if not notified[msg] then 566 vim.notify(msg, level, opts) 567 notified[msg] = true 568 return true 569 end 570 return false 571 end 572 end 573 574 local on_key_cbs = {} --- @type table<integer,[function, table]> 575 576 --- Adds Lua function {fn} with namespace id {ns_id} as a listener to every, 577 --- yes every, input key. 578 --- 579 --- The Nvim command-line option |-w| is related but does not support callbacks 580 --- and cannot be toggled dynamically. 581 --- 582 ---@note {fn} will be removed on error. 583 ---@note {fn} won't be invoked recursively, i.e. if {fn} itself consumes input, 584 --- it won't be invoked for those keys. 585 ---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| 586 --- 587 ---@param fn nil|fun(key: string, typed: string): string? Function invoked for every input key, 588 --- after mappings have been applied but before further processing. Arguments 589 --- {key} and {typed} are raw keycodes, where {key} is the key after mappings 590 --- are applied, and {typed} is the key(s) before mappings are applied. 591 --- {typed} may be empty if {key} is produced by non-typed key(s) or by the 592 --- same typed key(s) that produced a previous {key}. 593 --- If {fn} returns an empty string, {key} is discarded/ignored, and if {key} 594 --- is [<Cmd>] then the "[<Cmd>]…[<CR>]" sequence is discarded as a whole. 595 --- When {fn} is `nil`, the callback associated with namespace {ns_id} is removed. 596 ---@param ns_id integer? Namespace ID. If nil or 0, generates and returns a 597 --- new |nvim_create_namespace()| id. 598 ---@param opts table? Optional parameters 599 --- 600 ---@see |keytrans()| 601 --- 602 ---@return integer Namespace id associated with {fn}. Or count of all callbacks 603 ---if on_key() is called without arguments. 604 function vim.on_key(fn, ns_id, opts) 605 if fn == nil and ns_id == nil then 606 return vim.tbl_count(on_key_cbs) 607 end 608 609 vim.validate('fn', fn, 'callable', true) 610 vim.validate('ns_id', ns_id, 'number', true) 611 vim.validate('opts', opts, 'table', true) 612 opts = opts or {} 613 614 if ns_id == nil or ns_id == 0 then 615 ns_id = vim.api.nvim_create_namespace('') 616 end 617 618 on_key_cbs[ns_id] = fn and { fn, opts } 619 return ns_id 620 end 621 622 --- Executes the on_key callbacks. 623 ---@private 624 function vim._on_key(buf, typed_buf) 625 local failed = {} ---@type [integer, string][] 626 local discard = false 627 for k, v in pairs(on_key_cbs) do 628 local fn = v[1] 629 --- @type boolean, any 630 local ok, rv = xpcall(function() 631 return fn(buf, typed_buf) 632 end, debug.traceback) 633 if ok and rv ~= nil then 634 if type(rv) == 'string' and #rv == 0 then 635 discard = true 636 -- break -- Without break deliver to all callbacks even when it eventually discards. 637 -- "break" does not make sense unless callbacks are sorted by ???. 638 else 639 ok = false 640 rv = 'return string must be empty' 641 end 642 end 643 if not ok then 644 vim.on_key(nil, k) 645 table.insert(failed, { k, rv }) 646 end 647 end 648 649 if #failed > 0 then 650 local errmsg = '' 651 for _, v in ipairs(failed) do 652 errmsg = errmsg .. string.format('\nWith ns_id %d: %s', v[1], v[2]) 653 end 654 error(errmsg) 655 end 656 return discard 657 end 658 659 --- Convert UTF-32, UTF-16 or UTF-8 {index} to byte index. 660 --- If {strict_indexing} is false 661 --- then an out of range index will return byte length 662 --- instead of throwing an error. 663 --- 664 --- Invalid UTF-8 and NUL is treated like in |vim.str_utfindex()|. 665 --- An {index} in the middle of a UTF-16 sequence is rounded upwards to 666 --- the end of that sequence. 667 ---@param s string 668 ---@param encoding "utf-8"|"utf-16"|"utf-32" 669 ---@param index integer 670 ---@param strict_indexing? boolean # default: true 671 ---@return integer 672 function vim.str_byteindex(s, encoding, index, strict_indexing) 673 if type(encoding) == 'number' then 674 -- Legacy support for old API 675 -- Parameters: ~ 676 -- • {str} (`string`) 677 -- • {index} (`integer`) 678 -- • {use_utf16} (`boolean?`) 679 vim.deprecate( 680 'vim.str_byteindex', 681 'vim.str_byteindex(s, encoding, index, strict_indexing)', 682 '1.0' 683 ) 684 local old_index = encoding 685 local use_utf16 = index or false 686 return vim._str_byteindex(s, old_index, use_utf16) or error('index out of range') 687 end 688 689 -- Avoid vim.validate for performance. 690 if type(s) ~= 'string' or type(index) ~= 'number' then 691 vim.validate('s', s, 'string') 692 vim.validate('index', index, 'number') 693 end 694 695 local len = #s 696 697 if index == 0 or len == 0 then 698 return 0 699 end 700 701 if not utfs[encoding] then 702 vim.validate('encoding', encoding, function(v) 703 return utfs[v], 'invalid encoding' 704 end) 705 end 706 707 if strict_indexing ~= nil and type(strict_indexing) ~= 'boolean' then 708 vim.validate('strict_indexing', strict_indexing, 'boolean', true) 709 end 710 if strict_indexing == nil then 711 strict_indexing = true 712 end 713 714 if encoding == 'utf-8' then 715 if index > len then 716 return strict_indexing and error('index out of range') or len 717 end 718 return index 719 end 720 return vim._str_byteindex(s, index, encoding == 'utf-16') 721 or strict_indexing and error('index out of range') 722 or len 723 end 724 725 --- Convert byte index to UTF-32, UTF-16 or UTF-8 indices. If {index} is not 726 --- supplied, the length of the string is used. All indices are zero-based. 727 --- 728 --- If {strict_indexing} is false then an out of range index will return string 729 --- length instead of throwing an error. 730 --- Invalid UTF-8 bytes, and embedded surrogates are counted as one code point 731 --- each. An {index} in the middle of a UTF-8 sequence is rounded upwards to the end of 732 --- that sequence. 733 ---@param s string 734 ---@param encoding "utf-8"|"utf-16"|"utf-32" 735 ---@param index? integer 736 ---@param strict_indexing? boolean # default: true 737 ---@return integer 738 function vim.str_utfindex(s, encoding, index, strict_indexing) 739 if encoding == nil or type(encoding) == 'number' then 740 -- Legacy support for old API 741 -- Parameters: ~ 742 -- • {str} (`string`) 743 -- • {index} (`integer?`) 744 vim.deprecate( 745 'vim.str_utfindex', 746 'vim.str_utfindex(s, encoding, index, strict_indexing)', 747 '1.0' 748 ) 749 local old_index = encoding 750 local col32, col16 = vim._str_utfindex(s, old_index) --[[@as integer,integer]] 751 if not col32 or not col16 then 752 error('index out of range') 753 end 754 -- Return (multiple): ~ 755 -- (`integer`) UTF-32 index 756 -- (`integer`) UTF-16 index 757 --- @diagnostic disable-next-line: redundant-return-value 758 return col32, col16 759 end 760 761 if type(s) ~= 'string' or (index ~= nil and type(index) ~= 'number') then 762 vim.validate('s', s, 'string') 763 vim.validate('index', index, 'number', true) 764 end 765 766 if not index then 767 index = math.huge 768 strict_indexing = false 769 end 770 771 if index == 0 then 772 return 0 773 end 774 775 if not utfs[encoding] then 776 vim.validate('encoding', encoding, function(v) 777 return utfs[v], 'invalid encoding' 778 end) 779 end 780 781 if strict_indexing ~= nil and type(strict_indexing) ~= 'boolean' then 782 vim.validate('strict_indexing', strict_indexing, 'boolean', true) 783 end 784 if strict_indexing == nil then 785 strict_indexing = true 786 end 787 788 if encoding == 'utf-8' then 789 local len = #s 790 return index <= len and index or (strict_indexing and error('index out of range') or len) 791 end 792 local col32, col16 = vim._str_utfindex(s, index) --[[@as integer?,integer?]] 793 local col = encoding == 'utf-16' and col16 or col32 794 if col then 795 return col 796 end 797 if strict_indexing then 798 error('index out of range') 799 end 800 local max32, max16 = vim._str_utfindex(s)--[[@as integer integer]] 801 return encoding == 'utf-16' and max16 or max32 802 end 803 804 --- Generates a list of possible completions for the str 805 --- String has the pattern. 806 --- 807 --- 1. Can we get it to just return things in the global namespace with that name prefix 808 --- 2. Can we get it to return things from global namespace even with `print(` in front. 809 --- 810 --- @param pat string 811 --- @return any[], integer 812 function vim._expand_pat(pat, env) 813 env = env or _G 814 815 if pat == '' then 816 local result = vim.tbl_keys(env) 817 table.sort(result) 818 return result, 0 819 end 820 821 -- TODO: We can handle spaces in [] ONLY. 822 -- We should probably do that at some point, just for cooler completion. 823 -- TODO: We can suggest the variable names to go in [] 824 -- This would be difficult as well. 825 -- Probably just need to do a smarter match than just `:match` 826 827 -- Get the last part of the pattern 828 local last_part = pat:match('[%w.:_%[%]\'"]+$') 829 if not last_part then 830 return {}, 0 831 end 832 833 local parts, search_index = vim._expand_pat_get_parts(last_part) 834 835 local match_part = string.sub(last_part, search_index, #last_part) 836 local prefix_match_pat = string.sub(pat, 1, #pat - #match_part) or '' 837 local last_char = string.sub(last_part, #last_part) 838 839 local final_env = env 840 841 --- Allows submodules to be defined on a `vim.<module>` table without eager-loading the module. 842 --- 843 --- Cmdline completion (`:lua vim.lsp.c<tab>`) accesses `vim.lsp._submodules` when no other candidates. 844 --- Cmdline completion (`:lua vim.lsp.completion.g<tab>`) will eager-load the module anyway. #33007 845 --- 846 --- @param m table 847 --- @param k string 848 --- @return any 849 local function safe_tbl_get(m, k) 850 local val = rawget(m, k) 851 if val ~= nil then 852 return val 853 end 854 855 local mt = getmetatable(m) 856 if not mt then 857 return m == vim and vim._extra[k] or nil 858 end 859 860 -- use mt.__index, _submodules as fallback 861 if type(mt.__index) == 'table' then 862 return rawget(mt.__index, k) 863 end 864 865 local sub = rawget(m, '_submodules') 866 if sub and type(sub) == 'table' and rawget(sub, k) then 867 -- Access the module to force _defer_require() to load the module. 868 return m[k] 869 end 870 end 871 872 for _, part in ipairs(parts) do 873 if type(final_env) ~= 'table' then 874 return {}, 0 875 end 876 local key --- @type any 877 878 -- Normally, we just have a string 879 -- Just attempt to get the string directly from the environment 880 if type(part) == 'string' then 881 key = part 882 else 883 -- However, sometimes you want to use a variable, and complete on it 884 -- With this, you have the power. 885 886 -- MY_VAR = "api" 887 -- vim[MY_VAR] 888 -- -> _G[MY_VAR] -> "api" 889 local result_key = part[1] 890 if not result_key then 891 return {}, 0 892 end 893 894 local result = rawget(env, result_key) 895 896 if result == nil then 897 return {}, 0 898 end 899 900 key = result 901 end 902 final_env = safe_tbl_get(final_env, key) 903 904 if not final_env then 905 return {}, 0 906 end 907 end 908 909 local keys = {} --- @type table<string,true> 910 911 --- @param obj table<any,any> 912 local function insert_keys(obj) 913 for k, _ in pairs(obj) do 914 if 915 type(k) == 'string' 916 and string.sub(k, 1, string.len(match_part)) == match_part 917 and k:match('^[_%w]+$') ~= nil -- filter out invalid identifiers for field, e.g. 'foo#bar' 918 and (last_char ~= '.' or string.sub(k, 1, 1) ~= '_') -- don't include private fields after '.' 919 then 920 keys[k] = true 921 end 922 end 923 end 924 ---@param acc table<string,any> 925 local function _fold_to_map(acc, k, v) 926 acc[k] = (v or true) 927 return acc 928 end 929 930 if type(final_env) == 'table' then 931 insert_keys(final_env) 932 local sub = rawget(final_env, '_submodules') 933 if type(sub) == 'table' then 934 insert_keys(sub) 935 end 936 if final_env == vim then 937 insert_keys(vim._extra) 938 end 939 end 940 941 local mt = getmetatable(final_env) 942 if mt and type(mt.__index) == 'table' then 943 insert_keys(mt.__index) 944 end 945 946 -- Completion for dict accessors (special vim variables and vim.fn) 947 if mt and vim.tbl_contains({ vim.g, vim.t, vim.w, vim.b, vim.v, vim.env, vim.fn }, final_env) then 948 local prefix, type = unpack( 949 vim.fn == final_env and { '', 'function' } 950 or vim.g == final_env and { 'g:', 'var' } 951 or vim.t == final_env and { 't:', 'var' } 952 or vim.w == final_env and { 'w:', 'var' } 953 or vim.b == final_env and { 'b:', 'var' } 954 or vim.v == final_env and { 'v:', 'var' } 955 or vim.env == final_env and { '', 'environment' } 956 or { nil, nil } 957 ) 958 assert(prefix and type, "Can't resolve final_env") 959 local vars = vim.fn.getcompletion(prefix .. match_part, type) --- @type string[] 960 insert_keys(vim 961 .iter(vars) 962 :map(function(s) ---@param s string 963 s = s:gsub('[()]+$', '') -- strip '(' and ')' for function completions 964 return s:sub(#prefix + 1) -- strip the prefix, e.g., 'g:foo' => 'foo' 965 end) 966 :fold({}, _fold_to_map)) 967 end 968 969 -- Completion for option accessors (full names only) 970 if 971 mt 972 and vim.tbl_contains( 973 { vim.o, vim.go, vim.bo, vim.wo, vim.opt, vim.opt_local, vim.opt_global }, 974 final_env 975 ) 976 then 977 --- @type fun(option_name: string, option: vim.api.keyset.get_option_info): boolean 978 local filter = function(_, _) 979 return true 980 end 981 if vim.bo == final_env then 982 filter = function(_, option) 983 return option.scope == 'buf' 984 end 985 elseif vim.wo == final_env then 986 filter = function(_, option) 987 return option.scope == 'win' 988 end 989 end 990 991 --- @type table<string, vim.api.keyset.get_option_info> 992 local options = vim.api.nvim_get_all_options_info() 993 insert_keys(vim.iter(options):filter(filter):fold({}, _fold_to_map)) 994 end 995 996 keys = vim.tbl_keys(keys) 997 table.sort(keys) 998 999 return keys, #prefix_match_pat 1000 end 1001 1002 --- @param lua_string string 1003 --- @return (string|string[])[], integer 1004 vim._expand_pat_get_parts = function(lua_string) 1005 local parts = {} 1006 1007 local accumulator, search_index = '', 1 1008 local in_brackets = false 1009 local bracket_end = -1 --- @type integer? 1010 local string_char = nil 1011 for idx = 1, #lua_string do 1012 local s = lua_string:sub(idx, idx) 1013 1014 if not in_brackets and (s == '.' or s == ':') then 1015 table.insert(parts, accumulator) 1016 accumulator = '' 1017 1018 search_index = idx + 1 1019 elseif s == '[' then 1020 in_brackets = true 1021 1022 table.insert(parts, accumulator) 1023 accumulator = '' 1024 1025 search_index = idx + 1 1026 elseif in_brackets then 1027 if idx == bracket_end then 1028 in_brackets = false 1029 search_index = idx + 1 1030 1031 if string_char == 'VAR' then 1032 table.insert(parts, { accumulator }) 1033 accumulator = '' 1034 1035 string_char = nil 1036 end 1037 elseif not string_char then 1038 bracket_end = string.find(lua_string, ']', idx, true) 1039 1040 if s == '"' or s == "'" then 1041 string_char = s 1042 elseif s ~= ' ' then 1043 string_char = 'VAR' 1044 accumulator = s 1045 end 1046 elseif string_char then 1047 if string_char ~= s then 1048 accumulator = accumulator .. s 1049 else 1050 table.insert(parts, accumulator) 1051 accumulator = '' 1052 1053 string_char = nil 1054 end 1055 end 1056 else 1057 accumulator = accumulator .. s 1058 end 1059 end 1060 1061 --- @param val any[] 1062 parts = vim.tbl_filter(function(val) 1063 return #val > 0 1064 end, parts) 1065 1066 return parts, search_index 1067 end 1068 1069 do 1070 -- Ideally we should just call complete() inside omnifunc, though there are 1071 -- some bugs, so fake the two-step dance for now. 1072 local matches --- @type any[] 1073 1074 --- Omnifunc for completing Lua values from the runtime Lua interpreter, 1075 --- similar to the builtin completion for the `:lua` command. 1076 --- 1077 --- Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a Lua buffer. 1078 --- @param find_start 1|0 1079 function vim.lua_omnifunc(find_start, _) 1080 if find_start == 1 then 1081 local line = vim.api.nvim_get_current_line() 1082 local prefix = string.sub(line, 1, vim.api.nvim_win_get_cursor(0)[2]) 1083 local pos 1084 matches, pos = vim._expand_pat(prefix) 1085 return (#matches > 0 and pos) or -1 1086 else 1087 return matches 1088 end 1089 end 1090 end 1091 1092 --- @param inspect_strings boolean use vim.inspect() for strings 1093 function vim._print(inspect_strings, ...) 1094 local msg = {} 1095 for i = 1, select('#', ...) do 1096 local o = select(i, ...) 1097 if not inspect_strings and type(o) == 'string' then 1098 table.insert(msg, o) 1099 else 1100 table.insert(msg, vim.inspect(o, { newline = '\n', indent = ' ' })) 1101 end 1102 end 1103 print(table.concat(msg, '\n')) 1104 return ... 1105 end 1106 1107 --- "Pretty prints" the given arguments and returns them unmodified. 1108 --- 1109 --- Example: 1110 --- 1111 --- ```lua 1112 --- local hl_normal = vim.print(vim.api.nvim_get_hl(0, { name = 'Normal' })) 1113 --- ``` 1114 --- 1115 --- @see |vim.inspect()| 1116 --- @see |:=| 1117 --- @param ... any 1118 --- @return any # given arguments. 1119 function vim.print(...) 1120 return vim._print(false, ...) 1121 end 1122 1123 --- Translates keycodes. 1124 --- 1125 --- Example: 1126 --- 1127 --- ```lua 1128 --- local k = vim.keycode 1129 --- vim.g.mapleader = k'<bs>' 1130 --- ``` 1131 --- 1132 --- @param str string String to be converted. 1133 --- @return string 1134 --- @see |nvim_replace_termcodes()| 1135 function vim.keycode(str) 1136 return vim.api.nvim_replace_termcodes(str, true, true, true) 1137 end 1138 1139 --- @param server_addr string 1140 --- @param connect_error string 1141 function vim._cs_remote(rcid, server_addr, connect_error, args) 1142 --- @return string 1143 local function connection_failure_errmsg(consequence) 1144 local explanation --- @type string 1145 if server_addr == '' then 1146 explanation = 'No server specified with --server' 1147 else 1148 explanation = "Failed to connect to '" .. server_addr .. "'" 1149 if connect_error ~= '' then 1150 explanation = explanation .. ': ' .. connect_error 1151 end 1152 end 1153 return 'E247: ' .. explanation .. '. ' .. consequence 1154 end 1155 1156 local f_silent = false 1157 local f_tab = false 1158 1159 local subcmd = string.sub(args[1], 10) 1160 if subcmd == 'tab' then 1161 f_tab = true 1162 elseif subcmd == 'silent' then 1163 f_silent = true 1164 elseif 1165 subcmd == 'wait' 1166 or subcmd == 'wait-silent' 1167 or subcmd == 'tab-wait' 1168 or subcmd == 'tab-wait-silent' 1169 then 1170 return { errmsg = 'E5600: Wait commands not yet implemented in Nvim' } 1171 elseif subcmd == 'tab-silent' then 1172 f_tab = true 1173 f_silent = true 1174 elseif subcmd == 'send' then 1175 if rcid == 0 then 1176 return { errmsg = connection_failure_errmsg('Send failed.') } 1177 end 1178 vim.rpcrequest(rcid, 'nvim_input', args[2]) 1179 return { should_exit = true, tabbed = false } 1180 elseif subcmd == 'expr' then 1181 if rcid == 0 then 1182 return { errmsg = connection_failure_errmsg('Send expression failed.') } 1183 end 1184 local res = tostring(vim.rpcrequest(rcid, 'nvim_eval', args[2])) 1185 return { result = res, should_exit = true, tabbed = false } 1186 elseif subcmd ~= '' then 1187 return { errmsg = 'Unknown option argument: ' .. tostring(args[1]) } 1188 end 1189 1190 if rcid == 0 then 1191 if not f_silent then 1192 vim.notify(connection_failure_errmsg('Editing locally'), vim.log.levels.WARN) 1193 end 1194 else 1195 local command = {} 1196 if f_tab then 1197 table.insert(command, 'tab') 1198 end 1199 table.insert(command, 'drop') 1200 for i = 2, #args do 1201 table.insert(command, vim.fn.fnameescape(args[i])) 1202 end 1203 vim.fn.rpcrequest(rcid, 'nvim_command', table.concat(command, ' ')) 1204 end 1205 1206 return { 1207 should_exit = rcid ~= 0, 1208 tabbed = f_tab, 1209 } 1210 end 1211 1212 do 1213 local function truncated_echo(msg) 1214 -- Truncate message to avoid hit-enter-prompt 1215 local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace 1216 local msg_truncated = string.sub(msg, 1, max_width) 1217 vim.api.nvim_echo({ { msg_truncated, 'WarningMsg' } }, true, {}) 1218 end 1219 1220 local notified = false 1221 1222 function vim._truncated_echo_once(msg) 1223 if not notified then 1224 truncated_echo(msg) 1225 notified = true 1226 return true 1227 end 1228 return false 1229 end 1230 end 1231 1232 --- This is basically the same as debug.traceback(), except the full paths are shown. 1233 local function traceback() 1234 local level = 4 1235 local backtrace = { 'stack traceback:' } 1236 while true do 1237 local info = debug.getinfo(level, 'Sl') 1238 if not info then 1239 break 1240 end 1241 local msg = (' %s:%s'):format(info.source:gsub('^@', ''), info.currentline) 1242 table.insert(backtrace, msg) 1243 level = level + 1 1244 end 1245 return table.concat(backtrace, '\n') 1246 end 1247 1248 --- Shows a deprecation message to the user. 1249 --- 1250 ---@param name string Deprecated feature (function, API, etc.). 1251 ---@param alternative string|nil Suggested alternative feature. 1252 ---@param version string Version when the deprecated function will be removed. 1253 ---@param plugin string|nil Name of the plugin that owns the deprecated feature. 1254 --- Defaults to "Nvim". 1255 ---@param backtrace boolean|nil Prints backtrace. Defaults to true. 1256 --- 1257 ---@return string|nil # Deprecated message, or nil if no message was shown. 1258 function vim.deprecate(name, alternative, version, plugin, backtrace) 1259 plugin = plugin or 'Nvim' 1260 if plugin == 'Nvim' then 1261 require('vim.deprecated.health').add(name, version, traceback(), alternative) 1262 1263 -- Show a warning only if feature is hard-deprecated (see MAINTAIN.md). 1264 -- Example: if removal `version` is 0.12 (soft-deprecated since 0.10-dev), show warnings 1265 -- starting at 0.11, including 0.11-dev. 1266 local major, minor = version:match('(%d+)%.(%d+)') 1267 major, minor = tonumber(major), tonumber(minor) 1268 local nvim_major = 0 --- Current Nvim major version. 1269 1270 -- We can't "subtract" from a major version, so: 1271 -- * Always treat `major > nvim_major` as soft-deprecation. 1272 -- * Compare `minor - 1` if `major == nvim_major`. 1273 if major > nvim_major then 1274 return -- Always soft-deprecation (see MAINTAIN.md). 1275 end 1276 1277 local hard_deprecated_since = string.format('nvim-%d.%d', major, minor - 1) 1278 if major == nvim_major and vim.fn.has(hard_deprecated_since) == 0 then 1279 return 1280 end 1281 1282 local msg = ('%s is deprecated. Run ":checkhealth vim.deprecated" for more information'):format( 1283 name 1284 ) 1285 1286 local displayed = vim._truncated_echo_once(msg) 1287 return displayed and msg or nil 1288 else 1289 vim.validate('name', name, 'string') 1290 vim.validate('alternative', alternative, 'string', true) 1291 vim.validate('version', version, 'string', true) 1292 vim.validate('plugin', plugin, 'string', true) 1293 1294 local msg = ('%s is deprecated'):format(name) 1295 msg = alternative and ('%s, use %s instead.'):format(msg, alternative) or (msg .. '.') 1296 msg = ('%s\nFeature will be removed in %s %s'):format(msg, plugin, version) 1297 local displayed = vim.notify_once(msg, vim.log.levels.WARN) 1298 if displayed and backtrace ~= false then 1299 vim.notify(debug.traceback('', 2):sub(2), vim.log.levels.WARN) 1300 end 1301 return displayed and msg or nil 1302 end 1303 end 1304 1305 require('vim._core.options') 1306 1307 --- Remove at Nvim 1.0 1308 ---@deprecated 1309 vim.loop = vim.uv 1310 1311 -- Deprecated. Remove at Nvim 2.0 1312 vim.highlight = vim._defer_deprecated_module('vim.highlight', 'vim.hl') 1313 1314 return vim