diff options
Diffstat (limited to 'runtime')
50 files changed, 696 insertions, 381 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c8e0dcd0c5..5a6361d45f 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -654,35 +654,22 @@ nvim_del_var({name}) *nvim_del_var()* • {name} Variable name nvim_echo({chunks}, {history}, {opts}) *nvim_echo()* - Echo a message. + Prints a message given by a list of `[text, hl_group]` "chunks". + + Example: >lua + vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {}) +< Parameters: ~ - • {chunks} A list of `[text, hl_group]` arrays, each representing a - text chunk with specified highlight group name or ID. - `hl_group` element can be omitted for no highlight. + • {chunks} List of `[text, hl_group]` pairs, where each is a `text` + string highlighted by the (optional) name or ID `hl_group`. • {history} if true, add to |message-history|. • {opts} Optional parameters. - • verbose: Message is printed as a result of 'verbose' - option. If Nvim was invoked with -V3log_file, the message - will be redirected to the log_file and suppressed from - direct output. - -nvim_err_write({str}) *nvim_err_write()* - Writes a message to the Vim error buffer. Does not append "\n", the - message is buffered (won't display) until a linefeed is written. - - Parameters: ~ - • {str} Message - -nvim_err_writeln({str}) *nvim_err_writeln()* - Writes a message to the Vim error buffer. Appends "\n", so the buffer is - flushed (and displayed). - - Parameters: ~ - • {str} Message - - See also: ~ - • nvim_err_write() + • err: Treat the message like `:echoerr`. Sets `hl_group` + to |hl-ErrorMsg| by default. + • verbose: Message is controlled by the 'verbose' option. + Nvim invoked with `-V3log` will write the message to the + "log" file instead of standard output. nvim_eval_statusline({str}, {opts}) *nvim_eval_statusline()* Evaluates statusline string. @@ -775,6 +762,8 @@ nvim_get_api_info() *nvim_get_api_info()* nvim_get_chan_info({chan}) *nvim_get_chan_info()* Gets information about a channel. + See |nvim_list_uis()| for an example of how to get channel info. + Parameters: ~ • {chan} channel_id, or 0 for current channel @@ -796,7 +785,7 @@ nvim_get_chan_info({chan}) *nvim_get_chan_info()* present if a pty is used (e.g. for conpty on Windows). • "buffer" (optional) Buffer connected to |terminal| instance. • "client" (optional) Info about the peer (client on the other end of - the RPC channel), which it provided via |nvim_set_client_info()|. + the channel), as set by |nvim_set_client_info()|. nvim_get_color_by_name({name}) *nvim_get_color_by_name()* Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or @@ -1079,6 +1068,12 @@ nvim_list_tabpages() *nvim_list_tabpages()* nvim_list_uis() *nvim_list_uis()* Gets a list of dictionaries representing attached UIs. + Example: The Nvim builtin |TUI| sets its channel info as described in + |startup-tui|. In particular, it sets `client.name` to "nvim-tui". So you + can check if the TUI is running by inspecting the client name of each UI: >lua + vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name) +< + Return: ~ Array of UI dictionaries, each with these keys: • "height" Requested height of the UI @@ -1099,17 +1094,6 @@ nvim_load_context({dict}) *nvim_load_context()* Parameters: ~ • {dict} |Context| map. -nvim_notify({msg}, {log_level}, {opts}) *nvim_notify()* - Notify the user with a message - - Relays the call to vim.notify . By default forwards your message in the - echo area but can be overridden to trigger desktop notifications. - - Parameters: ~ - • {msg} Message to display to the user - • {log_level} The log level - • {opts} Reserved for future use. - nvim_open_term({buffer}, {opts}) *nvim_open_term()* Open a terminal instance in a buffer @@ -1127,7 +1111,7 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you can use Nvim as a "scrollback pager" (for terminals - like kitty): *terminal-scrollback-pager* >lua + like kitty): *ansi-colorize* *terminal-scrollback-pager* >lua vim.api.nvim_create_user_command('TermHl', function() local b = vim.api.nvim_create_buf(false, true) local chan = vim.api.nvim_open_term(b, {}) @@ -1154,13 +1138,6 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* Return: ~ Channel id, or 0 on error -nvim_out_write({str}) *nvim_out_write()* - Writes a message to the Vim output buffer. Does not append "\n", the - message is buffered (won't display) until a linefeed is written. - - Parameters: ~ - • {str} Message - nvim_paste({data}, {crlf}, {phase}) *nvim_paste()* Pastes at cursor (in any mode), and sets "redo" so dot (|.|) will repeat the input. UIs call this to implement "paste", but it's also intended for @@ -1259,25 +1236,23 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) *nvim_set_client_info()* nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes}) - Self-identifies the client. + Self-identifies the client. Sets the `client` object returned by + |nvim_get_chan_info()|. - The client/plugin/application should call this after connecting, to - provide hints about its identity and purpose, for debugging and - orchestration. + Clients should call this just after connecting, to provide hints for + debugging and orchestration. (Note: Something is better than nothing! + Fields are optional, but at least set `name`.) Can be called more than once; the caller should merge old info if appropriate. Example: library first identifies the channel, then a plugin using that library later identifies itself. - Note: ~ - • "Something is better than nothing". You don't need to include all the - fields. - Attributes: ~ |RPC| only Parameters: ~ - • {name} Short name for the connected client + • {name} Client short-name. Sets the `client.name` field of + |nvim_get_chan_info()|. • {version} Dict describing the version, with these (optional) keys: • "major" major version (defaults to 0 if not set, for no release yet) @@ -3175,11 +3150,13 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* • {config} Map defining the window configuration. Keys: • relative: Sets the window layout to "floating", placed at (row,col) coordinates relative to: - • "editor" The global editor grid + • "cursor" Cursor position in current window. + • "editor" The global editor grid. + • "laststatus" 'laststatus' if present, or last row. + • "mouse" Mouse position. + • "tabline" Tabline if present, or first row. • "win" Window given by the `win` field, or current window. - • "cursor" Cursor position in current window. - • "mouse" Mouse position • win: |window-ID| window to split, or relative window when creating a float (relative="win"). • anchor: Decides which corner of the float to place at diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index f466dde861..6e05dd24d2 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -8282,7 +8282,7 @@ search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]]) *search()* • {skip} (`string|function?`) Return: ~ - (`any`) + (`integer`) searchcount([{options}]) *searchcount()* Get or update the last search count, like what is displayed diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index ec98cc844f..4f320aeab3 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -16,8 +16,12 @@ Deprecated features DEPRECATED IN 0.11 *deprecated-0.11* API +• nvim_notify() Use |nvim_echo()| or `nvim_exec_lua("vim.notify(...)", ...)` instead. • nvim_subscribe() Plugins must maintain their own "multicast" channels list. • nvim_unsubscribe() Plugins must maintain their own "multicast" channels list. +• nvim_out_write() Use |nvim_echo()|. +• nvim_err_write() Use |nvim_echo()| with `{err=true}`. +• nvim_err_writeln() Use |nvim_echo()| with `{err=true}`. DIAGNOSTICS • *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead. diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 3437717467..7d97a18437 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -93,6 +93,10 @@ The {opts} table passed to a handler is the full set of configuration options values in the table are already resolved (i.e. if a user specifies a function for a config option, the function has already been evaluated). +If a diagnostic handler is configured with a "severity" key then the list of +diagnostics passed to that handler will be filtered using the value of that +key (see example below). + Nvim provides these handlers by default: "virtual_text", "signs", and "underline". @@ -119,6 +123,9 @@ with |vim.notify()|: >lua vim.diagnostic.config({ ["my/notify"] = { log_level = vim.log.levels.INFO + + -- This handler will only receive "error" diagnostics. + severity = vim.diagnostic.severity.ERROR, } }) < diff --git a/runtime/doc/health.txt b/runtime/doc/health.txt index bca145bd8e..3d37b88321 100644 --- a/runtime/doc/health.txt +++ b/runtime/doc/health.txt @@ -21,18 +21,7 @@ To run all healthchecks, use: >vim < Plugin authors are encouraged to write new healthchecks. |health-dev| - *g:health* -g:health This global variable controls the behavior and appearance of the - `health` floating window. It should be a dictionary containing the - following optional keys: - - `style`: string? Determines the display style of the `health` window. - Set to `'float'` to enable a floating window. Other - styles are not currently supported. - - Example: >lua - vim.g.health = { style = 'float' } - -Commands *health-commands* +COMMANDS *health-commands* *:che* *:checkhealth* :che[ckhealth] Run all healthchecks. @@ -60,6 +49,23 @@ Commands *health-commands* :checkhealth vim* < +USAGE *health-usage* + +Local mappings in the healthcheck buffer: + +q Closes the window. + +Global configuration: + + *g:health* +g:health Dictionary with the following optional keys: + - `style` (`'float'|nil`) Set to "float" to display :checkhealth in + a floating window instead of the default behavior. + + Example: >lua + vim.g.health = { style = 'float' } + +-------------------------------------------------------------------------------- Create a healthcheck *health-dev* Healthchecks are functions that check the user environment, configuration, or diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index 46b3ab507d..f7009aebfe 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -193,6 +193,7 @@ Jump to specific subjects by using tags. This can be done in two ways: Use CTRL-T or CTRL-O to jump back. Use ":q" to close the help window. +Use `yxx` to execute the current Lua/Vimscript code block. If there are several matches for an item you are looking for, this is how you can jump to each one of them: diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 16e6abe294..e8270123d7 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -32,7 +32,7 @@ Follow these steps to get LSP features: Example: >lua vim.lsp.config['luals'] = { -- Command and arguments to start the server. - cmd = { 'lua-language-server' } + cmd = { 'lua-language-server' }, -- Filetypes to automatically attach to. filetypes = { 'lua' }, @@ -93,7 +93,7 @@ Given: >lua multilineTokenSupport = true, } } - } + }, root_markers = { '.git' }, }) @@ -878,7 +878,7 @@ foldexpr({lnum}) *vim.lsp.foldexpr()* To use, check for the "textDocument/foldingRange" capability in an |LspAttach| autocommand. Example: >lua - vim.api.nvim_create_autocommand('LspAttach', { + vim.api.nvim_create_autocmd('LspAttach', { callback = function(args) local client = vim.lsp.get_client_by_id(args.data.client_id) if client:supports_method('textDocument/foldingRange') then diff --git a/runtime/doc/lua-guide.txt b/runtime/doc/lua-guide.txt index b40d5a0791..d0d148f689 100644 --- a/runtime/doc/lua-guide.txt +++ b/runtime/doc/lua-guide.txt @@ -153,7 +153,7 @@ its functions if this succeeds and prints an error message otherwise: if not ok then print("Module had an error") else - mymod.function() + mymod.func() end < In contrast to |:source|, |require()| not only searches through all `lua/` directories diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 6547a76f56..44cbf238cf 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -161,7 +161,7 @@ languages like Python and C#. Example: >lua local func_with_opts = function(opts) local will_do_foo = opts.foo local filename = opts.filename - ... + -- ... end func_with_opts { foo = true, filename = "hello.world" } @@ -1299,7 +1299,7 @@ global value of a |global-local| option, see |:setglobal|. A special interface |vim.opt| exists for conveniently interacting with list- -and map-style option from Lua: It allows accessing them as Lua tables and +and map-style options from Lua: It allows accessing them as Lua tables and offers object-oriented method for adding and removing entries. Examples: ~ @@ -1505,7 +1505,7 @@ vim.wo[{winid}][{bufnr}] *vim.wo* Lua module: vim *lua-vim* vim.cmd({command}) *vim.cmd()* - Executes Vim script commands. + Executes Vimscript (|Ex-commands|). Note that `vim.cmd` can be indexed with a command name to return a callable function to the command. @@ -1539,9 +1539,9 @@ vim.cmd({command}) *vim.cmd()* Parameters: ~ • {command} (`string|table`) Command(s) to execute. If a string, - executes multiple lines of Vim script at once. In this - case, it is an alias to |nvim_exec2()|, where `opts.output` - is set to false. Thus it works identical to |:source|. If a + executes multiple lines of Vimscript at once. In this case, + it is an alias to |nvim_exec2()|, where `opts.output` is + set to false. Thus it works identical to |:source|. If a table, executes a single command. In this case, it is an alias to |nvim_cmd()| where `opts` is empty. @@ -1805,7 +1805,7 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()* -- Runs synchronously: local obj = vim.system({'echo', 'hello'}, { text = true }):wait() - -- { code = 0, signal = 0, stdout = 'hello', stderr = '' } + -- { code = 0, signal = 0, stdout = 'hello\n', stderr = '' } < See |uv.spawn()| for more details. Note: unlike |uv.spawn()|, vim.system @@ -2428,7 +2428,7 @@ vim.validate({name}, {value}, {validator}, {optional}, {message}) function vim.startswith(s, prefix) vim.validate('s', s, 'string') vim.validate('prefix', prefix, 'string') - ... + -- ... end < 2. `vim.validate(spec)` (deprecated) where `spec` is of type @@ -2442,7 +2442,7 @@ vim.validate({name}, {value}, {validator}, {optional}, {message}) age={age, 'number'}, hobbies={hobbies, 'table'}, } - ... + -- ... end < @@ -3148,6 +3148,23 @@ vim.fs.parents({start}) *vim.fs.parents()* (`nil`) (`string?`) +vim.fs.relpath({base}, {target}, {opts}) *vim.fs.relpath()* + Gets `target` path relative to `base`, or `nil` if `base` is not an + ancestor. + + Example: >lua + vim.fs.relpath('/var', '/var/lib') -- 'lib' + vim.fs.relpath('/var', '/usr/bin') -- nil +< + + Parameters: ~ + • {base} (`string`) + • {target} (`string`) + • {opts} (`table?`) Reserved for future use + + Return: ~ + (`string?`) + vim.fs.rm({path}, {opts}) *vim.fs.rm()* WARNING: This feature is experimental/unstable. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 931f5e117c..e6a1adf15b 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -68,6 +68,8 @@ DIAGNOSTICS • The "underline" diagnostics handler sorts diagnostics by severity when using the "severity_sort" option. +• Diagnostics are filtered by severity before being passed to a diagnostic + handler |diagnostic-handlers|. EDITOR @@ -94,7 +96,7 @@ EVENTS • `msg_show`: • `history` argument indicating if the message was added to the history. • new message kinds: "bufwrite", "completion", "list_cmd", "lua_print", - "search_cmd", "undo", "wildlist". + "search_cmd", "undo", "verbose", wildlist". HIGHLIGHTS @@ -156,6 +158,11 @@ TREESITTER if no languages are explicitly registered. • |vim.treesitter.language.add()| returns `true` if a parser was loaded successfully and `nil,errmsg` otherwise instead of throwing an error. +• |vim.treesitter.get_parser()| and |vim.treesitter.start()| no longer parse + the tree before returning. Scripts must call |LanguageTree:parse()| explicitly. >lua + local p = vim.treesitter.get_parser(0, 'c') + p:parse() +< TUI @@ -178,6 +185,9 @@ API • Improved API "meta" docstrings and :help documentation. • |nvim__ns_set()| can set properties for a namespace +• |nvim_echo()| `err` field to print error messages and `chunks` accepts + highlight group IDs. +• |nvim_open_win()| `relative` field can be set to "laststatus" and "tabline". DEFAULTS @@ -219,6 +229,7 @@ DIAGNOSTICS EDITOR +• Use |yxx| in :help docs to execute Lua and Vimscript code examples. • Improved |paste| handling for redo (dot-repeat) and macros (|recording|): • Redoing a large paste is significantly faster and ignores 'autoindent'. • Replaying a macro with |@| also replays pasted text. @@ -274,6 +285,7 @@ LUA supporting two new parameters, `encoding` and `strict_indexing`. • |vim.json.encode()| has an option to enable forward slash escaping • |vim.fs.abspath()| converts paths to absolute paths. +• |vim.fs.relpath()| gets relative path compared to base path. OPTIONS @@ -289,6 +301,12 @@ PERFORMANCE inflight requests). This greatly improves performance with slow LSP servers. • 10x speedup for |vim.treesitter.foldexpr()| (when no parser exists for the buffer). +• Strong |treesitter-query| caching makes repeat |vim.treesitter.query.get()| + and |vim.treesitter.query.parse()| calls significantly faster for large + queries. +• Treesitter highlighting is now asynchronous. To force synchronous parsing, + use `vim.g._ts_force_sync_parsing = true`. +• Treesitter folding is now calculated asynchronously. PLUGINS @@ -331,6 +349,8 @@ TREESITTER • New |TSNode:child_with_descendant()|, which is nearly identical to |TSNode:child_containing_descendant()| except that it can return the descendant itself. +• |LanguageTree:parse()| optionally supports asynchronous invocation, which is + activated by passing the `on_parse` callback parameter. TUI @@ -359,8 +379,8 @@ UI • |vim.diagnostic.setqflist()| updates an existing quickfix list with the given title if found • |ui-messages| content chunks now also contain the highlight group ID. -• |:checkhealth| can be display in a floating window and controlled by - the |g:health| variable. +• |:checkhealth| can display in a floating window, controlled by the + |g:health| variable. ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c2ed19f34f..8d171183d6 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1560,8 +1560,8 @@ A jump table for the options with a short description can be found at |Q_op|. "menu" or "menuone". No effect if "longest" is present. noselect Same as "noinsert", except that no menu item is - pre-selected. If both "noinsert" and "noselect" are present, - "noselect" has precedence. + pre-selected. If both "noinsert" and "noselect" are + present, "noselect" has precedence. fuzzy Enable |fuzzy-matching| for completion candidates. This allows for more flexible and intuitive matching, where @@ -4657,8 +4657,8 @@ A jump table for the options with a short description can be found at |Q_op|. 'redrawtime' 'rdt' number (default 2000) global Time in milliseconds for redrawing the display. Applies to - 'hlsearch', 'inccommand', |:match| highlighting and syntax - highlighting. + 'hlsearch', 'inccommand', |:match| highlighting, syntax highlighting, + and async |LanguageTree:parse()|. When redrawing takes more than this many milliseconds no further matches will be highlighted. For syntax highlighting the time applies per window. When over the diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index a291c0277d..70082c7835 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -538,9 +538,9 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST: < Otherwise it works the same as `:ldo`. FILTERING A QUICKFIX OR LOCATION LIST: - *cfilter-plugin* *:Cfilter* *:Lfilter* + *cfilter-plugin* *:Cfilter* *:Lfilter* *package-cfilter* If you have too many entries in a quickfix list, you can use the cfilter -plugin to reduce the number of entries. Load the plugin with: > +plugin to reduce the number of entries. Load the plugin with: >vim packadd cfilter diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 0bfbea75fb..c0254c3fa1 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -164,8 +164,7 @@ argument. you can overwrite a file by adding an exclamation mark to the Ex command, as in ":w!". The 'readonly' option can be reset with ":set noro" (see the options chapter, |options|). - Subsequent edits will not be done in readonly mode. Calling - the executable "view" has the same effect as the -R argument. + Subsequent edits will not be done in readonly mode. The 'updatecount' option will be set to 10000, meaning that the swap file will not be updated automatically very often. See |-M| for disallowing modifications. @@ -226,7 +225,8 @@ argument. arguments. The {script} name is stored at `_G.arg[0]`. Sets 'verbose' to 1 (like "-V1"), so Lua `print()` writes to - output. + output, as well as other message-emitting functions like + |:echo|. If {script} prints messages and doesn't cause Nvim to exit, Nvim ensures output ends with a newline. @@ -288,21 +288,18 @@ argument. command from a script. |debug-mode| *-n* --n No |swap-file| will be used. Recovery after a crash will be - impossible. Handy if you want to view or edit a file on a - very slow medium (e.g., a floppy). - Can also be done with ":set updatecount=0". You can switch it - on again by setting the 'updatecount' option to some value, - e.g., ":set uc=100". - 'updatecount' is set to 0 AFTER executing commands from a - vimrc file, but before the GUI initializations. Thus it - overrides a setting for 'updatecount' in a vimrc file, but not - in a gvimrc file. See |startup|. - When you want to reduce accesses to the disk (e.g., for a - laptop), don't use "-n", but set 'updatetime' and - 'updatecount' to very big numbers, and type ":preserve" when - you want to save your work. This way you keep the possibility - for crash recovery. +-n Disables |swap-file| by setting 'updatecount' to 0 (after + executing any |vimrc|). Recovery after a crash will be + impossible. Improves peformance when working with a file on + a very slow medium (usb drive, network share). + + Enable it again by setting 'updatecount' to some value, e.g. + ":set updatecount=100". + + To reduce accesses to the disk, don't use "-n", but set + 'updatetime' and 'updatecount' to very big numbers, and type + ":preserve" when you want to save your work. This way you + keep the possibility for crash recovery. *-o* -o[N] Open N windows, split horizontally. If [N] is not given, diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index ff8e3a976f..a7f278990c 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -165,8 +165,8 @@ directory indicated in the request. >lua end }) -To try it out, select the above code and source it with `:'<,'>lua`, then run -this command in a :terminal buffer: > +To try it out, select the above code and source it with `:'<,'>lua` (or +`yxx`), then run this command in a :terminal buffer: > printf "\033]7;file://./foo/bar\033\\" @@ -207,7 +207,7 @@ Use |jobwait()| to check if the terminal job has finished: >vim let running = jobwait([&channel], 0)[0] == -1 < ============================================================================== -:Termdebug plugin *terminal-debug* +:Termdebug plugin *terminal-debug* *terminal-debugger* *package-termdebug* The Terminal debugging plugin can be used to debug a program with gdb and view the source code in a Vim window. Since this is completely contained inside diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 28fe943359..41679f80ca 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -70,7 +70,7 @@ adds arbitrary metadata and conditional data to a match. Queries are written in a lisp-like language documented in https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax -Note: The predicates listed there page differ from those Nvim supports. See +Note: The predicates listed there differ from those Nvim supports. See |treesitter-predicates| for a complete list of predicates supported by Nvim. Nvim looks for queries as `*.scm` files in a `queries` directory under @@ -1090,6 +1090,9 @@ start({bufnr}, {lang}) *vim.treesitter.start()* required for some plugins. In this case, add `vim.bo.syntax = 'on'` after the call to `start`. + Note: By default, the highlighter parses code asynchronously, using a + segment time of 3ms. + Example: >lua vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex', callback = function(args) @@ -1336,7 +1339,7 @@ parse({lang}, {query}) *vim.treesitter.query.parse()* `info.captures`). • `info.patterns`: information about predicates. - Example (select the code then run `:'<,'>lua` to try it): >lua + Example (to try it, use `yxx` or select the code then run `:'<,'>lua`): >lua local query = vim.treesitter.query.parse('vimdoc', [[ ; query ((h1) @str @@ -1401,8 +1404,8 @@ Query:iter_captures({node}, {source}, {start}, {stop}) Defaults to `node:end_()`. Return: ~ - (`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch`) - capture id, capture node, metadata, match + (`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`) + capture id, capture node, metadata, match, tree *Query:iter_matches()* Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) @@ -1422,7 +1425,7 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) -- `node` was captured by the `name` capture in the match local node_data = metadata[id] -- Node level metadata - ... use the info here ... + -- ... use the info here ... end end end @@ -1447,8 +1450,8 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) compatibility and will be removed in a future release. Return: ~ - (`fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata`) - pattern id, match, metadata + (`fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata, TSTree`) + pattern id, match, metadata, tree set({lang}, {query_name}, {text}) *vim.treesitter.query.set()* Sets the runtime query named {query_name} for {lang} @@ -1611,7 +1614,7 @@ LanguageTree:node_for_range({range}, {opts}) Return: ~ (`TSNode?`) -LanguageTree:parse({range}) *LanguageTree:parse()* +LanguageTree:parse({range}, {on_parse}) *LanguageTree:parse()* Recursively parse all regions in the language tree using |treesitter-parsers| for the corresponding languages and run injection queries on the parsed trees to determine whether child trees should be @@ -1622,14 +1625,27 @@ LanguageTree:parse({range}) *LanguageTree:parse()* if {range} is `true`). Parameters: ~ - • {range} (`boolean|Range?`) Parse this range in the parser's source. - Set to `true` to run a complete parse of the source (Note: - Can be slow!) Set to `false|nil` to only parse regions with - empty ranges (typically only the root tree without - injections). + • {range} (`boolean|Range?`) Parse this range in the parser's + source. Set to `true` to run a complete parse of the + source (Note: Can be slow!) Set to `false|nil` to only + parse regions with empty ranges (typically only the root + tree without injections). + • {on_parse} (`fun(err?: string, trees?: table<integer, TSTree>)?`) + Function invoked when parsing completes. When provided and + `vim.g._ts_force_sync_parsing` is not set, parsing will + run asynchronously. The first argument to the function is + a string respresenting the error type, in case of a + failure (currently only possible for timeouts). The second + argument is the list of trees returned by the parse (upon + success), or `nil` if the parse timed out (determined by + 'redrawtime'). + + If parsing was still able to finish synchronously (within + 3ms), `parse()` returns the list of trees. Otherwise, it + returns `nil`. Return: ~ - (`table<integer, TSTree>`) + (`table<integer, TSTree>?`) *LanguageTree:register_cbs()* LanguageTree:register_cbs({cbs}, {recursive}) diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 8f25133e7a..26ea03d2be 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -806,6 +806,7 @@ must handle. "search_cmd" Entered search command "search_count" Search count message ("S" flag of 'shortmess') "undo" |:undo| and |:redo| message + "verbose" 'verbose' message "wildlist" 'wildmode' "list" message "wmsg" Warning ("search hit BOTTOM", |W10|, …) New kinds may be added in the future; clients should treat unknown diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt index 1fc612de26..f8cfcbe547 100644 --- a/runtime/doc/usr_02.txt +++ b/runtime/doc/usr_02.txt @@ -684,6 +684,13 @@ Summary: *help-summary* > :help E128 < takes you to the |:function| command +27) Documenction for packages distributed with Vim have the form package-<name>. + So > + :help package-termdebug +< + will bring you to the help section for the included termdebug plugin and + how to enable it. + ============================================================================== diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index 698d1207d3..d75438cd22 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -235,7 +235,7 @@ an archive or as a repository. For an archive you can follow these steps: else. -Adding nohlsearch package *nohlsearch-install* +Adding nohlsearch package *nohlsearch-install* *package-nohlsearch* Load the plugin with this command: > packadd nohlsearch diff --git a/runtime/doc/usr_25.txt b/runtime/doc/usr_25.txt index 955d2ae5f0..8dbe1332b5 100644 --- a/runtime/doc/usr_25.txt +++ b/runtime/doc/usr_25.txt @@ -190,15 +190,15 @@ This results in the following: story. ~ -JUSTIFYING TEXT +JUSTIFYING TEXT *justify* *:Justify* *Justify()* *package-justify* Vim has no built-in way of justifying text. However, there is a neat macro package that does the job. To use this package, execute the following -command: > +command: >vim :packadd justify -Or put this line in your |vimrc|: > +Or put this line in your |vimrc|: >vim packadd! justify diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 6074931565..611e820cab 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -554,6 +554,11 @@ gO Show a filetype-specific, navigable "outline" of the *:sl!* *:sleep!* :[N]sl[eep]! [N][m] Same as above, but hide the cursor. + *yxx* +yxx Executes the current code block. + + Works in |help| buffers. + ============================================================================== 2. Using Vim like less or more *less* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index a92ddf33e6..a59312208a 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -347,11 +347,15 @@ Options: - `:set {option}<` removes local value for all |global-local| options. - `:setlocal {option}<` copies global value to local value for all options. +- 'ambiwidth' cannot be set to empty. - 'autoread' works in the terminal (if it supports "focus" events) +- 'background' cannot be set to empty. - 'cpoptions' flags: |cpo-_| - 'diffopt' "linematch" feature +- 'eadirection' cannot be set to empty. - 'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The user is prompted whether to trust the file. +- 'fileformat' cannot be set to empty. - 'fillchars' flags: "msgsep", "horiz", "horizup", "horizdown", "vertleft", "vertright", "verthoriz" - 'foldcolumn' supports up to 9 dynamic/fixed columns @@ -363,14 +367,17 @@ Options: - "clean" removes unloaded buffers from the jumplist. - the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|. - 'laststatus' global statusline support +- 'mousemodel' cannot be set to empty. - 'mousescroll' amount to scroll by when scrolling with a mouse - 'pumblend' pseudo-transparent popupmenu - 'scrollback' - 'shortmess' - "F" flag does not affect output from autocommands. - "q" flag fully hides macro recording message. +- 'showcmdloc' cannot be set to empty. - 'signcolumn' can show multiple signs (dynamic or fixed columns) - 'statuscolumn' full control of columns using 'statusline' format +- 'splitkeep' cannot be set to empty. - 'tabline' middle-click on tabpage label closes tabpage, and %@Func@foo%X can call any function on mouse-click - 'termpastefilter' @@ -423,8 +430,11 @@ TUI: < *'term'* *E529* *E530* *E531* - 'term' reflects the terminal type derived from |$TERM| and other environment - checks. For debugging only; not reliable during startup. >vim - :echo &term + checks. Use `:echo &term` to get its value. For debugging only; not + reliable during startup. + - Note: If you want to detect when Nvim is running in a terminal, use + `has('gui_running')` |has()| or see |nvim_list_uis()| for an example of + how to inspect the UI channel. - "builtin_x" means one of the |builtin-terms| was chosen, because the expected terminfo file was not found on the system. - Nvim will use 256-colour capability on Linux virtual terminals. Vim uses diff --git a/runtime/ftplugin/editorconfig.vim b/runtime/ftplugin/editorconfig.vim index 6d437351eb..1693a95c0b 100644 --- a/runtime/ftplugin/editorconfig.vim +++ b/runtime/ftplugin/editorconfig.vim @@ -1,7 +1,7 @@ " Vim filetype plugin " Language: EditorConfig " Maintainer: Riley Bruins <ribru17@gmail.com> -" Last Change: 2024 Jul 06 +" Last Change: 2025 Jan 10 if exists('b:did_ftplugin') finish @@ -10,4 +10,6 @@ let b:did_ftplugin = 1 setl comments=:#,:; commentstring=#\ %s -let b:undo_ftplugin = 'setl com< cms<' +setl omnifunc=syntaxcomplete#Complete + +let b:undo_ftplugin = 'setl com< cms< ofu<' diff --git a/runtime/ftplugin/help.lua b/runtime/ftplugin/help.lua index 8d991be0e4..689a4db408 100644 --- a/runtime/ftplugin/help.lua +++ b/runtime/ftplugin/help.lua @@ -31,5 +31,56 @@ vim.keymap.set('n', 'gO', function() require('vim.vimhelp').show_toc() end, { buffer = 0, silent = true }) -vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') .. '\n exe "nunmap <buffer> gO"' +-- Add "runnables" for Lua/Vimscript code examples. +---@type table<integer, { lang: string, code: string }> +local code_blocks = {} +local tree = vim.treesitter.get_parser():parse()[1] +local query = vim.treesitter.query.parse( + 'vimdoc', + [[ + (codeblock + (language) @_lang + . + (code) @code + (#any-of? @_lang "lua" "vim") + (#set! @code lang @_lang)) +]] +) +local run_message_ns = vim.api.nvim_create_namespace('vimdoc/run_message') + +vim.api.nvim_buf_clear_namespace(0, run_message_ns, 0, -1) +for _, match, metadata in query:iter_matches(tree:root(), 0, 0, -1) do + for id, nodes in pairs(match) do + local name = query.captures[id] + local node = nodes[1] + local start, _, end_ = node:parent():range() --[[@as integer]] + + if name == 'code' then + vim.api.nvim_buf_set_extmark(0, run_message_ns, start, 0, { + virt_text = { { 'Run with `yxx`', 'LspCodeLens' } }, + }) + local code = vim.treesitter.get_node_text(node, 0) + local lang_node = match[metadata[id].lang][1] --[[@as TSNode]] + local lang = vim.treesitter.get_node_text(lang_node, 0) + for i = start + 1, end_ do + code_blocks[i] = { lang = lang, code = code } + end + end + end +end + +vim.keymap.set('n', 'yxx', function() + local pos = vim.api.nvim_win_get_cursor(0)[1] + local code_block = code_blocks[pos] + if not code_block then + vim.print('No code block found') + elseif code_block.lang == 'lua' then + vim.cmd.lua(code_block.code) + elseif code_block.lang == 'vim' then + vim.cmd(code_block.code) + end +end, { buffer = true }) + +vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') + .. '\n exe "nunmap <buffer> gO" | exe "nunmap <buffer> yxx"' vim.b.undo_ftplugin = vim.b.undo_ftplugin .. ' | call v:lua.vim.treesitter.stop()' diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 4216a2acb7..d71116117e 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -224,7 +224,7 @@ do local function cmd(opts) local ok, err = pcall(vim.api.nvim_cmd, opts, {}) if not ok then - vim.api.nvim_err_writeln(err:sub(#'Vim:' + 1)) + vim.api.nvim_echo({ { err:sub(#'Vim:' + 1) } }, true, { err = true }) end end diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 44f17b3f85..4b28b63746 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -58,6 +58,7 @@ vim._extra = { --- @private vim.log = { + --- @enum vim.log.levels levels = { TRACE = 0, DEBUG = 1, @@ -92,7 +93,7 @@ local utfs = { --- --- -- Runs synchronously: --- local obj = vim.system({'echo', 'hello'}, { text = true }):wait() ---- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' } +--- -- { code = 0, signal = 0, stdout = 'hello\n', stderr = '' } --- --- ``` --- @@ -390,7 +391,7 @@ end local VIM_CMD_ARG_MAX = 20 ---- Executes Vim script commands. +--- Executes Vimscript (|Ex-commands|). --- --- Note that `vim.cmd` can be indexed with a command name to return a callable function to the --- command. @@ -425,7 +426,7 @@ local VIM_CMD_ARG_MAX = 20 --- ``` --- ---@param command string|table Command(s) to execute. ---- If a string, executes multiple lines of Vim script at once. In this +--- If a string, executes multiple lines of Vimscript at once. In this --- case, it is an alias to |nvim_exec2()|, where `opts.output` is set --- to false. Thus it works identical to |:source|. --- If a table, executes a single command. In this case, it is an alias @@ -620,13 +621,8 @@ end ---@param opts table|nil Optional parameters. Unused by default. ---@diagnostic disable-next-line: unused-local function vim.notify(msg, level, opts) -- luacheck: no unused args - if level == vim.log.levels.ERROR then - vim.api.nvim_err_writeln(msg) - elseif level == vim.log.levels.WARN then - vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, {}) - else - vim.api.nvim_echo({ { msg } }, true, {}) - end + local chunks = { { msg, level == vim.log.levels.WARN and 'WarningMsg' or nil } } + vim.api.nvim_echo(chunks, true, { err = level == vim.log.levels.ERROR }) end do diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 7297c8ad38..670e867c1e 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1097,29 +1097,28 @@ function vim.api.nvim_del_user_command(name) end --- @param name string Variable name function vim.api.nvim_del_var(name) end ---- Echo a message. +--- Prints a message given by a list of `[text, hl_group]` "chunks". --- ---- @param chunks any[] A list of `[text, hl_group]` arrays, each representing a ---- text chunk with specified highlight group name or ID. ---- `hl_group` element can be omitted for no highlight. +--- Example: +--- ```lua +--- vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {}) +--- ``` +--- +--- @param chunks any[] List of `[text, hl_group]` pairs, where each is a `text` string highlighted by +--- the (optional) name or ID `hl_group`. --- @param history boolean if true, add to `message-history`. --- @param opts vim.api.keyset.echo_opts Optional parameters. ---- - verbose: Message is printed as a result of 'verbose' option. ---- If Nvim was invoked with -V3log_file, the message will be ---- redirected to the log_file and suppressed from direct output. +--- - err: Treat the message like `:echoerr`. Sets `hl_group` to `hl-ErrorMsg` by default. +--- - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log` +--- will write the message to the "log" file instead of standard output. function vim.api.nvim_echo(chunks, history, opts) end ---- Writes a message to the Vim error buffer. Does not append "\n", the ---- message is buffered (won't display) until a linefeed is written. ---- ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_err_write(str) end ---- Writes a message to the Vim error buffer. Appends "\n", so the buffer is ---- flushed (and displayed). ---- ---- @see vim.api.nvim_err_write ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_err_writeln(str) end --- Evaluates a Vimscript `expression`. Dicts and Lists are recursively expanded. @@ -1279,6 +1278,8 @@ function vim.api.nvim_get_autocmds(opts) end --- Gets information about a channel. --- +--- See `nvim_list_uis()` for an example of how to get channel info. +--- --- @param chan integer channel_id, or 0 for current channel --- @return table<string,any> # Channel info dict with these keys: --- - "id" Channel id. @@ -1296,8 +1297,8 @@ function vim.api.nvim_get_autocmds(opts) end --- "/dev/pts/1". If unknown, the key will still be present if a pty is used (e.g. --- for conpty on Windows). --- - "buffer" (optional) Buffer connected to |terminal| instance. ---- - "client" (optional) Info about the peer (client on the other end of the RPC channel), ---- which it provided via |nvim_set_client_info()|. +--- - "client" (optional) Info about the peer (client on the other end of the channel), as set +--- by |nvim_set_client_info()|. --- function vim.api.nvim_get_chan_info(chan) end @@ -1619,6 +1620,14 @@ function vim.api.nvim_list_tabpages() end --- Gets a list of dictionaries representing attached UIs. --- +--- Example: The Nvim builtin `TUI` sets its channel info as described in `startup-tui`. In +--- particular, it sets `client.name` to "nvim-tui". So you can check if the TUI is running by +--- inspecting the client name of each UI: +--- +--- ```lua +--- vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name) +--- ``` +--- --- @return any[] # Array of UI dictionaries, each with these keys: --- - "height" Requested height of the UI --- - "width" Requested width of the UI @@ -1638,14 +1647,10 @@ function vim.api.nvim_list_wins() end --- @return any function vim.api.nvim_load_context(dict) end ---- Notify the user with a message ---- ---- Relays the call to vim.notify . By default forwards your message in the ---- echo area but can be overridden to trigger desktop notifications. ---- ---- @param msg string Message to display to the user ---- @param log_level integer The log level ---- @param opts table<string,any> Reserved for future use. +--- @deprecated +--- @param msg string +--- @param log_level integer +--- @param opts table<string,any> --- @return any function vim.api.nvim_notify(msg, log_level, opts) end @@ -1664,7 +1669,8 @@ function vim.api.nvim_notify(msg, log_level, opts) end --- in a virtual terminal having the intended size. --- --- Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you ---- can use Nvim as a "scrollback pager" (for terminals like kitty): [terminal-scrollback-pager]() +--- can use Nvim as a "scrollback pager" (for terminals like kitty): [ansi-colorize]() +--- [terminal-scrollback-pager]() --- --- ```lua --- vim.api.nvim_create_user_command('TermHl', function() @@ -1748,10 +1754,12 @@ function vim.api.nvim_open_term(buffer, opts) end --- @param config vim.api.keyset.win_config Map defining the window configuration. Keys: --- - relative: Sets the window layout to "floating", placed at (row,col) --- coordinates relative to: ---- - "editor" The global editor grid ---- - "win" Window given by the `win` field, or current window. ---- - "cursor" Cursor position in current window. ---- - "mouse" Mouse position +--- - "cursor" Cursor position in current window. +--- - "editor" The global editor grid. +--- - "laststatus" 'laststatus' if present, or last row. +--- - "mouse" Mouse position. +--- - "tabline" Tabline if present, or first row. +--- - "win" Window given by the `win` field, or current window. --- - win: `window-ID` window to split, or relative window when creating a --- float (relative="win"). --- - anchor: Decides which corner of the float to place at (row,col): @@ -1859,10 +1867,8 @@ function vim.api.nvim_open_term(buffer, opts) end --- @return integer # Window handle, or 0 on error function vim.api.nvim_open_win(buffer, enter, config) end ---- Writes a message to the Vim output buffer. Does not append "\n", the ---- message is buffered (won't display) until a linefeed is written. ---- ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_out_write(str) end --- Parse command line. diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index c08ab0663b..98e916115e 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -88,6 +88,7 @@ error('Cannot require a meta file') --- @field pattern? string|string[] --- @class vim.api.keyset.echo_opts +--- @field err? boolean --- @field verbose? boolean --- @class vim.api.keyset.empty diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index e5cea884c5..14f252516a 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -52,7 +52,7 @@ vim.go.ari = vim.go.allowrevins --- set to one of CJK locales. See Unicode Standard Annex #11 --- (https://www.unicode.org/reports/tr11). --- ---- @type string +--- @type 'single'|'double' vim.o.ambiwidth = "single" vim.o.ambw = vim.o.ambiwidth vim.go.ambiwidth = vim.o.ambiwidth @@ -208,7 +208,7 @@ vim.go.awa = vim.go.autowriteall --- will change. To use other settings, place ":highlight" commands AFTER --- the setting of the 'background' option. --- ---- @type string +--- @type 'light'|'dark' vim.o.background = "dark" vim.o.bg = vim.o.background vim.go.background = vim.o.background @@ -595,7 +595,7 @@ vim.wo.briopt = vim.wo.breakindentopt --- This option is used together with 'buftype' and 'swapfile' to specify --- special kinds of buffers. See `special-buffers`. --- ---- @type string +--- @type ''|'hide'|'unload'|'delete'|'wipe' vim.o.bufhidden = "" vim.o.bh = vim.o.bufhidden vim.bo.bufhidden = vim.o.bufhidden @@ -658,7 +658,7 @@ vim.bo.bl = vim.bo.buflisted --- without saving. For writing there must be matching `BufWriteCmd|, --- |FileWriteCmd` or `FileAppendCmd` autocommands. --- ---- @type string +--- @type ''|'acwrite'|'help'|'nofile'|'nowrite'|'quickfix'|'terminal'|'prompt' vim.o.buftype = "" vim.o.bt = vim.o.buftype vim.bo.buftype = vim.o.buftype @@ -1087,8 +1087,8 @@ vim.go.cia = vim.go.completeitemalign --- "menu" or "menuone". No effect if "longest" is present. --- --- noselect Same as "noinsert", except that no menu item is ---- pre-selected. If both "noinsert" and "noselect" are present, ---- "noselect" has precedence. +--- pre-selected. If both "noinsert" and "noselect" are +--- present, "noselect" has precedence. --- --- fuzzy Enable `fuzzy-matching` for completion candidates. This --- allows for more flexible and intuitive matching, where @@ -1118,7 +1118,7 @@ vim.go.cot = vim.go.completeopt --- For Insert mode completion the buffer-local value is used. For --- command line completion the global value is used. --- ---- @type string +--- @type ''|'slash'|'backslash' vim.o.completeslash = "" vim.o.csl = vim.o.completeslash vim.bo.completeslash = vim.o.completeslash @@ -1824,7 +1824,7 @@ vim.go.dy = vim.go.display --- hor horizontally, height of windows is not affected --- both width and height of windows is affected --- ---- @type string +--- @type 'both'|'ver'|'hor' vim.o.eadirection = "both" vim.o.ead = vim.o.eadirection vim.go.eadirection = vim.o.eadirection @@ -2126,7 +2126,7 @@ vim.go.fencs = vim.go.fileencodings --- option is set, because the file would be different when written. --- This option cannot be changed when 'modifiable' is off. --- ---- @type string +--- @type 'unix'|'dos'|'mac' vim.o.fileformat = "unix" vim.o.ff = vim.o.fileformat vim.bo.fileformat = vim.o.fileformat @@ -2382,7 +2382,7 @@ vim.go.fcl = vim.go.foldclose --- "[1-9]": to display a fixed number of columns --- See `folding`. --- ---- @type string +--- @type 'auto'|'auto:1'|'auto:2'|'auto:3'|'auto:4'|'auto:5'|'auto:6'|'auto:7'|'auto:8'|'auto:9'|'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' vim.o.foldcolumn = "0" vim.o.fdc = vim.o.foldcolumn vim.wo.foldcolumn = vim.o.foldcolumn @@ -2479,7 +2479,7 @@ vim.wo.fmr = vim.wo.foldmarker --- `fold-syntax` syntax Syntax highlighting items specify folds. --- `fold-diff` diff Fold text that is not changed. --- ---- @type string +--- @type 'manual'|'expr'|'marker'|'indent'|'syntax'|'diff' vim.o.foldmethod = "manual" vim.o.fdm = vim.o.foldmethod vim.wo.foldmethod = vim.o.foldmethod @@ -3144,7 +3144,7 @@ vim.bo.ims = vim.bo.imsearch --- 'redrawtime') then 'inccommand' is automatically disabled until --- `Command-line-mode` is done. --- ---- @type string +--- @type 'nosplit'|'split'|'' vim.o.inccommand = "nosplit" vim.o.icm = vim.o.inccommand vim.go.inccommand = vim.o.inccommand @@ -4354,7 +4354,7 @@ vim.go.mh = vim.go.mousehide --- "g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click) --- "g<RightMouse>" is "<C-RightMouse> ("CTRL-T") --- ---- @type string +--- @type 'extend'|'popup'|'popup_setpos' vim.o.mousemodel = "popup_setpos" vim.o.mousem = vim.o.mousemodel vim.go.mousemodel = vim.o.mousemodel @@ -4845,8 +4845,8 @@ vim.go.redrawdebug = vim.o.redrawdebug vim.go.rdb = vim.go.redrawdebug --- Time in milliseconds for redrawing the display. Applies to ---- 'hlsearch', 'inccommand', `:match` highlighting and syntax ---- highlighting. +--- 'hlsearch', 'inccommand', `:match` highlighting, syntax highlighting, +--- and async `LanguageTree:parse()`. --- When redrawing takes more than this many milliseconds no further --- matches will be highlighted. --- For syntax highlighting the time applies per window. When over the @@ -4947,7 +4947,7 @@ vim.wo.rl = vim.wo.rightleft --- This is useful for languages such as Hebrew, Arabic and Farsi. --- The 'rightleft' option must be set for 'rightleftcmd' to take effect. --- ---- @type string +--- @type 'search' vim.o.rightleftcmd = "search" vim.o.rlc = vim.o.rightleftcmd vim.wo.rightleftcmd = vim.o.rightleftcmd @@ -5222,7 +5222,7 @@ vim.go.sect = vim.go.sections --- backwards, you cannot include the last character of a line, when --- starting in Normal mode and 'virtualedit' empty. --- ---- @type string +--- @type 'inclusive'|'exclusive'|'old' vim.o.selection = "inclusive" vim.o.sel = vim.o.selection vim.go.selection = vim.o.selection @@ -5788,7 +5788,7 @@ vim.go.sc = vim.go.showcmd --- place the text. Without a custom 'statusline' or 'tabline' it will be --- displayed in a convenient location. --- ---- @type string +--- @type 'last'|'statusline'|'tabline' vim.o.showcmdloc = "last" vim.o.sloc = vim.o.showcmdloc vim.go.showcmdloc = vim.o.showcmdloc @@ -5920,7 +5920,7 @@ vim.go.siso = vim.go.sidescrolloff --- "number" display signs in the 'number' column. If the number --- column is not present, then behaves like "auto". --- ---- @type string +--- @type 'yes'|'no'|'auto'|'auto:1'|'auto:2'|'auto:3'|'auto:4'|'auto:5'|'auto:6'|'auto:7'|'auto:8'|'auto:9'|'yes:1'|'yes:2'|'yes:3'|'yes:4'|'yes:5'|'yes:6'|'yes:7'|'yes:8'|'yes:9'|'number' vim.o.signcolumn = "auto" vim.o.scl = vim.o.signcolumn vim.wo.signcolumn = vim.o.signcolumn @@ -6228,7 +6228,7 @@ vim.go.sb = vim.go.splitbelow --- with the previous cursor position. For "screen", the text cannot always --- be kept on the same screen line when 'wrap' is enabled. --- ---- @type string +--- @type 'cursor'|'screen'|'topline' vim.o.splitkeep = "cursor" vim.o.spk = vim.o.splitkeep vim.go.splitkeep = vim.o.splitkeep @@ -6876,7 +6876,7 @@ vim.go.tbs = vim.go.tagbsearch --- match Match case --- smart Ignore case unless an upper case letter is used --- ---- @type string +--- @type 'followic'|'ignore'|'match'|'followscs'|'smart' vim.o.tagcase = "followic" vim.o.tc = vim.o.tagcase vim.bo.tagcase = vim.o.tagcase @@ -7758,7 +7758,7 @@ vim.go.wop = vim.go.wildoptions --- key is never used for the menu. --- This option is not used for <F10>; on Win32. --- ---- @type string +--- @type 'yes'|'menu'|'no' vim.o.winaltkeys = "menu" vim.o.wak = vim.o.winaltkeys vim.go.winaltkeys = vim.o.winaltkeys diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 031b109b38..6316ab2bfc 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -7537,7 +7537,7 @@ function vim.fn.screenstring(row, col) end --- @param stopline? integer --- @param timeout? integer --- @param skip? string|function ---- @return any +--- @return integer function vim.fn.search(pattern, flags, stopline, timeout, skip) end --- Get or update the last search count, like what is displayed diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua index dc37595578..8338c5ead7 100644 --- a/runtime/lua/vim/_options.lua +++ b/runtime/lua/vim/_options.lua @@ -768,7 +768,7 @@ end --- --- --- A special interface |vim.opt| exists for conveniently interacting with list- ---- and map-style option from Lua: It allows accessing them as Lua tables and +--- and map-style options from Lua: It allows accessing them as Lua tables and --- offers object-oriented method for adding and removing entries. --- --- Examples: ~ diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 90f967fe79..6466c7d6e8 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1395,10 +1395,6 @@ M.handlers.signs = { return end - if opts.signs and opts.signs.severity then - diagnostics = filter_by_severity(opts.signs.severity, diagnostics) - end - -- 10 is the default sign priority when none is explicitly specified local priority = opts.signs and opts.signs.priority or 10 local get_priority = severity_to_extmark_priority(priority, opts) @@ -1501,10 +1497,6 @@ M.handlers.underline = { return end - if opts.underline and opts.underline.severity then - diagnostics = filter_by_severity(opts.underline.severity, diagnostics) - end - local ns = M.get_namespace(namespace) if not ns.user_data.underline_ns then ns.user_data.underline_ns = @@ -1565,7 +1557,6 @@ M.handlers.virtual_text = { return end - local severity --- @type vim.diagnostic.SeverityFilter? if opts.virtual_text then if opts.virtual_text.format then diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics) @@ -1576,9 +1567,6 @@ M.handlers.virtual_text = { then diagnostics = prefix_source(diagnostics) end - if opts.virtual_text.severity then - severity = opts.virtual_text.severity - end end local ns = M.get_namespace(namespace) @@ -1590,9 +1578,6 @@ M.handlers.virtual_text = { local virt_text_ns = ns.user_data.virt_text_ns local buffer_line_diagnostics = diagnostic_lines(diagnostics) for line, line_diagnostics in pairs(buffer_line_diagnostics) do - if severity then - line_diagnostics = filter_by_severity(severity, line_diagnostics) - end local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text) if virt_texts then @@ -1797,7 +1782,8 @@ function M.show(namespace, bufnr, diagnostics, opts) for handler_name, handler in pairs(M.handlers) do if handler.show and opts_res[handler_name] then - handler.show(namespace, bufnr, diagnostics, opts_res) + local filtered = filter_by_severity(opts_res[handler_name].severity, diagnostics) + handler.show(namespace, bufnr, filtered, opts_res) end end end diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index dee1bd88ca..1960bca52b 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1574,6 +1574,11 @@ local filename = { ['.gitmodules'] = 'gitconfig', ['.gitattributes'] = 'gitattributes', ['.gitignore'] = 'gitignore', + ['.ignore'] = 'gitignore', + ['.dockerignore'] = 'gitignore', + ['.npmignore'] = 'gitignore', + ['.rgignore'] = 'gitignore', + ['.vscodeignore'] = 'gitignore', ['gitolite.conf'] = 'gitolite', ['git-rebase-todo'] = 'gitrebase', gkrellmrc = 'gkrellmrc', @@ -2369,6 +2374,8 @@ local pattern = { ['%.html%.m4$'] = 'htmlm4', ['^JAM.*%.'] = starsetf('jam'), ['^Prl.*%.'] = starsetf('jam'), + ['^${HOME}/.*/Code/User/.*%.json$'] = 'jsonc', + ['^${HOME}/.*/VSCodium/User/.*%.json$'] = 'jsonc', ['%.properties_..$'] = 'jproperties', ['%.properties_.._..$'] = 'jproperties', ['%.properties_.._.._'] = starsetf('jproperties'), diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 2d989fdbac..30a9951f6a 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -757,7 +757,7 @@ function M.html(_, bufnr) if matchregex( line, - [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content\|{{.*}}]] + [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content]] ) then return 'htmlangular' diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 04a6e43db1..91e06688b3 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -741,4 +741,37 @@ function M.abspath(path) return M.joinpath(cwd, path) end +--- Gets `target` path relative to `base`, or `nil` if `base` is not an ancestor. +--- +--- Example: +--- +--- ```lua +--- vim.fs.relpath('/var', '/var/lib') -- 'lib' +--- vim.fs.relpath('/var', '/usr/bin') -- nil +--- ``` +--- +--- @param base string +--- @param target string +--- @param opts table? Reserved for future use +--- @return string|nil +function M.relpath(base, target, opts) + vim.validate('base', base, 'string') + vim.validate('target', target, 'string') + vim.validate('opts', opts, 'table', true) + + base = vim.fs.normalize(vim.fs.abspath(base)) + target = vim.fs.normalize(vim.fs.abspath(target)) + if base == target then + return '.' + end + + local prefix = '' + if iswin then + prefix, base = split_windows_path(base) + end + base = prefix .. base .. (base ~= '/' and '/' or '') + + return vim.startswith(target, base) and target:sub(#base + 1) or nil +end + return M diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 6dc902489f..ee376f3a11 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -11,18 +11,7 @@ --- < --- Plugin authors are encouraged to write new healthchecks. |health-dev| --- ---- *g:health* ---- g:health This global variable controls the behavior and appearance of the ---- `health` floating window. It should be a dictionary containing the ---- following optional keys: ---- - `style`: string? Determines the display style of the `health` window. ---- Set to `'float'` to enable a floating window. Other ---- styles are not currently supported. ---- ---- Example: >lua ---- vim.g.health = { style = 'float' } ---- ---- Commands *health-commands* +--- COMMANDS *health-commands* --- --- *:che* *:checkhealth* --- :che[ckhealth] Run all healthchecks. @@ -50,6 +39,23 @@ --- :checkhealth vim* --- < --- +--- USAGE *health-usage* +--- +--- Local mappings in the healthcheck buffer: +--- +--- q Closes the window. +--- +--- Global configuration: +--- +--- *g:health* +--- g:health Dictionary with the following optional keys: +--- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in +--- a floating window instead of the default behavior. +--- +--- Example: >lua +--- vim.g.health = { style = 'float' } +--- +--- -------------------------------------------------------------------------------- --- Create a healthcheck *health-dev* --- --- Healthchecks are functions that check the user environment, configuration, or diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5b92926a21..23f4e104d0 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1389,7 +1389,7 @@ end --- |LspAttach| autocommand. Example: --- --- ```lua ---- vim.api.nvim_create_autocommand('LspAttach', { +--- vim.api.nvim_create_autocmd('LspAttach', { --- callback = function(args) --- local client = vim.lsp.get_client_by_id(args.data.client_id) --- if client:supports_method('textDocument/foldingRange') then diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 5d11312c77..a99363d3d6 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -702,14 +702,14 @@ local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'err --- --- @param ... string List to write to the buffer local function err_message(...) - local message = table.concat(vim.iter({ ... }):flatten():totable()) + local chunks = { { table.concat({ ... }) } } if vim.in_fast_event() then vim.schedule(function() - api.nvim_err_writeln(message) + vim.api.nvim_echo(chunks, true, { err = true }) api.nvim_command('redraw') end) else - api.nvim_err_writeln(message) + vim.api.nvim_echo(chunks, true, { err = true }) api.nvim_command('redraw') end end diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 9a879d9f38..8c1f3f10d4 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -208,7 +208,7 @@ end --- @param uri string --- @param client_id? integer ---- @param diagnostics vim.Diagnostic[] +--- @param diagnostics lsp.Diagnostic[] --- @param is_pull boolean local function handle_diagnostics(uri, client_id, diagnostics, is_pull) local fname = vim.uri_to_fname(uri) diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 1945040bda..3779c342e8 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -582,9 +582,8 @@ NSC['window/showMessage'] = function(_, params, ctx) if message_type == protocol.MessageType.Error then err_message('LSP[', client_name, '] ', message) else - --- @type string - local message_type_name = protocol.MessageType[message_type] - api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) + message = ('LSP[%s][%s] %s\n'):format(client_name, protocol.MessageType[message_type], message) + api.nvim_echo({ { message } }, true, { err = true }) end return params end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 5cccb3321f..14633adf0c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -879,8 +879,7 @@ function M.make_floating_popup_options(width, height, opts) col = col + (opts.offset_x or 0), height = height, focusable = opts.focusable, - relative = opts.relative == 'mouse' and 'mouse' - or opts.relative == 'editor' and 'editor' + relative = (opts.relative == 'mouse' or opts.relative == 'editor') and opts.relative or 'cursor', style = 'minimal', width = width, @@ -1433,7 +1432,7 @@ function M._make_floating_popup_size(contents, opts) if vim.tbl_isempty(line_widths) then for _, line in ipairs(contents) do local line_width = vim.fn.strdisplaywidth(line:gsub('%z', '\n')) - height = height + math.ceil(line_width / wrap_at) + height = height + math.max(1, math.ceil(line_width / wrap_at)) end else for i = 1, #contents do diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 24c3f243e5..02b12490af 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -959,7 +959,7 @@ do --- function vim.startswith(s, prefix) --- vim.validate('s', s, 'string') --- vim.validate('prefix', prefix, 'string') - --- ... + --- -- ... --- end --- ``` --- @@ -979,7 +979,7 @@ do --- age={age, 'number'}, --- hobbies={hobbies, 'table'}, --- } - --- ... + --- -- ... --- end --- ``` --- diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 89dc4e289a..0269699dfd 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -61,8 +61,6 @@ function M._create_parser(bufnr, lang, opts) { on_bytes = bytes_cb, on_detach = detach_cb, on_reload = reload_cb, preview = true } ) - self:parse() - return self end @@ -397,6 +395,8 @@ end --- Note: By default, disables regex syntax highlighting, which may be required for some plugins. --- In this case, add `vim.bo.syntax = 'on'` after the call to `start`. --- +--- Note: By default, the highlighter parses code asynchronously, using a segment time of 3ms. +--- --- Example: --- --- ```lua @@ -408,8 +408,8 @@ end --- }) --- ``` --- ----@param bufnr (integer|nil) Buffer to be highlighted (default: current buffer) ----@param lang (string|nil) Language of the parser (default: from buffer filetype) +---@param bufnr integer? Buffer to be highlighted (default: current buffer) +---@param lang string? Language of the parser (default: from buffer filetype) function M.start(bufnr, lang) bufnr = vim._resolve_bufnr(bufnr) local parser = assert(M.get_parser(bufnr, lang, { error = false })) diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index d16013eca2..2777241e9f 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -69,8 +69,8 @@ end ---@param info TS.FoldInfo ---@param srow integer? ---@param erow integer? 0-indexed, exclusive ----@param parse_injections? boolean -local function compute_folds_levels(bufnr, info, srow, erow, parse_injections) +---@param callback function? +local function compute_folds_levels(bufnr, info, srow, erow, callback) srow = srow or 0 erow = erow or api.nvim_buf_line_count(bufnr) @@ -79,104 +79,112 @@ local function compute_folds_levels(bufnr, info, srow, erow, parse_injections) return end - parser:parse(parse_injections and { srow, erow } or nil) - - local enter_counts = {} ---@type table<integer, integer> - local leave_counts = {} ---@type table<integer, integer> - local prev_start = -1 - local prev_stop = -1 - - parser:for_each_tree(function(tree, ltree) - local query = ts.query.get(ltree:lang(), 'folds') - if not query then + parser:parse(nil, function(_, trees) + if not trees then return end - -- Collect folds starting from srow - 1, because we should first subtract the folds that end at - -- srow - 1 from the level of srow - 1 to get accurate level of srow. - for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do - for id, nodes in pairs(match) do - if query.captures[id] == 'fold' then - local range = ts.get_range(nodes[1], bufnr, metadata[id]) - local start, _, stop, stop_col = Range.unpack4(range) - - if #nodes > 1 then - -- assumes nodes are ordered by range - local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id]) - local _, _, end_stop, end_stop_col = Range.unpack4(end_range) - stop = end_stop - stop_col = end_stop_col - end + local enter_counts = {} ---@type table<integer, integer> + local leave_counts = {} ---@type table<integer, integer> + local prev_start = -1 + local prev_stop = -1 - if stop_col == 0 then - stop = stop - 1 - end + parser:for_each_tree(function(tree, ltree) + local query = ts.query.get(ltree:lang(), 'folds') + if not query then + return + end - local fold_length = stop - start + 1 - - -- Fold only multiline nodes that are not exactly the same as previously met folds - -- Checking against just the previously found fold is sufficient if nodes - -- are returned in preorder or postorder when traversing tree - if - fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop) - then - enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1 - leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1 - prev_start = start - prev_stop = stop + -- Collect folds starting from srow - 1, because we should first subtract the folds that end at + -- srow - 1 from the level of srow - 1 to get accurate level of srow. + for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do + for id, nodes in pairs(match) do + if query.captures[id] == 'fold' then + local range = ts.get_range(nodes[1], bufnr, metadata[id]) + local start, _, stop, stop_col = Range.unpack4(range) + + if #nodes > 1 then + -- assumes nodes are ordered by range + local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id]) + local _, _, end_stop, end_stop_col = Range.unpack4(end_range) + stop = end_stop + stop_col = end_stop_col + end + + if stop_col == 0 then + stop = stop - 1 + end + + local fold_length = stop - start + 1 + + -- Fold only multiline nodes that are not exactly the same as previously met folds + -- Checking against just the previously found fold is sufficient if nodes + -- are returned in preorder or postorder when traversing tree + if + fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop) + then + enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1 + leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1 + prev_start = start + prev_stop = stop + end end end end - end - end) + end) - local nestmax = vim.wo.foldnestmax - local level0_prev = info.levels0[srow] or 0 - local leave_prev = leave_counts[srow] or 0 - - -- We now have the list of fold opening and closing, fill the gaps and mark where fold start - for lnum = srow + 1, erow do - local enter_line = enter_counts[lnum] or 0 - local leave_line = leave_counts[lnum] or 0 - local level0 = level0_prev - leave_prev + enter_line - - -- Determine if it's the start/end of a fold - -- NB: vim's fold-expr interface does not have a mechanism to indicate that - -- two (or more) folds start at this line, so it cannot distinguish between - -- ( \n ( \n )) \n (( \n ) \n ) - -- versus - -- ( \n ( \n ) \n ( \n ) \n ) - -- Both are represented by ['>1', '>2', '2', '>2', '2', '1'], and - -- vim interprets as the second case. - -- If it did have such a mechanism, (clamped - clamped_prev) - -- would be the correct number of starts to pass on. - local adjusted = level0 ---@type integer - local prefix = '' - if enter_line > 0 then - prefix = '>' - if leave_line > 0 then - -- If this line ends a fold f1 and starts a fold f2, then move f1's end to the previous line - -- so that f2 gets the correct level on this line. This may reduce the size of f1 below - -- foldminlines, but we don't handle it for simplicity. - adjusted = level0 - leave_line - leave_line = 0 + local nestmax = vim.wo.foldnestmax + local level0_prev = info.levels0[srow] or 0 + local leave_prev = leave_counts[srow] or 0 + + -- We now have the list of fold opening and closing, fill the gaps and mark where fold start + for lnum = srow + 1, erow do + local enter_line = enter_counts[lnum] or 0 + local leave_line = leave_counts[lnum] or 0 + local level0 = level0_prev - leave_prev + enter_line + + -- Determine if it's the start/end of a fold + -- NB: vim's fold-expr interface does not have a mechanism to indicate that + -- two (or more) folds start at this line, so it cannot distinguish between + -- ( \n ( \n )) \n (( \n ) \n ) + -- versus + -- ( \n ( \n ) \n ( \n ) \n ) + -- Both are represented by ['>1', '>2', '2', '>2', '2', '1'], and + -- vim interprets as the second case. + -- If it did have such a mechanism, (clamped - clamped_prev) + -- would be the correct number of starts to pass on. + local adjusted = level0 ---@type integer + local prefix = '' + if enter_line > 0 then + prefix = '>' + if leave_line > 0 then + -- If this line ends a fold f1 and starts a fold f2, then move f1's end to the previous line + -- so that f2 gets the correct level on this line. This may reduce the size of f1 below + -- foldminlines, but we don't handle it for simplicity. + adjusted = level0 - leave_line + leave_line = 0 + end end - end - -- Clamp at foldnestmax. - local clamped = adjusted - if adjusted > nestmax then - prefix = '' - clamped = nestmax - end + -- Clamp at foldnestmax. + local clamped = adjusted + if adjusted > nestmax then + prefix = '' + clamped = nestmax + end - -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels(). - info.levels0[lnum] = adjusted - info.levels[lnum] = prefix .. tostring(clamped) + -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels(). + info.levels0[lnum] = adjusted + info.levels[lnum] = prefix .. tostring(clamped) - leave_prev = leave_line - level0_prev = adjusted - end + leave_prev = leave_line + level0_prev = adjusted + end + + if callback then + callback() + end + end) end local M = {} @@ -267,6 +275,8 @@ local function on_changedtree(bufnr, foldinfo, tree_changes) schedule_if_loaded(bufnr, function() local srow_upd, erow_upd ---@type integer?, integer? local max_erow = api.nvim_buf_line_count(bufnr) + -- TODO(ribru17): Replace this with a proper .all() awaiter once #19624 is resolved + local iterations = 0 for _, change in ipairs(tree_changes) do local srow, _, erow, ecol = Range.unpack4(change) -- If a parser doesn't have any ranges explicitly set, treesitter will @@ -280,12 +290,14 @@ local function on_changedtree(bufnr, foldinfo, tree_changes) end -- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit. srow = math.max(srow - vim.wo.foldminlines, 0) - compute_folds_levels(bufnr, foldinfo, srow, erow) srow_upd = srow_upd and math.min(srow_upd, srow) or srow erow_upd = erow_upd and math.max(erow_upd, erow) or erow - end - if #tree_changes > 0 then - foldinfo:foldupdate(bufnr, srow_upd, erow_upd) + compute_folds_levels(bufnr, foldinfo, srow, erow, function() + iterations = iterations + 1 + if iterations == #tree_changes then + foldinfo:foldupdate(bufnr, srow_upd, erow_upd) + end + end) end end) end @@ -343,8 +355,9 @@ local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, foldinfo.on_bytes_range = nil -- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit. srow = math.max(srow - vim.wo.foldminlines, 0) - compute_folds_levels(bufnr, foldinfo, srow, erow) - foldinfo:foldupdate(bufnr, srow, erow) + compute_folds_levels(bufnr, foldinfo, srow, erow, function() + foldinfo:foldupdate(bufnr, srow, erow) + end) end) end end @@ -401,9 +414,10 @@ api.nvim_create_autocmd('OptionSet', { for _, bufnr in ipairs(bufs) do foldinfos[bufnr] = FoldInfo.new(bufnr) api.nvim_buf_call(bufnr, function() - compute_folds_levels(bufnr, foldinfos[bufnr]) + compute_folds_levels(bufnr, foldinfos[bufnr], nil, nil, function() + foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr)) + end) end) - foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr)) end end, }) diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 96503c38ea..be138885d5 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -69,6 +69,7 @@ end ---@field private _queries table<string,vim.treesitter.highlighter.Query> ---@field tree vim.treesitter.LanguageTree ---@field private redraw_count integer +---@field parsing boolean true if we are parsing asynchronously local TSHighlighter = { active = {}, } @@ -147,8 +148,6 @@ function TSHighlighter.new(tree, opts) vim.opt_local.spelloptions:append('noplainbuffer') end) - self.tree:parse() - return self end @@ -384,19 +383,23 @@ function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _) end ---@private ----@param _win integer ---@param buf integer ---@param topline integer ---@param botline integer -function TSHighlighter._on_win(_, _win, buf, topline, botline) +function TSHighlighter._on_win(_, _, buf, topline, botline) local self = TSHighlighter.active[buf] - if not self then + if not self or self.parsing then return false end - self.tree:parse({ topline, botline + 1 }) - self:prepare_highlight_states(topline, botline + 1) + self.parsing = self.tree:parse({ topline, botline + 1 }, function(_, trees) + if trees and self.parsing then + self.parsing = false + api.nvim__redraw({ buf = buf, valid = false, flush = false }) + end + end) == nil self.redraw_count = self.redraw_count + 1 - return true + self:prepare_highlight_states(topline, botline) + return #self._highlight_states > 0 end api.nvim_set_decoration_provider(ns, { diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 446051dfd7..238a078703 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -133,8 +133,9 @@ function M.add(lang, opts) path = paths[1] end - return loadparser(path, lang, symbol_name) or nil, - string.format('Cannot load parser %s for language "%s"', path, lang) + local res = loadparser(path, lang, symbol_name) + return res, + res == nil and string.format('Cannot load parser %s for language "%s"', path, lang) or nil end --- @param x string|string[] diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 330eb45749..945a2301a9 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -44,6 +44,8 @@ local query = require('vim.treesitter.query') local language = require('vim.treesitter.language') local Range = require('vim.treesitter._range') +local default_parse_timeout_ms = 3 + ---@alias TSCallbackName ---| 'changedtree' ---| 'bytes' @@ -76,6 +78,10 @@ local TSCallbackNames = { ---@field private _injections_processed boolean ---@field private _opts table Options ---@field private _parser TSParser Parser for language +---Table of regions for which the tree is currently running an async parse +---@field private _ranges_being_parsed table<string, boolean> +---Table of callback queues, keyed by each region for which the callbacks should be run +---@field private _cb_queues table<string, fun(err?: string, trees?: table<integer, TSTree>)[]> ---@field private _has_regions boolean ---@field private _regions table<integer, Range6[]>? ---List of regions this tree should manage and parse. If nil then regions are @@ -130,6 +136,8 @@ function LanguageTree.new(source, lang, opts) _injections_processed = false, _valid = false, _parser = vim._create_ts_parser(lang), + _ranges_being_parsed = {}, + _cb_queues = {}, _callbacks = {}, _callbacks_rec = {}, } @@ -232,6 +240,7 @@ end ---@param reload boolean|nil function LanguageTree:invalidate(reload) self._valid = false + self._parser:reset() -- buffer was reloaded, reparse all trees if reload then @@ -334,10 +343,12 @@ end --- @private --- @param range boolean|Range? +--- @param timeout integer? --- @return Range6[] changes --- @return integer no_regions_parsed --- @return number total_parse_time -function LanguageTree:_parse_regions(range) +--- @return boolean finished whether async parsing still needs time +function LanguageTree:_parse_regions(range, timeout) local changes = {} local no_regions_parsed = 0 local total_parse_time = 0 @@ -357,9 +368,14 @@ function LanguageTree:_parse_regions(range) ) then self._parser:set_included_ranges(ranges) + self._parser:set_timeout(timeout and timeout * 1000 or 0) -- ms -> micros local parse_time, tree, tree_changes = tcall(self._parser.parse, self._parser, self._trees[i], self._source, true) + if not tree then + return changes, no_regions_parsed, total_parse_time, false + end + -- Pass ranges if this is an initial parse local cb_changes = self._trees[i] and tree_changes or tree:included_ranges(true) @@ -373,7 +389,7 @@ function LanguageTree:_parse_regions(range) end end - return changes, no_regions_parsed, total_parse_time + return changes, no_regions_parsed, total_parse_time, true end --- @private @@ -409,6 +425,82 @@ function LanguageTree:_add_injections() return query_time end +--- @param range boolean|Range? +--- @return string +local function range_to_string(range) + return type(range) == 'table' and table.concat(range, ',') or tostring(range) +end + +--- @private +--- @param range boolean|Range? +--- @param callback fun(err?: string, trees?: table<integer, TSTree>) +function LanguageTree:_push_async_callback(range, callback) + local key = range_to_string(range) + self._cb_queues[key] = self._cb_queues[key] or {} + local queue = self._cb_queues[key] + queue[#queue + 1] = callback +end + +--- @private +--- @param range boolean|Range? +--- @param err? string +--- @param trees? table<integer, TSTree> +function LanguageTree:_run_async_callbacks(range, err, trees) + local key = range_to_string(range) + for _, cb in ipairs(self._cb_queues[key]) do + cb(err, trees) + end + self._ranges_being_parsed[key] = false + self._cb_queues[key] = {} +end + +--- Run an asynchronous parse, calling {on_parse} when complete. +--- +--- @private +--- @param range boolean|Range? +--- @param on_parse fun(err?: string, trees?: table<integer, TSTree>) +--- @return table<integer, TSTree>? trees the list of parsed trees, if parsing completed synchronously +function LanguageTree:_async_parse(range, on_parse) + self:_push_async_callback(range, on_parse) + + -- If we are already running an async parse, just queue the callback. + local range_string = range_to_string(range) + if not self._ranges_being_parsed[range_string] then + self._ranges_being_parsed[range_string] = true + else + return + end + + local buf = vim.b[self._source] + local ct = buf.changedtick + local total_parse_time = 0 + local redrawtime = vim.o.redrawtime + local timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ms or nil + + local function step() + -- If buffer was changed in the middle of parsing, reset parse state + if buf.changedtick ~= ct then + ct = buf.changedtick + total_parse_time = 0 + end + + local parse_time, trees, finished = tcall(self._parse, self, range, timeout) + total_parse_time = total_parse_time + parse_time + + if finished then + self:_run_async_callbacks(range, nil, trees) + return trees + elseif total_parse_time > redrawtime then + self:_run_async_callbacks(range, 'TIMEOUT', nil) + return nil + else + vim.schedule(step) + end + end + + return step() +end + --- Recursively parse all regions in the language tree using |treesitter-parsers| --- for the corresponding languages and run injection queries on the parsed trees --- to determine whether child trees should be created and parsed. @@ -420,11 +512,33 @@ end --- Set to `true` to run a complete parse of the source (Note: Can be slow!) --- Set to `false|nil` to only parse regions with empty ranges (typically --- only the root tree without injections). ---- @return table<integer, TSTree> -function LanguageTree:parse(range) +--- @param on_parse fun(err?: string, trees?: table<integer, TSTree>)? Function invoked when parsing completes. +--- When provided and `vim.g._ts_force_sync_parsing` is not set, parsing will run +--- asynchronously. The first argument to the function is a string respresenting the error type, +--- in case of a failure (currently only possible for timeouts). The second argument is the list +--- of trees returned by the parse (upon success), or `nil` if the parse timed out (determined +--- by 'redrawtime'). +--- +--- If parsing was still able to finish synchronously (within 3ms), `parse()` returns the list +--- of trees. Otherwise, it returns `nil`. +--- @return table<integer, TSTree>? +function LanguageTree:parse(range, on_parse) + if on_parse then + return self:_async_parse(range, on_parse) + end + local trees, _ = self:_parse(range) + return trees +end + +--- @private +--- @param range boolean|Range|nil +--- @param timeout integer? +--- @return table<integer, TSTree> trees +--- @return boolean finished +function LanguageTree:_parse(range, timeout) if self:is_valid() then self:_log('valid') - return self._trees + return self._trees, true end local changes --- @type Range6[]? @@ -433,10 +547,15 @@ function LanguageTree:parse(range) local no_regions_parsed = 0 local query_time = 0 local total_parse_time = 0 + local is_finished --- @type boolean -- At least 1 region is invalid if not self:is_valid(true) then - changes, no_regions_parsed, total_parse_time = self:_parse_regions(range) + changes, no_regions_parsed, total_parse_time, is_finished = self:_parse_regions(range, timeout) + timeout = timeout and math.max(timeout - total_parse_time, 0) + if not is_finished then + return self._trees, is_finished + end -- Need to run injections when we parsed something if no_regions_parsed > 0 then self._injections_processed = false @@ -457,10 +576,17 @@ function LanguageTree:parse(range) }) for _, child in pairs(self._children) do - child:parse(range) + if timeout == 0 then + return self._trees, false + end + local ctime, _, child_finished = tcall(child._parse, child, range, timeout) + timeout = timeout and math.max(timeout - ctime, 0) + if not child_finished then + return self._trees, child_finished + end end - return self._trees + return self._trees, true end --- Invokes the callback for each |LanguageTree| recursively. @@ -907,6 +1033,7 @@ function LanguageTree:_edit( ) end + self._parser:reset() self._regions = nil local changed_range = { diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 1fc001b39f..66ab0d52f0 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -262,6 +262,7 @@ local explicit_queries = setmetatable({}, { ---@param query_name string Name of the query (e.g., "highlights") ---@param text string Query text (unparsed). function M.set(lang, query_name, text) + M.get:clear(lang, query_name) explicit_queries[lang][query_name] = M.parse(lang, text) end @@ -284,7 +285,15 @@ M.get = memoize('concat-2', function(lang, query_name) end return M.parse(lang, query_string) -end) +end, false) + +api.nvim_create_autocmd('OptionSet', { + pattern = { 'runtimepath' }, + group = api.nvim_create_augroup('ts_query_cache_reset', { clear = true }), + callback = function() + M.get:clear() + end, +}) --- Parses a {query} string and returns a `Query` object (|lua-treesitter-query|), which can be used --- to search the tree for the query patterns (via |Query:iter_captures()|, |Query:iter_matches()|), @@ -292,7 +301,7 @@ end) --- - `captures`: a list of unique capture names defined in the query (alias: `info.captures`). --- - `info.patterns`: information about predicates. --- ---- Example (select the code then run `:'<,'>lua` to try it): +--- Example (to try it, use `yxx` or select the code then run `:'<,'>lua`): --- ```lua --- local query = vim.treesitter.query.parse('vimdoc', [[ --- ; query @@ -316,7 +325,7 @@ M.parse = memoize('concat-2', function(lang, query) assert(language.add(lang)) local ts_query = vim._ts_parse_query(lang, query) return Query.new(lang, ts_query) -end) +end, false) --- Implementations of predicates that can optionally be prefixed with "any-". --- @@ -904,8 +913,8 @@ end ---@param start? integer Starting line for the search. Defaults to `node:start()`. ---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`. --- ----@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch): ---- capture id, capture node, metadata, match +---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree): +--- capture id, capture node, metadata, match, tree --- ---@note Captures are only returned if the query pattern of a specific capture contained predicates. function Query:iter_captures(node, source, start, stop) @@ -915,6 +924,8 @@ function Query:iter_captures(node, source, start, stop) start, stop = value_or_node_range(start, stop, node) + -- Copy the tree to ensure it is valid during the entire lifetime of the iterator + local tree = node:tree():copy() local cursor = vim._create_ts_querycursor(node, self.query, start, stop, { match_limit = 256 }) -- For faster checks that a match is not in the cache. @@ -961,7 +972,7 @@ function Query:iter_captures(node, source, start, stop) match_cache[match_id] = metadata end - return capture, captured_node, metadata, match + return capture, captured_node, metadata, match, tree end return iter end @@ -983,7 +994,7 @@ end --- -- `node` was captured by the `name` capture in the match --- --- local node_data = metadata[id] -- Node level metadata ---- ... use the info here ... +--- -- ... use the info here ... --- end --- end --- end @@ -1002,7 +1013,7 @@ end --- (last) node instead of the full list of matching nodes. This option is only for backward --- compatibility and will be removed in a future release. --- ----@return (fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata): pattern id, match, metadata +---@return (fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata, TSTree): pattern id, match, metadata, tree function Query:iter_matches(node, source, start, stop, opts) opts = opts or {} opts.match_limit = opts.match_limit or 256 @@ -1013,6 +1024,8 @@ function Query:iter_matches(node, source, start, stop, opts) start, stop = value_or_node_range(start, stop, node) + -- Copy the tree to ensure it is valid during the entire lifetime of the iterator + local tree = node:tree():copy() local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts) local function iter() @@ -1050,7 +1063,7 @@ function Query:iter_matches(node, source, start, stop, opts) end -- TODO(lewis6991): create a new function that returns {match, metadata} - return pattern_i, captures, metadata + return pattern_i, captures, metadata, tree end return iter end diff --git a/runtime/syntax/lyrics.vim b/runtime/syntax/lyrics.vim index fd127988f2..48a5b1171c 100644 --- a/runtime/syntax/lyrics.vim +++ b/runtime/syntax/lyrics.vim @@ -2,7 +2,7 @@ " Language: LyRiCs " Maintainer: ObserverOfTime <chronobserver@disroot.org> " Filenames: *.lrc -" Last Change: 2024 Sep 20 +" Last Change: 2025 Jan 13 if exists('b:current_syntax') finish @@ -23,7 +23,7 @@ syn match lrcTagName contained nextgroup=lrcTagValue syn match lrcTagValue /:\zs.\+\ze\]/ contained " Lyrics -syn match lrcLyricTime /^\s*\(\[\d\d:\d\d\.\d\d\]\)\+/ +syn match lrcLyricTime /^\s*\(\[\d\d:\d\d\.\d\d\d\?\]\)\+/ \ contains=lrcNumber nextgroup=lrcLyricLine syn match lrcLyricLine /.*$/ contained contains=lrcWordTime,@Spell syn match lrcWordTime /<\d\d:\d\d\.\d\d>/ contained contains=lrcNumber,@NoSpell diff --git a/runtime/syntax/tiasm.vim b/runtime/syntax/tiasm.vim index bdadc4a0a7..c79596bdfe 100644 --- a/runtime/syntax/tiasm.vim +++ b/runtime/syntax/tiasm.vim @@ -99,4 +99,4 @@ hi def link tiasmIdentifier Identifier hi def link tiasmType Type hi def link tiasmFunction Function -let b:current_syntax = "lineartiasm" +let b:current_syntax = "tiasm" diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 12798201e2..edc69b907c 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -553,19 +553,21 @@ syn region vimPatSepZone oneline contained matchgroup=vimPatSepZ start="\\%\ syn region vimPatRegion contained transparent matchgroup=vimPatSepR start="\\[z%]\=(" end="\\)" contains=@vimSubstList oneline syn match vimNotPatSep contained "\\\\" syn cluster vimStringGroup contains=vimEscape,vimEscapeBrace,vimPatSep,vimNotPatSep,vimPatSepErr,vimPatSepZone,@Spell -syn region vimString oneline keepend start=+[^a-zA-Z>\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup extend -syn region vimString oneline keepend start=+[^a-zA-Z>\\@]'+lc=1 end=+'+ extend +syn region vimString oneline keepend matchgroup=vimString start=+[^a-zA-Z>\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup extend +syn region vimString oneline matchgroup=vimString start=+[^a-zA-Z>\\@]'+lc=1 end=+'+ contains=vimQuoteEscape extend "syn region vimString oneline start="\s/\s*\A"lc=1 skip="\\\\\|\\+" end="/" contains=@vimStringGroup " see tst45.vim syn match vimString contained +"[^"]*\\$+ skipnl nextgroup=vimStringCont syn match vimStringCont contained +\(\\\\\|.\)\{-}[^\\]"+ + syn match vimEscape contained "\\." " syn match vimEscape contained +\\[befnrt\"]+ syn match vimEscape contained "\\\o\{1,3}\|\\[xX]\x\{1,2}\|\\u\x\{1,4}\|\\U\x\{1,8}" syn match vimEscape contained "\\<" contains=vimNotation syn match vimEscape contained "\\<\*[^>]*>\=>" +syn match vimQuoteEscape contained "''" -syn region vimString oneline start=+$'+ skip=+''+ end=+'+ contains=@vimStringInterpolation extend -syn region vimString oneline start=+$"+ end=+"+ contains=@vimStringGroup,@vimStringInterpolation extend +syn region vimString oneline matchgroup=vimString start=+$'+ skip=+''+ end=+'+ contains=vimQuoteEscape,@vimStringInterpolation extend +syn region vimString oneline matchgroup=vimString start=+$"+ end=+"+ contains=@vimStringGroup,@vimStringInterpolation extend syn region vimStringInterpolationExpr oneline contained matchgroup=vimSep start=+{+ end=+}+ contains=@vimExprList syn match vimStringInterpolationBrace contained "{{" syn match vimStringInterpolationBrace contained "}}" @@ -1399,6 +1401,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimPattern Type hi def link vimPlainMark vimMark hi def link vimPlainRegister vimRegister + hi def link vimQuoteEscape vimEscape hi def link vimRegister SpecialChar hi def link vimScriptDelim Comment hi def link vimSearchDelim Statement |