diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 5f52d01668..8e40505a7e 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -171,7 +171,7 @@ Put this in `uppercase.vim` and run: >bash nvim --headless --cmd "source uppercase.vim" ============================================================================== -5. Using a prompt buffer *prompt-buffer* +5. Using a prompt buffer *repl* *prompt-buffer* Prompt buffers provide a "prompt" interface: they are like regular buffers, except only the last section of the buffer is editable, and the user can @@ -221,8 +221,12 @@ Any command that starts Insert mode, such as "a", "i", "A" and "I", will move the cursor to the last line. "A" will move to the end of the line, "I" to the start of the line. -Example: start a shell in the background and prompt for the next shell -command, displaying shell output above the prompt: >lua + *emacs-eshell* +Example: The following code snippet implements a "shell buffer" similar to +Emacs "eshell". Because the interface is a prompt-buffer, you can edit the +prompt using normal Nvim commands, unlike a |:terminal| shell. Each prompt is +sent to the shell process started by |jobstart()|, and appended to the buffer +when output is received: >lua local shell_job diff --git a/runtime/doc/dev.txt b/runtime/doc/dev.txt index e5ea15ae0a..115f2bfb01 100644 --- a/runtime/doc/dev.txt +++ b/runtime/doc/dev.txt @@ -275,7 +275,8 @@ Docstring format: - {somefield}? (integer) Documentation for some field < -- Files declared as `@meta` are only used for typing and documentation (similar to "*.d.ts" typescript files). +- Files declared as `@meta` are only used for typing and documentation + (similar to "*.d.ts" typescript files). Example: the help for |vim.paste()| is generated from a docstring decorating vim.paste in runtime/lua/vim/_core/editor.lua like this: > @@ -315,10 +316,11 @@ have help tags. It runs in CI, but you can also run it locally: > < ------------------------------------------------------------------------------ -Stdlib design *dev-lua* +Stdlib *dev-lua* -See also |dev-naming|. +GUIDANCE +- Lua names generally should conform to |dev-naming|. - Keep the core Lua modules |lua-stdlib| simple. Avoid elaborate OOP or pseudo-OOP designs. Plugin authors just want functions to call, not a big, fancy inheritance hierarchy. @@ -337,7 +339,8 @@ See also |dev-naming|. - Mimic the pairs() or ipairs() interface if the function is intended for use in a |for-in| loop. - *dev-error-patterns* +ERRORS *dev-error-patterns* + To communicate failure to a consumer, choose from these patterns (in order of preference): 1. `retval, errmsg` @@ -359,9 +362,37 @@ preference): - High-level / application-level messages. End-user invokes these directly. -INTERFACE CONVENTIONS *dev-patterns* +------------------------------------------------------------------------------ +API *dev-api* -Where applicable, these patterns apply to _both_ Lua and the API: +Where applicable, this section applies to both Lua stdlib and RPC API. + +GUIDANCE + +- When adding an API, check the following: + - What precedents did you draw from? How does your solution compare to them? + - Does your new API allow future expansion? How? Or why not? + - Is the new API similar to existing APIs? Do we need to deprecate the old ones? + - Did you cross-reference related concepts in the docs? +- Avoid "mutually exclusive" parameters (via constraints or limitations, if + necessary). For example nvim_create_autocmd() has mutually exclusive + "callback" and "command" args; but the "command" arg could be eliminated by + simply not supporting Vimscript function names, and treating a string + "callback" arg as an Ex command (which can call Vimscript functions). The + "buffer" arg could also be eliminated by treating a number "pattern" as + a buffer number. +- Avoid functions that depend on cursor position, current buffer, etc. Instead + the function should take a position parameter, buffer parameter, etc. + +WHERE THINGS GO + +- API (libnvim/RPC): exposes low-level internals, or fundamental things (such + as `nvim_exec_lua()`) needed by clients or C consumers. +- Lua stdlib = high-level functionality that builds on top of the API. + +See also |dev-layout|. + +API PATTERNS *dev-api-patterns* - When accepting a buffer id, etc., 0 means "current buffer", nil means "all buffers". Likewise for window id, tabpage id, etc. @@ -375,11 +406,9 @@ Where applicable, these patterns apply to _both_ Lua and the API: `function(, cb())` => `function() -> `. - Example: >lua -- ✅ OK: - filter(…, opts, function() … end) + filter(…, opts, fn) -- ❌ NO: - filter(function() … end, …, opts) - -- ❌ NO: - filter(…, function() … end, opts) + filter(…, fn, opts) - Expose a `config(opts)` function if the module accepts user configuration. If `opts` is omitted (or `nil`) it returns the current configuration. - Example: See |vim.diagnostic.config()|. @@ -406,32 +435,7 @@ Where applicable, these patterns apply to _both_ Lua and the API: < ------------------------------------------------------------------------------ -API design *dev-api* - -See also |dev-naming|. - -- When adding an API, check the following: - - What precedents did you draw from? How does your solution compare to them? - - Does your new API allow future expansion? How? Or why not? - - Is the new API similar to existing APIs? Do we need to deprecate the old ones? - - Did you cross-reference related concepts in the docs? -- Avoid "mutually exclusive" parameters--via constraints or limitations, if - necessary. For example nvim_create_autocmd() has mutually exclusive - "callback" and "command" args; but the "command" arg could be eliminated by - simply not supporting Vimscript function names, and treating a string - "callback" arg as an Ex command (which can call Vimscript functions). The - "buffer" arg could also be eliminated by treating a number "pattern" as - a buffer number. -- Avoid functions that depend on cursor position, current buffer, etc. Instead - the function should take a position parameter, buffer parameter, etc. - -WHERE THINGS GO - -- API (libnvim/RPC): exposes low-level internals, or fundamental things (such - as `nvim_exec_lua()`) needed by clients or C consumers. -- Lua stdlib = high-level functionality that builds on top of the API. - -NAMING GUIDELINES *dev-naming* +NAMING *dev-naming* Naming is exceedingly important: the name of a thing is the primary interface for uses it, discusses it, searches for it, shares it... Consistent @@ -456,6 +460,7 @@ Use existing common {verb} names (actions) if possible: user-initiated and without error. (Compare "abort", which cancels and signals error/failure.) - clear: Clears state but does not destroy the container + - close: Releases/destroys a resource. - create: Creates a new (non-trivial) thing (TODO: rename to "def"?) - del: Deletes a thing (or group of things) - detach: Dispose attached listener (TODO: rename to "un"?) @@ -484,6 +489,7 @@ Use existing common {verb} names (actions) if possible: Do NOT use these deprecated verbs: - contains: Prefer "has". + - destroy: Prefer "close". - disable: Prefer `enable(enable: boolean)`. - exit: Prefer "cancel" (or "stop" if appropriate). - is_disabled: Prefer `!is_enabled()`. @@ -511,6 +517,23 @@ Do NOT use these deprecated nouns: - command Use "cmd" instead - window Use "win" instead + *continuation* +A "continuation" is implemented as a callback function that returns the result +of an async function and is called exactly once. Often accepts `err` as the +first parameter, to avoid erroring on the main thread. Example: > + foo(…, callback: fun(err, …)) +Use the name `callback` only for a continuation. All other callbacks and event +handlers should be named with the `on_…` convention. |dev-name-events| + + *dev-namespace-name* +Use namespace names like `nvim.foo.bar`: > + vim.api.nvim_create_namespace('nvim.lsp.codelens') +< + + *dev-augroup-name* +Use autocommand group names like `nvim.foo.bar`: > + vim.api.nvim_create_augroup('nvim.treesitter.dev') +< *dev-name-events* Use the "on_" prefix to name event-handling callbacks and also the interface for "registering" such handlers (on_key). The dual nature is acceptable to avoid @@ -519,21 +542,12 @@ a confused collection of naming conventions for these related concepts. Editor |events| (autocommands) are historically named like: > {Noun}{Event} -Use this format to name API (RPC) events: > - nvim_{noun}_{event-name}_event - -Example: > - nvim_buf_changedtick_event +RPC API events use this format: > + nvim_{noun}_{event}_event (example: nvim_buf_changedtick_event) < - *continuation* -A "continuation" is implemented as a callback function that returns the result -of an async function and is called exactly once. Often accepts `err` as the -first parameter, to avoid erroring on the main thread. Example: > - foo(…, callback: fun(err, …)) -Use the name `callback` only for a continuation. All other callbacks and event -handlers should be named with the |dev-name-events| "on_…" convention. - *dev-api-name* +RPC API NAMING *dev-api-name* + Use this format to name new RPC |API| functions: > nvim_{topic}_{verb}_{arbitrary-qualifiers} @@ -552,19 +566,8 @@ be a parameter (typically manifest as mutually-exclusive buf/win/… flags like - Example: `nvim_buf_del_mark` acts on a `Buffer` object (the first parameter) and uses the "del" {verb}. - *dev-namespace-name* -Use namespace names like `nvim.foo.bar`: > - vim.api.nvim_create_namespace('nvim.lsp.codelens') -< - - *dev-augroup-name* -Use autocommand group names like `nvim.foo.bar`: > - vim.api.nvim_create_augroup('nvim.treesitter.dev') -< - -INTERFACE PATTERNS *dev-api-patterns* - -Prefer adding a single `nvim_{topic}_{verb}_…` interface for a given topic. +The RPC API doesn't have "modules" so its functions are grouped by a naming +convention. Prefer the `nvim_{topic}_{verb}_…` convention where possible. Example: > @@ -614,6 +617,34 @@ recommended (compare these 12(!) functions to the above 3 functions): > nvim_win_get_ns(…) nvim_tabpage_get_ns(…) + +------------------------------------------------------------------------------ +Interface patterns *dev-interface-patterns* + +EX COMMANDS *dev-excmd* + +Prefer "flat" Ex commands (`:foobar`) instead of "subcommands" (`:foo bar`). +except when the benefits below outweigh the costs. + +Pros of subcommands: +- Useful when subcommands are discovered _at runtime_ (e.g. `:checkhealth foo`). + Plugins can extend the dispatcher by injecting subcommands. +- `:Foo` completes only the `Foo` subcommands, whereas completing + a flat name matches every command sharing the prefix (e.g. `:FooBar` and + `:FootballBar`). +- Completion can vary dynamically: e.g. `:lsp ` can hide `stop` if no + client is running. (But this can be an antifeature, because it makes the + behavior less predictable.) + +Cons of subcommands: +- Less harmonious with fuzzy pickers. Pickers can easily list `:foobar`, + whereas enumerating subcommands requires special logic. And in a picker, + `:foo bar` is often two "steps". +- Adds a step when tab-completing. +- Awkward to support `[!]`, ranges, `[count]`, etc., per-subcommand. +- Implementation cost: a dispatcher with custom completion. + + ------------------------------------------------------------------------------ API client *dev-api-client* @@ -656,7 +687,7 @@ Examples of API-client package names: - ❌ NO: python-client - ❌ NO: neovim_ -API CLIENT IMPLEMENTATION GUIDELINES +API CLIENT IMPLEMENTATION - Separate the transport layer from the rest of the library. |rpc-connecting| - Use a MessagePack library that implements at least version 5 of the @@ -680,7 +711,7 @@ API CLIENT IMPLEMENTATION GUIDELINES ------------------------------------------------------------------------------ -EXTERNAL UI *dev-ui* +External UI *dev-ui* External UIs should be aware of the |api-contract|. In particular, future versions of Nvim may add new items to existing events. The API is strongly diff --git a/runtime/doc/dev_arch.txt b/runtime/doc/dev_arch.txt index 82840239c0..68778dcdd9 100644 --- a/runtime/doc/dev_arch.txt +++ b/runtime/doc/dev_arch.txt @@ -20,7 +20,7 @@ The purpose of this document is to give: Type |gO| to see the table of contents. ============================================================================== -Project layout: where things go +Project layout: where things go *dev-layout* C CODE diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index bc702aa236..973f7a033e 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -29,28 +29,25 @@ Follow these steps to get LSP features: https://microsoft.github.io/language-server-protocol/implementors/servers/ 2. Define a new config |lsp-new-config| (or install https://github.com/neovim/nvim-lspconfig). Example: >lua - vim.lsp.config['lua_ls'] = { + vim.lsp.config['emmylua_ls'] = { -- Command and arguments to start the server. - cmd = { 'lua-language-server' }, + cmd = { 'emmylua_ls' }, -- Filetypes to automatically attach to. filetypes = { 'lua' }, - -- Sets the "workspace" to the directory where any of these files is found. - -- Files that share a root directory will reuse the LSP server connection. + -- Sets the workspace "root" to the directory where any of these files is found. + -- Files sharing a root will reuse the LSP client/connection. -- Nested lists indicate equal priority, see |vim.lsp.Config|. - root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' }, - -- Specific settings to send to the server. The schema is server-defined. - -- Example: https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json + root_markers = { { '.emmyrc.json', '.luarc.json' }, '.git' }, + -- Server-specific settings. https://github.com/EmmyLuaLs/emmylua-analyzer-rust/blob/main/docs/config/emmyrc_json_EN.md settings = { - Lua = { - runtime = { - version = 'LuaJIT', - } + runtime = { + version = 'LuaJIT', } } } 3. Use |vim.lsp.enable()| to enable the config. Example: >lua - vim.lsp.enable('lua_ls') + vim.lsp.enable('emmylua_ls') 4. Open a code file matching one of the `filetypes` specified in the config. Note: Depending on the LSP server, you may need to ensure your project has a |lsp-root_markers| file so the workspace can be recognized. @@ -1109,8 +1106,8 @@ config({name}, {cfg}) *vim.lsp.config()* filetypes = { 'c', 'cpp' }, } < - • Get the resolved configuration for "lua_ls": >lua - local cfg = vim.lsp.config.lua_ls + • Get the resolved configuration for "emmylua_ls": >lua + local cfg = vim.lsp.config.emmylua_ls < Attributes: ~ @@ -1137,13 +1134,13 @@ enable({name}, {enable}) *vim.lsp.enable()* Examples: >lua vim.lsp.enable('clangd') - vim.lsp.enable({'lua_ls', 'pyright'}) + vim.lsp.enable({'emmylua_ls', 'pyright'}) < Example: To dynamically decide whether LSP is activated, define a |lsp-root_dir()| function which calls `on_dir()` only when you want that config to activate: >lua - vim.lsp.config('lua_ls', { + vim.lsp.config('emmylua_ls', { root_dir = function(bufnr, on_dir) if vim.fs.ext(vim.fn.bufname(bufnr)) ~= 'txt' then on_dir(vim.fn.getcwd()) @@ -1707,7 +1704,7 @@ workspace_diagnostics({opts}) *vim.lsp.buf.workspace_diagnostics()* clients. See also: ~ - • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_dagnostics + • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_diagnostic workspace_symbol({query}, {opts}) *vim.lsp.buf.workspace_symbol()* Lists all symbols in the current workspace in the quickfix window. diff --git a/runtime/doc/nvim.txt b/runtime/doc/nvim.txt index 032176e56b..c8044622b2 100644 --- a/runtime/doc/nvim.txt +++ b/runtime/doc/nvim.txt @@ -31,6 +31,8 @@ copying the "example_init.lua" file: >vim :exe 'edit' stdpath('config') .. '/init.lua' :read $VIMRUNTIME/example_init.lua + :write ++p + :restart < See |lua-guide| for practical notes on using Lua to configure Nvim. @@ -55,7 +57,7 @@ Transitioning from Vim *nvim-from-vim* 1. To start the transition, create your |init.vim| (user config) file: >vim - :exe 'edit '.stdpath('config').'/init.vim' + :exe 'edit' stdpath('config') .. '/init.vim' :write ++p 2. Add these contents to the file: >vim diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4469c88499..220ae172c4 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4189,7 +4189,7 @@ A jump table for the options with a short description can be found at |Q_op|. combine it with "tab:", for example: >vim set listchars+=tab:>-,lead:. < - *lcs-leadmultispace* + *lcs-leadmultispace* *indent-guides* leadmultispace:c... Like the |lcs-multispace| value, but for leading spaces only. Also overrides |lcs-lead| for leading @@ -4200,6 +4200,13 @@ A jump table for the options with a short description can be found at |Q_op|. < Where "XXX" denotes the first non-blank characters in the line. + + Combined with |lcs-leadtab|, this can be used to show + "indentation guides" (vertical lines). + For example, with 'shiftwidth' 2: >vim + set list listchars=leadtab:\ \ │,tab:\ \ │,leadmultispace:\ \ │ +< For richer rendering (per-level colors, treesitter-aware + scopes, etc.) use a third-party plugin. *lcs-leadtab* leadtab:xy[z] Like |lcs-tab|, but only for leading tabs. When diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 49ffeaf05d..fa3943f701 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -620,7 +620,7 @@ setting can affect the entire editor in ways that are not initially obvious. To find the cause of a problem in your config, you must "bisect" it: 1. Remove or disable half of your |config|. -2. Restart Nvim. +2. Restart Nvim (|:restart|). 3. If the problem still occurs, goto 1. 4. If the problem is gone, restore half of the removed lines. 5. Continue narrowing your config in this way, until you find the setting or diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index c76be97d8b..6c8f0e8bd9 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -1780,7 +1780,7 @@ Query:iter_captures({node}, {source}, {start_row}, {end_row}, {opts}) Return: ~ (`fun(end_line: integer?, end_col: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`) - capture id, capture node, metadata, match, tree + capture-id, capture-node, metadata, match, tree *Query:iter_matches()* Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) @@ -1822,7 +1822,7 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) Return: ~ (`fun(): integer, table, vim.treesitter.query.TSMetadata, TSTree`) - pattern id, match, metadata, tree + pattern-id, match, metadata, tree set({lang}, {query_name}, {text}) *vim.treesitter.query.set()* Sets the runtime query named {query_name} for {lang} diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index 2fd81852e3..8c6c1f8175 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -552,7 +552,7 @@ local function parse_cmdline(arg_lead, cmd_line) if arg_lead:match('^[^()]+%([^()]*$') then -- cursor (|) is at ':Man printf(|' or ':Man 1 printf(|' - -- The later is is allowed because of ':Man pri'. + -- The latter is allowed because of ':Man pri'. -- It will offer 'priclass.d(1m)' even though section is specified as 1. local tmp = vim.split(arg_lead, '(', { plain = true }) local name = tmp[1] diff --git a/runtime/lua/vim/_meta/options.gen.lua b/runtime/lua/vim/_meta/options.gen.lua index e30178d579..a151e0859c 100644 --- a/runtime/lua/vim/_meta/options.gen.lua +++ b/runtime/lua/vim/_meta/options.gen.lua @@ -4158,7 +4158,7 @@ vim.wo.list = vim.o.list --- set listchars+=tab:>-,lead:. --- ``` --- ---- *lcs-leadmultispace* +--- *lcs-leadmultispace* *indent-guides* --- leadmultispace:c... --- Like the `lcs-multispace` value, but for leading --- spaces only. Also overrides `lcs-lead` for leading @@ -4171,6 +4171,16 @@ vim.wo.list = vim.o.list --- --- Where "XXX" denotes the first non-blank characters in --- the line. +--- +--- Combined with `lcs-leadtab`, this can be used to show +--- "indentation guides" (vertical lines). +--- For example, with 'shiftwidth' 2: +--- +--- ```vim +--- set list listchars=leadtab:\ \ │,tab:\ \ │,leadmultispace:\ \ │ +--- ``` +--- For richer rendering (per-level colors, treesitter-aware +--- scopes, etc.) use a third-party plugin. --- *lcs-leadtab* --- leadtab:xy[z] --- Like `lcs-tab`, but only for leading tabs. When diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index cc23d69d69..9ede441bde 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -274,9 +274,9 @@ end --- filetypes = { 'c', 'cpp' }, --- } --- ``` ---- - Get the resolved configuration for "lua_ls": +--- - Get the resolved configuration for "emmylua_ls": --- ```lua ---- local cfg = vim.lsp.config.lua_ls +--- local cfg = vim.lsp.config.emmylua_ls --- ``` --- ---@since 13 @@ -589,14 +589,14 @@ end --- --- ```lua --- vim.lsp.enable('clangd') ---- vim.lsp.enable({'lua_ls', 'pyright'}) +--- vim.lsp.enable({'emmylua_ls', 'pyright'}) --- ``` --- --- Example: To _dynamically_ decide whether LSP is activated, define a |lsp-root_dir()| function --- which calls `on_dir()` only when you want that config to activate: --- --- ```lua ---- vim.lsp.config('lua_ls', { +--- vim.lsp.config('emmylua_ls', { --- root_dir = function(bufnr, on_dir) --- if vim.fs.ext(vim.fn.bufname(bufnr)) ~= 'txt' then --- on_dir(vim.fn.getcwd()) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 3ea48b20d5..4861a1a517 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1077,7 +1077,7 @@ end --- Request workspace-wide diagnostics. --- @param opts? vim.lsp.WorkspaceDiagnosticsOpts ---- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_dagnostics +--- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_diagnostic function M.workspace_diagnostics(opts) validate('opts', opts, 'table', true) diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 70b6cc4cc5..861cefd3aa 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -923,7 +923,7 @@ end --- - start_col (integer) Starting column for the search. --- ---@return (fun(end_line: integer|nil, end_col: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree): ---- capture id, capture node, metadata, match, tree +--- 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_row, end_row, opts) @@ -1040,7 +1040,7 @@ end --- - max_start_depth (integer) if non-zero, sets the maximum start depth --- for each match. This is used to prevent traversing too deep into a tree. --- ----@return (fun(): integer, table, vim.treesitter.query.TSMetadata, TSTree): pattern id, match, metadata, tree +---@return (fun(): integer, table, vim.treesitter.query.TSMetadata, TSTree): pattern-id, match, metadata, tree function Query:iter_matches(node, source, start, stop, opts) opts = opts or {} diff --git a/src/nvim/options.lua b/src/nvim/options.lua index c7b8feb75f..a3ce711a1c 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -5447,7 +5447,7 @@ local options = { combine it with "tab:", for example: >vim set listchars+=tab:>-,lead:. < - *lcs-leadmultispace* + *lcs-leadmultispace* *indent-guides* leadmultispace:c... Like the |lcs-multispace| value, but for leading spaces only. Also overrides |lcs-lead| for leading @@ -5458,6 +5458,13 @@ local options = { < Where "XXX" denotes the first non-blank characters in the line. + + Combined with |lcs-leadtab|, this can be used to show + "indentation guides" (vertical lines). + For example, with 'shiftwidth' 2: >vim + set list listchars=leadtab:\ \ │,tab:\ \ │,leadmultispace:\ \ │ + < For richer rendering (per-level colors, treesitter-aware + scopes, etc.) use a third-party plugin. *lcs-leadtab* leadtab:xy[z] Like |lcs-tab|, but only for leading tabs. When