commit 3f16037e45cfd7e66986e938ff2a6ff6165ebbed
parent 30634f63e26da1ecfd8642eec4a53f4c220247d7
Author: Justin M. Keyes <justinkz@gmail.com>
Date: Sat, 25 Oct 2025 16:46:25 +0200
docs: getpos, getregion, lsp
Diffstat:
17 files changed, 176 insertions(+), 126 deletions(-)
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
@@ -382,9 +382,10 @@ CmdUndefined When a user command is used but it isn't
always define the user command and have it
invoke an autoloaded function. See |autoload|.
*CmdlineChanged*
-CmdlineChanged After a change was made to the text inside
- command line. Be careful not to mess up the
- command line, it may cause Vim to lock up.
+CmdlineChanged After EVERY change inside command line. Also
+ triggered during mappings! Use |<Cmd>| instead
+ of ":" in mappings, to avoid that.
+
<afile> expands to the |cmdline-char|.
*CmdlineEnter*
CmdlineEnter After entering the command-line (including
diff --git a/runtime/doc/dev_arch.txt b/runtime/doc/dev_arch.txt
@@ -144,19 +144,17 @@ asynchronous events, which can include:
Nvim implements this functionality by entering another event loop while
waiting for characters, so instead of: >py
- def state_enter(state_callback, data):
+ def state_enter(on_state, data):
do
- key = readkey() # read a key from the user
- while state_callback(data, key) # invoke the callback for the current state
-<
+ key = readkey() # Read a key from the user
+ while on_state(data, key) # Invoke callback for the current state
-The Nvim program loop is more like: >py
+the Nvim program loop is more like: >py
- def state_enter(state_callback, data):
+ def state_enter(on_state, data):
do
- event = read_next_event() # read an event from the operating system
- while state_callback(data, event) # invoke the callback for the current state
-<
+ event = read_next_event() # Read an event from the OS
+ while on_state(data, event) # Invoke callback for current state
where `event` is something the operating system delivers to us, including (but
not limited to) user input. The `read_next_event()` part is internally
diff --git a/runtime/doc/dev_test.txt b/runtime/doc/dev_test.txt
@@ -33,7 +33,7 @@ Tests are broadly divided into unit tests (`test/unit/`), functional tests
opposed to Lua). They are essentially "integration" tests, they test the
full system. But they are fast.
-You can learn the [Lua concepts 15 minutes](https://learnxinyminutes.com/docs/lua/),
+You can learn [Lua concepts 15 minutes](https://learnxinyminutes.com/docs/lua/),
see also |lua-guide|. Use any existing test as a template to start writing new
tests, or see |dev-quickstart|.
diff --git a/runtime/doc/dev_tools.txt b/runtime/doc/dev_tools.txt
@@ -93,6 +93,8 @@ TUI INSPECT
Use the Ghostty https://ghostty.org/ inspector tool to observe and query the
output and events from any terminal application such as Nvim.
+From the Ghostty inspector you can click the "Terminal IO" tab to get a trace.
+
TERMINFO LOGGING
At 'verbose' level 3, Nvim logs its internal terminfo state, so you can see
@@ -139,9 +141,10 @@ region is repainted internally. To also highlight excess internal redraws, use >
TUI TRACE
-In the rare case that you want to collect a trace of terminal output, the
-ancient `script` command is still the "state of the art". The libvterm
-`vterm-dump` utility formats the result for human-readability.
+From the Ghostty inspector you can click the "Terminal IO" tab to get a trace.
+
+Alternatively, the ancient `script` command is the "state of the art". The
+libvterm `vterm-dump` utility formats the result for human-readability.
Record a Nvim terminal session and format it with `vterm-dump`: >sh
diff --git a/runtime/doc/faq.txt b/runtime/doc/faq.txt
@@ -120,7 +120,6 @@ manage the cursor style:
Alternatively, consider setting the desired cursor shape in your shell (e.g.
with PROMPT_COMMAND in bash).
-<
------------------------------------------------------------------------------
CURSOR SHAPE DOESN'T CHANGE IN TMUX
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
@@ -73,20 +73,21 @@ listed below, if (1) the language server supports the functionality and (2)
the options are empty or were set by the builtin runtime (ftplugin) files. The
options are not restored when the LSP client is stopped or detached.
-GLOBAL DEFAULTS
- *gra* *gri* *grn* *grr* *grt* *i_CTRL-S* *v_an* *v_in*
+GLOBAL DEFAULTS *gra* *gri* *grn* *grr* *grt* *i_CTRL-S* *v_an* *v_in*
+
These GLOBAL keymaps are created unconditionally when Nvim starts:
-- "gra" is mapped in Normal and Visual mode to |vim.lsp.buf.code_action()|
-- "gri" is mapped in Normal mode to |vim.lsp.buf.implementation()|
-- "grn" is mapped in Normal mode to |vim.lsp.buf.rename()|
-- "grr" is mapped in Normal mode to |vim.lsp.buf.references()|
-- "grt" is mapped in Normal mode to |vim.lsp.buf.type_definition()|
-- "gO" is mapped in Normal mode to |vim.lsp.buf.document_symbol()|
-- CTRL-S is mapped in Insert mode to |vim.lsp.buf.signature_help()|
-- "an" and "in" are mapped in Visual mode to outer and inner incremental
+- "gra" (Normal and Visual mode) is mapped to |vim.lsp.buf.code_action()|
+- "gri" is mapped to |vim.lsp.buf.implementation()|
+- "grn" is mapped to |vim.lsp.buf.rename()|
+- "grr" is mapped to |vim.lsp.buf.references()|
+- "grt" is mapped to |vim.lsp.buf.type_definition()|
+- "gO" is mapped to |vim.lsp.buf.document_symbol()|
+- CTRL-S (Insert mode) is mapped to |vim.lsp.buf.signature_help()|
+- "an" and "in" (Visual mode) are mapped to outer and inner incremental
selections, respectively, using |vim.lsp.buf.selection_range()|
BUFFER-LOCAL DEFAULTS
+
- 'omnifunc' is set to |vim.lsp.omnifunc()|, use |i_CTRL-X_CTRL-O| to trigger
completion.
- 'tagfunc' is set to |vim.lsp.tagfunc()|. This enables features like
@@ -101,6 +102,7 @@ BUFFER-LOCAL DEFAULTS
- To opt out call `vim.lsp.document_color.enable(false, args.buf)` on |LspAttach|.
DISABLING DEFAULTS *lsp-defaults-disable*
+
You can remove GLOBAL keymaps at any time using |vim.keymap.del()| or
|:unmap|. See also |gr-default|.
@@ -130,7 +132,7 @@ Use |vim.lsp.config()| to define or modify LSP configurations, and
|vim.lsp.start()| which allows you to share and merge configs (provided by
Nvim, plugins, and your local config).
- *lsp-new-config*
+NEW CONFIG *lsp-new-config*
To create a new config you can either use `vim.lsp.config()` or create
a `lsp/<config-name>.lua` file.
@@ -155,7 +157,7 @@ EXAMPLE: DEFINE A CONFIG AS A FILE ~
:lua vim.lsp.enable('foo')
5. Run `:checkhealth vim.lsp`, check "Enabled Configurations". 🌈
- *lsp-config-merge*
+HOW CONFIGS ARE MERGED *lsp-config-merge*
When an LSP client starts, it resolves its configuration by merging the
following sources (merge semantics defined by |vim.tbl_deep_extend()| with
"force" behavior), in order of increasing priority:
@@ -166,6 +168,7 @@ following sources (merge semantics defined by |vim.tbl_deep_extend()| with
3. Configurations defined anywhere else.
Example: given the following configs... >lua
+
-- Defined in init.lua
vim.lsp.config('*', {
capabilities = {
@@ -177,20 +180,19 @@ Example: given the following configs... >lua
},
root_markers = { '.git' },
})
-
-- Defined in <rtp>/lsp/clangd.lua
return {
cmd = { 'clangd' },
root_markers = { '.clangd', 'compile_commands.json' },
filetypes = { 'c', 'cpp' },
}
-
-- Defined in init.lua
vim.lsp.config('clangd', {
filetypes = { 'c' },
})
<
...the merged result is: >lua
+
{
-- From the clangd configuration in <rtp>/lsp/clangd.lua
cmd = { 'clangd' },
@@ -213,7 +215,7 @@ Example: given the following configs... >lua
}
}
<
- *lsp-attach*
+CONFIGURE ON ATTACH *lsp-attach*
To use LSP features beyond those provided by Nvim (see |lsp-buf|), you can set
keymaps and options on |Client:on_attach()| or |LspAttach|. Not all language
servers provide the same capabilities; check `supports_method()` in your
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
@@ -2410,15 +2410,13 @@ vim.filetype.match({args}) *vim.filetype.match()*
Mutually exclusive with {buf}.
Return (multiple): ~
- (`string?`) If a match was found, the matched filetype.
- (`function?`) A function that modifies buffer state when called (for
- example, to set some filetype specific buffer variables). The function
- accepts a buffer number as its only argument.
- (`boolean?`) Return true if a match was found by falling back to a
- generic configuration file (i.e., ".conf"). If true, the filetype
- should be set with `:setf FALLBACK conf`, which enables a later
- |:setf| command to override the filetype. See `:help setf` for more
- information.
+ (`string?`) The matched filetype, if any.
+ (`function?`) A function `fun(buf: integer)` that modifies buffer
+ state when called (for example, to set some filetype specific buffer
+ variables).
+ (`boolean?`) true if a match was found by falling back to a generic
+ filetype (i.e., ".conf"), which indicates the filetype should be set
+ with `:setf FALLBACK conf`. See |:setfiletype|.
==============================================================================
@@ -2434,6 +2432,13 @@ Example: >lua
end
<
+ *vim.fs.read()*
+You can use |readblob()| to get a file's contents without explicitly opening/closing it.
+
+Example: >lua
+ vim.print(vim.fn.readblob('.git/config'))
+<
+
vim.fs.abspath({path}) *vim.fs.abspath()*
Convert path to an absolute path. A tilde (~) character at the beginning
@@ -3438,6 +3443,18 @@ vim.keymap.set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()*
end, { expr = true })
-- Map "[%%" to a <Plug> mapping:
vim.keymap.set('n', '[%%', '<Plug>(MatchitNormalMultiBackward)')
+
+ -- Use `getregionpos(getpos('v'))` to get the "current visual selection":
+ vim.keymap.set('x', 'M', function()
+ local region = vim.fn.getregionpos(vim.fn.getpos('v'), vim.fn.getpos('.'), {
+ type = 'v',
+ exclusive = false,
+ eol = false,
+ })
+ local line1 = region[1][1][2]
+ local line2 = region[#region][1][2]
+ vim.print({ line1, line2 })
+ end)
<
Parameters: ~
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
@@ -326,9 +326,8 @@ be seen as a special key.
*<Cmd>* *:map-cmd*
The <Cmd> pseudokey begins a "command mapping", which executes the command
-directly without changing modes. Where you might use ":...<CR>" in the
-{rhs} of a mapping, you can instead use "<Cmd>...<CR>".
-Example: >
+directly without changing modes. Where you might use ":…<CR>" in the {rhs} of
+a mapping, you can instead use "<Cmd>…<CR>". Example: >
noremap x <Cmd>echo mode(1)<CR>
<
This is more flexible than `:<C-U>` in Visual and Operator-pending mode, or
@@ -348,7 +347,7 @@ Note:
|CmdlineEnter| and |CmdlineLeave| events. This helps performance.
- For the same reason, |keycodes| like <C-R><C-W> are interpreted as plain,
unmapped keys.
-- The command is not echo'ed, no need for <silent>.
+- The command is not echo'd, no need for <silent>.
- The {rhs} is not subject to abbreviations nor to other mappings, even if the
mapping is recursive.
- In Visual mode you can use `line('v')` and `col('v')` to get one end of the
@@ -357,7 +356,7 @@ Note:
*E1255* *E1136*
<Cmd> commands must terminate, that is, they must be followed by <CR> in the
{rhs} of the mapping definition. |Command-line| mode is never entered. To use
-a literal <CR> in the {rhs}, use |<lt>|.
+a literal "<" in the {rhs}, use |<lt>|.
1.3 MAPPING AND MODES *:map-modes*
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -71,8 +71,8 @@ EDITOR
- |i_CTRL-R| inserts named/clipboard registers (A-Z,a-z,0-9+) literally, like
pasting instead of like user input. Improves performance, avoids broken
formatting. To get the old behavior you can use `<C-R>=@x`.
-- Buffer names now follow RFC3986 for detecting a scheme, meaning
- "svn+ssh", "ed2k", and "iris.xpc" are now treated as URI schemes
+- Buffer name URI scheme parsing more closely follows RFC3986, so for example
+ "svn+ssh:", "ed2k:", and "iris.xpc:" are recognized as URI schemes.
EVENTS
diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt
@@ -3931,33 +3931,30 @@ getpid() *getpid()*
(`integer`)
getpos({expr}) *getpos()*
- Get the position for String {expr}.
- The accepted values for {expr} are:
- . The cursor position.
- $ The last line in the current buffer.
+ Gets a position, where {expr} is one of:
+ . Cursor position.
+ $ Last line in the current buffer.
'x Position of mark x (if the mark is not set, 0 is
returned for all values).
w0 First line visible in current window (one if the
display isn't updated, e.g. in silent Ex mode).
w$ Last line visible in current window (this is one
less than "w0" if no lines are visible).
- v When not in Visual mode, returns the cursor
- position. In Visual mode, returns the other end
- of the Visual area. A good way to think about
- this is that in Visual mode "v" and "." complement
- each other. While "." refers to the cursor
- position, "v" refers to where |v_o| would move the
- cursor. As a result, you can use "v" and "."
- together to work on all of a selection in
- characterwise Visual mode. If the cursor is at
- the end of a characterwise Visual area, "v" refers
- to the start of the same Visual area. And if the
- cursor is at the start of a characterwise Visual
- area, "v" refers to the end of the same Visual
- area. "v" differs from |'<| and |'>| in that it's
- updated right away.
- Note that a mark in another file can be used. The line number
- then applies to another buffer.
+ v End of the current Visual selection (unlike |'<|
+ |'>| which give the previous, not current, Visual
+ selection), or the cursor position if not in Visual
+ mode.
+
+ To get the current selected region: >vim
+ let region = getregionpos(getpos('v'), getpos('.'))
+<
+ Explanation: in Visual mode "v" and "." complement each
+ other. While "." refers to the cursor position, "v"
+ refers to where |v_o| would move the cursor. So you can
+ use "v" and "." together to get the selected region.
+
+ Note that if a mark in another file is used, the line number
+ applies to that buffer.
The result is a |List| with four numbers:
[bufnum, lnum, col, off]
@@ -4249,8 +4246,14 @@ getregionpos({pos1}, {pos2} [, {opts}]) *getregionpos()*
the offset of the character's first cell not included in the
selection, otherwise all its cells are included.
- Apart from the options supported by |getregion()|, {opts} also
- supports the following:
+ To get the current visual selection: >vim
+ let region = getregionpos(getpos('v'), getpos('.'))
+<
+ The {opts} Dict supports the following items:
+
+ type See |getregion()|.
+
+ exclusive See |getregion()|.
eol If |TRUE|, indicate positions beyond
the end of a line with "col" values
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
@@ -3530,33 +3530,30 @@ function vim.fn.getmousepos() end
--- @return integer
function vim.fn.getpid() end
---- Get the position for String {expr}.
---- The accepted values for {expr} are:
---- . The cursor position.
---- $ The last line in the current buffer.
+--- Gets a position, where {expr} is one of:
+--- . Cursor position.
+--- $ Last line in the current buffer.
--- 'x Position of mark x (if the mark is not set, 0 is
--- returned for all values).
--- w0 First line visible in current window (one if the
--- display isn't updated, e.g. in silent Ex mode).
--- w$ Last line visible in current window (this is one
--- less than "w0" if no lines are visible).
---- v When not in Visual mode, returns the cursor
---- position. In Visual mode, returns the other end
---- of the Visual area. A good way to think about
---- this is that in Visual mode "v" and "." complement
---- each other. While "." refers to the cursor
---- position, "v" refers to where |v_o| would move the
---- cursor. As a result, you can use "v" and "."
---- together to work on all of a selection in
---- characterwise Visual mode. If the cursor is at
---- the end of a characterwise Visual area, "v" refers
---- to the start of the same Visual area. And if the
---- cursor is at the start of a characterwise Visual
---- area, "v" refers to the end of the same Visual
---- area. "v" differs from |'<| and |'>| in that it's
---- updated right away.
---- Note that a mark in another file can be used. The line number
---- then applies to another buffer.
+--- v End of the current Visual selection (unlike |'<|
+--- |'>| which give the previous, not current, Visual
+--- selection), or the cursor position if not in Visual
+--- mode.
+---
+--- To get the current selected region: >vim
+--- let region = getregionpos(getpos('v'), getpos('.'))
+--- <
+--- Explanation: in Visual mode "v" and "." complement each
+--- other. While "." refers to the cursor position, "v"
+--- refers to where |v_o| would move the cursor. So you can
+--- use "v" and "." together to get the selected region.
+---
+--- Note that if a mark in another file is used, the line number
+--- applies to that buffer.
---
--- The result is a |List| with four numbers:
--- [bufnum, lnum, col, off]
@@ -3839,8 +3836,14 @@ function vim.fn.getregion(pos1, pos2, opts) end
--- the offset of the character's first cell not included in the
--- selection, otherwise all its cells are included.
---
---- Apart from the options supported by |getregion()|, {opts} also
---- supports the following:
+--- To get the current visual selection: >vim
+--- let region = getregionpos(getpos('v'), getpos('.'))
+--- <
+--- The {opts} Dict supports the following items:
+---
+--- type See |getregion()|.
+---
+--- exclusive See |getregion()|.
---
--- eol If |TRUE|, indicate positions beyond
--- the end of a line with "col" values
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
@@ -3101,14 +3101,12 @@ end
---
---@param args vim.filetype.match.args Table specifying which matching strategy to use.
--- Accepted keys are:
----@return string|nil # If a match was found, the matched filetype.
----@return function|nil # A function that modifies buffer state when called (for example, to set some
---- filetype specific buffer variables). The function accepts a buffer number as
---- its only argument.
----@return boolean|nil # Return true if a match was found by falling back to a generic configuration
---- file (i.e., ".conf"). If true, the filetype should be set with
---- `:setf FALLBACK conf`, which enables a later |:setf| command to override the
---- filetype. See `:help setf` for more information.
+---@return string|nil # The matched filetype, if any.
+---@return function|nil # A function `fun(buf: integer)` that modifies buffer state when called (for
+--- example, to set some filetype specific buffer variables).
+---@return boolean|nil # true if a match was found by falling back to a generic filetype
+--- (i.e., ".conf"), which indicates the filetype should be set with
+--- `:setf FALLBACK conf`. See |:setfiletype|.
function M.match(args)
vim.validate('arg', args, 'table')
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
@@ -9,6 +9,15 @@
--- vim.print('file exists')
--- end
--- <
+---
+--- *vim.fs.read()*
+--- You can use |readblob()| to get a file's contents without explicitly opening/closing it.
+---
+--- Example:
+---
+--- >lua
+--- vim.print(vim.fn.readblob('.git/config'))
+--- <
local uv = vim.uv
diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua
@@ -30,6 +30,18 @@ local keymap = {}
--- end, { expr = true })
--- -- Map "[%%" to a <Plug> mapping:
--- vim.keymap.set('n', '[%%', '<Plug>(MatchitNormalMultiBackward)')
+---
+--- -- Use `getregionpos(getpos('v'))` to get the "current visual selection":
+--- vim.keymap.set('x', 'M', function()
+--- local region = vim.fn.getregionpos(vim.fn.getpos('v'), vim.fn.getpos('.'), {
+--- type = 'v',
+--- exclusive = false,
+--- eol = false,
+--- })
+--- local line1 = region[1][1][2]
+--- local line2 = region[#region][1][2]
+--- vim.print({ line1, line2 })
+--- end)
--- ```
---
---@param mode string|string[] Mode "short-name" (see |nvim_set_keymap()|), or a list thereof.
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
@@ -4410,33 +4410,30 @@ M.funcs = {
args = 1,
base = 1,
desc = [=[
- Get the position for String {expr}.
- The accepted values for {expr} are:
- . The cursor position.
- $ The last line in the current buffer.
+ Gets a position, where {expr} is one of:
+ . Cursor position.
+ $ Last line in the current buffer.
'x Position of mark x (if the mark is not set, 0 is
returned for all values).
w0 First line visible in current window (one if the
display isn't updated, e.g. in silent Ex mode).
w$ Last line visible in current window (this is one
less than "w0" if no lines are visible).
- v When not in Visual mode, returns the cursor
- position. In Visual mode, returns the other end
- of the Visual area. A good way to think about
- this is that in Visual mode "v" and "." complement
- each other. While "." refers to the cursor
- position, "v" refers to where |v_o| would move the
- cursor. As a result, you can use "v" and "."
- together to work on all of a selection in
- characterwise Visual mode. If the cursor is at
- the end of a characterwise Visual area, "v" refers
- to the start of the same Visual area. And if the
- cursor is at the start of a characterwise Visual
- area, "v" refers to the end of the same Visual
- area. "v" differs from |'<| and |'>| in that it's
- updated right away.
- Note that a mark in another file can be used. The line number
- then applies to another buffer.
+ v End of the current Visual selection (unlike |'<|
+ |'>| which give the previous, not current, Visual
+ selection), or the cursor position if not in Visual
+ mode.
+
+ To get the current selected region: >vim
+ let region = getregionpos(getpos('v'), getpos('.'))
+ <
+ Explanation: in Visual mode "v" and "." complement each
+ other. While "." refers to the cursor position, "v"
+ refers to where |v_o| would move the cursor. So you can
+ use "v" and "." together to get the selected region.
+
+ Note that if a mark in another file is used, the line number
+ applies to that buffer.
The result is a |List| with four numbers:
[bufnum, lnum, col, off]
@@ -4746,8 +4743,14 @@ M.funcs = {
the offset of the character's first cell not included in the
selection, otherwise all its cells are included.
- Apart from the options supported by |getregion()|, {opts} also
- supports the following:
+ To get the current visual selection: >vim
+ let region = getregionpos(getpos('v'), getpos('.'))
+ <
+ The {opts} Dict supports the following items:
+
+ type See |getregion()|.
+
+ exclusive See |getregion()|.
eol If |TRUE|, indicate positions beyond
the end of a line with "col" values
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
@@ -1153,7 +1153,7 @@ void ex_changes(exarg_T *eap)
} \
}
-// don't delete the line, just put at first deleted line
+// "NO DELete": don't delete the line, just put at first deleted line.
#define ONE_ADJUST_NODEL(add) \
{ \
lp = add; \
diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua
@@ -1014,6 +1014,9 @@ function M.add_builddir_to_rtp()
end
--- Create folder with non existing parents
+---
+--- TODO(justinmk): lift this and `t.mkdir()` into vim.fs.
+---
--- @param path string
--- @return boolean?
function M.mkdir_p(path)