Nvim :help pages, generated
from source
using the tree-sitter-vimdoc parser.
CmdAtom.lhs is the high-level user input collected during an action,
including getchar() input. This is signficant: it reflects the semantic
intent. CmdAtom.keys reveals the low-level internal commands that were
produced.lhs) RESOLUTION (keys)changed=false).
x "dl" Translated builtin.
,d "dl" Mapping :nnoremap ,d x.
@q "dl" Macro @q = "x".
<F6> "dlw" Composite :nnoremap <F6> xw:
type='mapping', subatoms "dl" and "w"
concat to the composite keys.
,Dw "dw" Incomplete mapping: ended mid-
operation (:nnoremap ,D d),
captures its continuation ("w").
ds) ":call …<NL>)" "Payload" mapping (e.g. vim-surround
"ds'" reads getchar()).
]q nil Lua mapping.
V<C-E>d "" Unreplayable: viewport-dependent
Visual sequence; lhs is only
a hint/label.keys (mode "n"), or lhs (mode "m") if keys=nil.
local function replay(a)
vim.api.nvim_feedkeys(a.keys or a.lhs, a.keys and 'n' or 'm', false)
endmoved=true).
local last ---@type vim.event.cmdatom.data?
vim.api.nvim_create_autocmd('CmdAtom', {
callback = function(ev)
-- Skip edits, and the "," mapping itself.
local motion = ev.data.moved or ev.match == 'motion'
if motion and not (ev.data.changed or ev.data.lhs == ',') then
last = ev.data
end
end,
})
vim.keymap.set('n', ',', function()
-- CmdAtom is deferred; schedule the replay, in case "," follows a motion.
vim.schedule(function()
if last then
vim.api.nvim_feedkeys(last.keys or last.lhs, last.keys and 'n' or 'm', false)
end
end)
end)
local last ---@type vim.event.cmdatom.data?
vim.api.nvim_create_autocmd('CmdAtom', {
callback = function(ev)
local is_redo_or_undo = ev.data.changed and (ev.data.undoseq or 0) <= (vim.b[ev.buf].maxseq or 0)
vim.b[ev.buf].maxseq = math.max(vim.b[ev.buf].maxseq or 0, ev.data.undoseq or 0)
if ev.data.changed and not is_redo_or_undo and ev.data.lhs ~= '.' then
last = ev.data
end
end,
})
vim.keymap.set('n', '.', function()
-- Multicursors: degrade to builtin "." (cascades).
local mc = vim.api.nvim_create_namespace('nvim.multicursor')
if #vim.api.nvim_buf_get_extmarks(0, mc, 0, -1, { limit = 1 }) > 0 then
vim.api.nvim_feedkeys('.', 'n', false)
return
end
-- CmdAtom is deferred; schedule the replay, in case "." follows an edit.
vim.schedule(function()
if last then
vim.api.nvim_feedkeys(last.keys or last.lhs, last.keys and 'n' or 'm', false)
end
end)
end)<Enter> saves it. "<Space>"
without a count replays the macro.
-- Track the last 20 atoms.
local atom_ring = {} ---@type vim.event.cmdatom.data[]
vim.api.nvim_create_autocmd('CmdAtom', {
callback = function(ev)
-- Skip this mapping itself, and cmdwin edits.
if ev.data.lhs ~= ' ' and vim.fn.getcmdwintype() == '' then
atom_ring[#atom_ring + 1] = ev.data
if #atom_ring > 20 then
table.remove(atom_ring, 1)
end
end
end,
})
-- [count]<space> shows a cmdwin where the user can edit/save the last [count] atoms as a "macro".
-- <space> (no count) replays it.
vim.keymap.set('n', '<Space>', function()
local count = vim.v.count
-- CmdAtom is deferred; schedule it so pending events land in the ring first.
vim.schedule(function()
count = math.min(count, #atom_ring)
if count == 0 then -- Replay the saved macro.
for _, step in ipairs(vim.g.atom_macro or {}) do
vim.api.nvim_feedkeys(vim.keycode(step.keys or step.lhs), step.keys and 'n' or 'm', false)
end
return
end
local parts = {}
for i = #atom_ring - count + 1, #atom_ring do
local a = atom_ring[i]
local keys = a.keys or ('%s%s'):format(a.count or '', a.lhs)
local field = a.keys and 'keys' or 'lhs'
parts[#parts + 1] = ('{%s=%q},'):format(field, vim.fn.keytrans(keys))
end
local cmd = ('lua vim.g.atom_macro = { %s }'):format(table.concat(parts, ' '))
-- Draft it on the cmdline; CTRL-F opens the cmdwin to edit it.
vim.api.nvim_feedkeys((':%s%s'):format(cmd, vim.keycode('<C-f>')), 'n', false)
end)
end)
vim.api.nvim_create_autocmd('CmdAtom', {
callback = function(ev)
-- Undo/redo: the buffer changed to an already-seen undo state.
local undid = ev.data.changed and (ev.data.undoseq or 0) <= (vim.b[ev.buf].maxseq or 0)
vim.b[ev.buf].maxseq = math.max(vim.b[ev.buf].maxseq or 0, ev.data.undoseq or 0)
if undid then
vim.cmd('normal! `[') -- Start of the changed text.
end
end,
}){pattern} matches.{pattern} does NOT match.:g/^Obsolete/d _Using the underscore after
:d avoids clobbering registers or the clipboard.
This also makes it faster.{pattern}, you can use any other
single byte character, but not an alphabetic character, '\', '"', '|' or '!'.
This is useful if you want to include a '/' in the search pattern or
replacement string.:g/found/v/notfound/{cmd}
This first finds all lines containing "found", but only executes {cmd} when
there is no match for "notfound".:normal command::g/pat/normal {commands}
Make sure that {commands} ends with a whole command, otherwise Vim will wait
for you to type the rest of the command for each match. The screen will not
have been updated, so you don't know what you are doing. See :normal.:g/pat/s//PAT/gThis replaces all occurrences of "pat" with "PAT". The same can be done with:
:%s/pat/PAT/gWhich is two characters shorter!
{0-9a-zA-Z"}
(uppercase to append). The 'q' command is disabled
while executing a register, and it doesn't work inside
a mapping and :normal.{0-9a-z".=*+} [count]
times. Note that register '%' (name of the current
file) and '#' (name of the alternate file) cannot be
used.
The register is executed like a mapping, that means
that the difference between 'wildchar' and 'wildcharm'
applies, and undo might not be synced in the same way.
For "@=" you are prompted to enter an expression. The
result of the expression is then executed.
See also @:.{Visual}@{0-9a-z".=*+} In linewise Visual mode, execute the contents of the
{Visual}@@ register for each selected line.
See visual-repeat, default-mappings.{0-9a-z".=*+} as an
Ex command. First set cursor at line [addr] (default
is current line). When the last line in the register
does not have a <CR> it will be added automatically
when the 'e' flag is present in 'cpoptions'.
For ":@=" the last used expression is used. The
result of evaluating the expression is executed as an
Ex command.
Mappings are not recognized in these commands.
When the line-continuation character (\) is present
at the beginning of a line in a linewise register,
then it is combined with the previous line. This is
useful for yanking and executing parts of a Vim
script.CTRL-C interrupts the cascade.
:g/pattern/normal! nQ
:cdo normal! Q :2,4cdo normal! Q
for _, m in ipairs(vim.fn.matchbufline('%', [[pattern]], 1, '$')) do
vim.api.nvim_mcursor(0, { m.lnum, m.byteidx })
end:marks nvim.multicursor
{Visual}[count]Q Places a cursor at each match of the last search pattern
within the Visually-selected lines.<C-LeftMouse> Toggles a multicursor at the click position, without
moving the primary cursor (or changing windows). Does
NOT disable follow-mode q=. No-op in Insert-mode.local mc_ns = vim.api.nvim_create_namespace('nvim.multicursor')
vim.api.nvim_buf_clear_namespace(0, mc_ns, 0, -1)CTRL-A During a multicursor session, inserts an ascending
number ("counter") at each cursor, so a column of
cursors becomes 1, 2, 3, …. Use [count] to choose the
initial number.{motion} at every
cursor, then disables follow-mode again.
vim.keymap.set('n', 'q-', function()
vim.cmd('normal! 1q=')
vim.api.nvim_create_autocmd('CmdAtom', {
callback = function(ev)
if ev.data.lhs == 'q-' then
return -- Skip the mapping itself.
end
vim.cmd('normal! 2q=')
return true -- Delete the handler.
end,
})
end)"*yy, "+yy) are global, not per-cursor. On
multicursor exit, you can set the clipboard to the joined yank::let @+=@"