mirror of
https://github.com/neovim/neovim.git
synced 2026-09-02 12:23:50 +00:00
Problem:
- `lhs` is not fully realized. E.g. for a "payload" mapping
`lhs` omits the `getchar()` payload during a mapping (vim-surround
`ds'` reports `lhs="ds"`). This means plugins like vim-repeat are
still needed...
- The "delta" fields of a CmdAtom are calculated too late.
- `<abuf>` and `changed` check whatever (wrong) buffer a command
(":bnext") might land in.
- CTRL-W_w between two windows on the same buffer reports type="motion".
Solution:
- `CmdOrigin` samples (buf/win/cursor/changedtick) at each "scope" entry
(CmdFrame, composite, Visual session, insert session).
- `dd<C-w>l` reports `changed=true` for the buffer it edited, regardless
of where the cursor ends up.
- New fields:
- `pos`: cursor position at command start.
- `moved`: indicates whether the cursor moved (in same buffer).
- `undoseq`: undo state at settlement.
- lhs now includes the payload: "ds)" reports lhs="ds)" instead of "ds".
- Easy for users to "replay" any atom.
- Rename: type "command" => "normal", "ex" => "excmd"; `arg` => `cmdarg`
- Drop `cascade` field (no reason to expose it)
288 lines
10 KiB
Plaintext
288 lines
10 KiB
Plaintext
*repeat.txt* Nvim
|
|
|
|
|
|
NVIM REFERENCE MANUAL
|
|
|
|
|
|
Repeating commands *repeating*
|
|
|
|
Chapter 26 of the user manual introduces repeating |usr_26.txt|.
|
|
|
|
Type |gO| to see the table of contents.
|
|
|
|
==============================================================================
|
|
Single repeat *single-repeat*
|
|
|
|
*.*
|
|
. Repeat last change, with count replaced with [count].
|
|
Also repeat a yank command, when the 'y' flag is
|
|
included in 'cpoptions'. Does not repeat a
|
|
command-line command.
|
|
|
|
*@:*
|
|
@: Repeat last command-line [count] times.
|
|
|
|
Simple changes can be repeated with the "." command. Without a count, the
|
|
count of the last change is used. If you enter a count, it will replace the
|
|
last one. |v:count| and |v:count1| will be set.
|
|
|
|
If the last change included a specification of a numbered register, the
|
|
register number will be incremented. See |redo-register| for an example how
|
|
to use this.
|
|
|
|
Repeating an insert |ins-repeat| re-executes the whole session, including
|
|
non-jump cursor-moves: "iab<Left>c<Esc>." produces "acb", not "c". Note that
|
|
jumps (mouse, |i_<PageUp>|, …) split an insert session.
|
|
|
|
Repeating a Visual-mode command re-executes the captured keysequence,
|
|
selection included. See |visual-repeat|.
|
|
|
|
==============================================================================
|
|
Semantic repeat *cmdatom* *action-repeat*
|
|
|
|
The |CmdAtom| event is published on every user action. This avoids the need
|
|
for plugins to "announce" the repeatable unit, thus plugins like vim-repeat
|
|
aren't needed.
|
|
|
|
*excalibur*
|
|
`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.
|
|
|
|
INPUT (`lhs`) RESOLUTION (`keys`) ~
|
|
dw "dw" Not translated.
|
|
zfa{ "zfa{" Not an edit (`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.
|
|
ds) ":call …<CR>" "Payload" mapping (reads |getchar()|
|
|
e.g. vim-surround "ds'"), `remap=true`
|
|
]q "" Lua mapping, `remap=true`
|
|
,D "" Incomplete mapping (ended mid-
|
|
operation) `:nnoremap ,D d`,
|
|
`pending='operator'`, the NEXT atom
|
|
("dw") completes it
|
|
V<C-E>d "" Unreplayable: viewport-dependent
|
|
Visual sequence; `lhs` is only
|
|
a hint/label.
|
|
|
|
To repeat an atom, feed `keys` (mode "n"), or `lhs` (mode "m"). Empty `keys`
|
|
without `remap` means the atom is unreplayable. >lua
|
|
|
|
local function replay(a)
|
|
if not a.remap and a.keys == '' then
|
|
return -- Unreplayable Visual op.
|
|
end
|
|
vim.api.nvim_feedkeys(a.remap and a.lhs or a.keys, a.remap and 'm' or 'n', false)
|
|
end
|
|
<
|
|
*motion-repeat*
|
|
Example: This "," mapping repeats the last "motion" ("zj", "3w", "fx",
|
|
"/pat<CR>", "]c") by checking `(moved and not changed)`. Drop the
|
|
`not ev.data.moved` condition to repeat any non-edit ("zz", "zfa{", CTRL-W_w): >lua
|
|
|
|
local last ---@type vim.event.cmdatom.data?
|
|
vim.api.nvim_create_autocmd('CmdAtom', {
|
|
callback = function(ev)
|
|
if not ev.data.moved or ev.data.changed or ev.data.lhs == ',' then
|
|
return -- Skip non-motions, edits, and this mapping itself.
|
|
end
|
|
last = ev.data
|
|
end,
|
|
})
|
|
vim.keymap.set('n', ',', function()
|
|
-- CmdAtom is deferred: schedule the replay, in case "," follows a motion.
|
|
vim.schedule(function()
|
|
if last then
|
|
replay(last)
|
|
end
|
|
end)
|
|
end)
|
|
<
|
|
*restore-undo-cursor*
|
|
Example: Restore cursor position after undo. Works for |u|, "3u", |CTRL-R|,
|
|
":undo N", |g-| and any mapping: >lua
|
|
|
|
local seen = {} ---@type table<integer, table>
|
|
vim.api.nvim_create_autocmd('CmdAtom', {
|
|
callback = function(ev)
|
|
local seq = ev.data.undoseq
|
|
if not seq then
|
|
return
|
|
end
|
|
local s = seen[ev.buf] or {}
|
|
seen[ev.buf] = s
|
|
-- Note: g- :earlier may cross undo-tree branches, "best effort" in that case.
|
|
if s.prev and seq < s.prev and s[seq + 1] then
|
|
vim.api.nvim_win_set_cursor(0, s[seq + 1]) -- Undo: first abandoned state.
|
|
elseif s.prev and seq > s.prev and s[seq] then
|
|
vim.api.nvim_win_set_cursor(0, s[seq]) -- Redo: revisiting a known seq.
|
|
elseif ev.data.changed and not s[seq] then
|
|
s[seq] = ev.data.pos -- New edit.
|
|
end
|
|
s.prev = seq
|
|
end,
|
|
})
|
|
<
|
|
|
|
==============================================================================
|
|
Multiple repeat *multi-repeat*
|
|
|
|
*:g* *:global* *E148*
|
|
:[range]g[lobal]/{pattern}/[cmd]
|
|
Execute the Ex command [cmd] (default ":p") on the
|
|
lines within [range] where {pattern} matches.
|
|
|
|
:[range]g[lobal]!/{pattern}/[cmd]
|
|
Execute the Ex command [cmd] (default ":p") on the
|
|
lines within [range] where {pattern} does NOT match.
|
|
|
|
*:v* *:vglobal*
|
|
:[range]v[global]/{pattern}/[cmd]
|
|
Same as :g!.
|
|
|
|
Example: >
|
|
:g/^Obsolete/d _
|
|
Using the underscore after `:d` avoids clobbering registers or the clipboard.
|
|
This also makes it faster.
|
|
|
|
Instead of the '/' which surrounds the {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.
|
|
|
|
For the definition of a pattern, see |pattern|.
|
|
|
|
NOTE [cmd] may contain a range; see |collapse| and |edit-paragraph-join| for
|
|
examples.
|
|
|
|
The global commands work by first scanning through the [range] lines and
|
|
marking each line where a match occurs (for a multi-line pattern, only the
|
|
start of the match matters).
|
|
In a second scan the [cmd] is executed for each marked line, as if the cursor
|
|
was in that line. For ":v" and ":g!" the command is executed for each not
|
|
marked line. If a line is deleted its mark disappears.
|
|
The default for [range] is the whole buffer (1,$). Use "CTRL-C" to interrupt
|
|
the command. If an error message is given for a line, the command for that
|
|
line is aborted and the global command continues with the next marked or
|
|
unmarked line.
|
|
*E147*
|
|
When the command is used recursively, it only works on one line. Giving a
|
|
range is then not allowed. This is useful to find all lines that match a
|
|
pattern and do not match another pattern: >
|
|
:g/found/v/notfound/{cmd}
|
|
This first finds all lines containing "found", but only executes {cmd} when
|
|
there is no match for "notfound".
|
|
|
|
Any Ex command can be used, see |ex-cmd-index|. To execute a Normal mode
|
|
command, you can use the `: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|.
|
|
|
|
The undo/redo command will undo/redo the whole global command at once.
|
|
The previous context mark will only be set once (with "''" you go back to
|
|
where the cursor was before the global command).
|
|
|
|
The global command sets both the last used search pattern and the last used
|
|
substitute pattern (this is vi compatible). This makes it easy to globally
|
|
replace a string: >
|
|
:g/pat/s//PAT/g
|
|
This replaces all occurrences of "pat" with "PAT". The same can be done with: >
|
|
:%s/pat/PAT/g
|
|
Which is two characters shorter!
|
|
|
|
==============================================================================
|
|
Complex repeat *complex-repeat*
|
|
|
|
*q* *recording* *macro*
|
|
q{0-9a-zA-Z"} Record typed characters into register {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|.
|
|
|
|
Note: If the register being used for recording is also
|
|
used for |y| and |p| the result is most likely not
|
|
what is expected, because the put will paste the
|
|
recorded macro and the yank will overwrite the
|
|
recorded macro.
|
|
|
|
Note: The recording happens while you type, replaying
|
|
the register happens as if the keys come from a
|
|
mapping. This matters, for example, for undo, which
|
|
only syncs when commands were typed.
|
|
|
|
q Stops recording.
|
|
Implementation note: The 'q' that stops recording is
|
|
not stored in the register, unless it was the result
|
|
of a mapping
|
|
|
|
*@*
|
|
@{0-9a-z".=*+} Execute the contents of register {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 |@:|.
|
|
|
|
*@@* *E748*
|
|
@@ Repeat the previous @{0-9a-z":*} [count] times.
|
|
|
|
*v_@-default*
|
|
{Visual}@{0-9a-z".=*+} In linewise Visual mode, execute the contents of the
|
|
{Visual}@@ register for each selected line.
|
|
See |visual-repeat|, |default-mappings|.
|
|
|
|
*Q*
|
|
Q Repeat the last recorded register [count] times.
|
|
See |reg_recorded()|.
|
|
|
|
*v_Q-default*
|
|
{Visual}Q In linewise Visual mode, repeat the last recorded
|
|
register for each selected line.
|
|
See |visual-repeat|, |default-mappings|.
|
|
|
|
*v_Q*
|
|
{Visual}Q Place a cursor on each line of the Visual selection.
|
|
|
|
*:@*
|
|
:[addr]@{0-9a-z".=*+} Execute the contents of register {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.
|
|
|
|
*:@:*
|
|
:[addr]@: Repeat last command-line. First set cursor at line
|
|
[addr] (default is current line).
|
|
|
|
:[addr]@ *:@@*
|
|
:[addr]@@ Repeat the previous :@{register}. First set cursor at
|
|
line [addr] (default is current line).
|
|
|
|
==============================================================================
|
|
Multiple cursors *mcursor* *multicursor*
|
|
|
|
todo
|
|
|
|
|
|
vim:tw=78:ts=8:noet:ft=help:norl:
|