mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
feat: add undo!
Allows using `undo!` to undo changes and remove them from the undo-tree. Can only be used for moving backwards in the same undo branch.
This commit is contained in:
parent
0124a7bfa9
commit
1e3d9c7dbc
@ -22,6 +22,14 @@ u Undo [count] changes.
|
|||||||
:u[ndo] {N} Jump to after change number {N}. See |undo-branches|
|
:u[ndo] {N} Jump to after change number {N}. See |undo-branches|
|
||||||
for the meaning of {N}.
|
for the meaning of {N}.
|
||||||
|
|
||||||
|
:u[ndo]! Undo one change and remove it from undo history.
|
||||||
|
*E5767*
|
||||||
|
:u[ndo]! {N} Like ":u[ndo] {N}", but forget all changes in the
|
||||||
|
current undo branch up until {N}. You may only use
|
||||||
|
":undo! {N}" to move backwards in the same undo
|
||||||
|
branch, not to redo or switch to a different undo
|
||||||
|
branch.
|
||||||
|
|
||||||
*CTRL-R*
|
*CTRL-R*
|
||||||
CTRL-R Redo [count] changes which were undone.
|
CTRL-R Redo [count] changes which were undone.
|
||||||
|
|
||||||
|
@ -2947,7 +2947,7 @@ module.cmds = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
command='undo',
|
command='undo',
|
||||||
flags=bit.bor(RANGE, COUNT, ZEROR, TRLBAR, CMDWIN),
|
flags=bit.bor(BANG, RANGE, COUNT, ZEROR, TRLBAR, CMDWIN),
|
||||||
addr_type='ADDR_OTHER',
|
addr_type='ADDR_OTHER',
|
||||||
func='ex_undo',
|
func='ex_undo',
|
||||||
},
|
},
|
||||||
|
@ -76,6 +76,7 @@
|
|||||||
#include "nvim/terminal.h"
|
#include "nvim/terminal.h"
|
||||||
#include "nvim/ui.h"
|
#include "nvim/ui.h"
|
||||||
#include "nvim/undo.h"
|
#include "nvim/undo.h"
|
||||||
|
#include "nvim/undo_defs.h"
|
||||||
#include "nvim/version.h"
|
#include "nvim/version.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
@ -8231,10 +8232,39 @@ static void ex_bang(exarg_T *eap)
|
|||||||
/// ":undo".
|
/// ":undo".
|
||||||
static void ex_undo(exarg_T *eap)
|
static void ex_undo(exarg_T *eap)
|
||||||
{
|
{
|
||||||
if (eap->addr_count == 1) { // :undo 123
|
if (eap->addr_count != 1) {
|
||||||
undo_time(eap->line2, false, false, true);
|
if (eap->forceit) {
|
||||||
} else {
|
u_undo_and_forget(1); // :undo!
|
||||||
u_undo(1);
|
} else {
|
||||||
|
u_undo(1); // :undo
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long step = eap->line2;
|
||||||
|
|
||||||
|
if (eap->forceit) { // undo! 123
|
||||||
|
// change number for "undo!" must be lesser than current change number
|
||||||
|
if (step >= curbuf->b_u_seq_cur) {
|
||||||
|
emsg(_(e_undobang_cannot_redo_or_move_branch));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ensure that target change number is in same branch
|
||||||
|
// while also counting the amount of undoes it'd take to reach target
|
||||||
|
u_header_T *uhp;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (uhp = curbuf->b_u_curhead ? curbuf->b_u_curhead : curbuf->b_u_newhead;
|
||||||
|
uhp != NULL && uhp->uh_seq > step;
|
||||||
|
uhp = uhp->uh_next.ptr, ++count) {
|
||||||
|
}
|
||||||
|
if (step != 0 && (uhp == NULL || uhp->uh_seq < step)) {
|
||||||
|
emsg(_(e_undobang_cannot_redo_or_move_branch));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
u_undo_and_forget(count);
|
||||||
|
} else { // :undo 123
|
||||||
|
undo_time(step, false, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1013,6 +1013,9 @@ EXTERN char e_line_number_out_of_range[] INIT(= N_("E1247: Line number out of ra
|
|||||||
|
|
||||||
EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long"));
|
EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long"));
|
||||||
|
|
||||||
|
EXTERN char e_undobang_cannot_redo_or_move_branch[]
|
||||||
|
INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch"));
|
||||||
|
|
||||||
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
|
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
|
||||||
EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
|
EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
|
||||||
|
|
||||||
|
@ -2,9 +2,18 @@ local helpers = require('test.functional.helpers')(after_each)
|
|||||||
|
|
||||||
local clear = helpers.clear
|
local clear = helpers.clear
|
||||||
local command = helpers.command
|
local command = helpers.command
|
||||||
|
local eval = helpers.eval
|
||||||
local expect = helpers.expect
|
local expect = helpers.expect
|
||||||
|
local eq = helpers.eq
|
||||||
local feed = helpers.feed
|
local feed = helpers.feed
|
||||||
|
local feed_command = helpers.feed_command
|
||||||
local insert = helpers.insert
|
local insert = helpers.insert
|
||||||
|
local funcs = helpers.funcs
|
||||||
|
|
||||||
|
local function lastmessage()
|
||||||
|
local messages = funcs.split(funcs.execute('messages'), '\n')
|
||||||
|
return messages[#messages]
|
||||||
|
end
|
||||||
|
|
||||||
describe('u CTRL-R g- g+', function()
|
describe('u CTRL-R g- g+', function()
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
@ -59,3 +68,61 @@ describe('u CTRL-R g- g+', function()
|
|||||||
undo_and_redo(4, 'g-', 'g+', '1')
|
undo_and_redo(4, 'g-', 'g+', '1')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe(':undo! command', function()
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
feed('i1 little bug in the code<Esc>')
|
||||||
|
feed('o1 little bug in the code<Esc>')
|
||||||
|
feed('oTake 1 down, patch it around<Esc>')
|
||||||
|
feed('o99 little bugs in the code<Esc>')
|
||||||
|
end)
|
||||||
|
it('works', function()
|
||||||
|
feed_command('undo!')
|
||||||
|
expect([[
|
||||||
|
1 little bug in the code
|
||||||
|
1 little bug in the code
|
||||||
|
Take 1 down, patch it around]])
|
||||||
|
feed('<C-r>')
|
||||||
|
eq('Already at newest change', lastmessage())
|
||||||
|
end)
|
||||||
|
it('works with arguments', function()
|
||||||
|
feed_command('undo! 2')
|
||||||
|
expect([[
|
||||||
|
1 little bug in the code
|
||||||
|
1 little bug in the code]])
|
||||||
|
feed('<C-r>')
|
||||||
|
eq('Already at newest change', lastmessage())
|
||||||
|
end)
|
||||||
|
it('correctly sets alternative redo', function()
|
||||||
|
feed('uo101 little bugs in the code<Esc>')
|
||||||
|
feed_command('undo!')
|
||||||
|
feed('<C-r>')
|
||||||
|
expect([[
|
||||||
|
1 little bug in the code
|
||||||
|
1 little bug in the code
|
||||||
|
Take 1 down, patch it around
|
||||||
|
99 little bugs in the code]])
|
||||||
|
|
||||||
|
feed('uuoTake 2 down, patch them around<Esc>')
|
||||||
|
feed('o101 little bugs in the code<Esc>')
|
||||||
|
feed_command('undo! 2')
|
||||||
|
feed('<C-r><C-r>')
|
||||||
|
expect([[
|
||||||
|
1 little bug in the code
|
||||||
|
1 little bug in the code
|
||||||
|
Take 1 down, patch it around
|
||||||
|
99 little bugs in the code]])
|
||||||
|
end)
|
||||||
|
it('fails when attempting to redo or move to different undo branch', function()
|
||||||
|
feed_command('undo! 4')
|
||||||
|
eq('E5767: Cannot use :undo! to redo or move to a different undo branch', eval('v:errmsg'))
|
||||||
|
feed('u')
|
||||||
|
feed_command('undo! 4')
|
||||||
|
eq('E5767: Cannot use :undo! to redo or move to a different undo branch', eval('v:errmsg'))
|
||||||
|
feed('o101 little bugs in the code<Esc>')
|
||||||
|
feed('o101 little bugs in the code<Esc>')
|
||||||
|
feed_command('undo! 4')
|
||||||
|
eq('E5767: Cannot use :undo! to redo or move to a different undo branch', eval('v:errmsg'))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user