mirror of
https://github.com/neovim/neovim.git
synced 2026-11-02 20:30:40 -06:00
feat(editor): undo restores cursor position #41520
Problem: Undo places the cursor wherever the cursor happened to sit at "save time" (`uh_cursor` is sampled lazily on the first change). Examples: - `i` preserves, but `a` does not - `diw`, `atest<Esc>`, `d^` abandon the original position - `D`, `o` restore it (by accident). Solution: `composite` tracks the pending atom (and its `origin`) across frames. A `stuffed` continuation frame inherits the `origin` + prepped redo. Store `origin` info in the undo header, so undo can restore it. - Not for a mid-command undo break (i_CTRL-G_u). - Undoing a mapping restores where the mapping started (which technically may be different than where the "edit" started).
This commit is contained in:
1 parent
617f9e628c
commit
7e2e3f8c25
16 files changed
+185
-137
No files matched your search
@@ -305,6 +305,11 @@ EDITOR
|
||||
real-time, instead of only on |FocusGained| or |:checktime|.
|
||||
• During |complete()|-triggered completion, CTRL-N and CTRL-P are now subject
|
||||
to insert-mode mappings.
|
||||
• |undo| restores the cursor to its original position ("diw", "atest<Esc>",
|
||||
"d^", …), instead of jumping to the start of the changed text.
|
||||
• |undo| no longer restores the old (wrong) position of a mark that you moved
|
||||
(|m|, |:mark|) later. The mark shifts with the text it was moved to, the
|
||||
same as a mark the change never touched.
|
||||
• Multibyte characters, translated by 'langmap', now invoke correct
|
||||
mappings.
|
||||
Example: >vim
|
||||
@@ -526,9 +531,6 @@ These existing features changed their behavior.
|
||||
`opts.plain=true` and now does not expand leading tildes ("~") in addition
|
||||
to environment variables ("expand_env" is still accepted, for backwards
|
||||
compatibility).
|
||||
• |undo| no longer restores the old (wrong) position of a mark that you moved
|
||||
(|m|, |:mark|) later. The mark shifts with the text it was moved to, the
|
||||
same as a mark the change never touched.
|
||||
• |:helptags| finds help tags with the "vimdoc" |treesitter| parser (and thus
|
||||
requires it to be installed).
|
||||
• `:helptags ALL` reports |E152| for "doc" directories it cannot write,
|
||||
|
||||
+9
-20
@@ -103,11 +103,10 @@ operations provided by plugins, without the need for "announcement" via
|
||||
vim-repeat or similar. >lua
|
||||
|
||||
local last ---@type vim.event.cmdatom.data?
|
||||
local maxseq = {} ---@type table<integer, integer>
|
||||
vim.api.nvim_create_autocmd('CmdAtom', {
|
||||
callback = function(ev)
|
||||
local is_redo_or_undo = ev.data.changed and (ev.data.undoseq or 0) <= (maxseq[ev.buf] or 0)
|
||||
maxseq[ev.buf] = vim.fn.undotree(ev.buf).seq_last
|
||||
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 = vim.fn.undotree(ev.buf).seq_last
|
||||
if ev.data.changed and not is_redo_or_undo and ev.data.lhs ~= '.' then
|
||||
last = ev.data
|
||||
end
|
||||
@@ -168,27 +167,17 @@ without a count replays the macro. >lua
|
||||
end)
|
||||
<
|
||||
*restore-undo-cursor*
|
||||
Example: Restore cursor position after undo. Works for |u|, "3u", |CTRL-R|,
|
||||
":undo N", |g-| and any mapping: >lua
|
||||
By default, |undo| returns the cursor to its original position. If you don't
|
||||
like that, you can use this snippet to get Vim's behavior instead: >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
|
||||
-- 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 = vim.fn.undotree(ev.buf).seq_last
|
||||
if undid then
|
||||
vim.cmd('normal! `[') -- Start of the changed text.
|
||||
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,
|
||||
})
|
||||
<
|
||||
|
||||
@@ -14,7 +14,8 @@ The basics are explained in section |02.5| of the user manual.
|
||||
1. Undo and redo commands *undo-commands*
|
||||
|
||||
<Undo> or *undo* *<Undo>* *u*
|
||||
u Undo [count] changes.
|
||||
u Undo [count] changes. The cursor returns to its
|
||||
original position.
|
||||
|
||||
*:u* *:un* *:undo*
|
||||
:u[ndo] Undo one change.
|
||||
|
||||
Reference in new issue
Block a user