vim-patch:7.4.849

Problem:    Moving the cursor in Insert mode starts new undo sequence.
Solution:   Add CTRL-G U to keep the undo sequence for the following
            cursor movement command. (Christian Brabandt)

8b5f65a527

Closes #3492
This commit is contained in:
Justin M. Keyes
2015-09-07 22:27:21 -04:00
parent 1ca5646bb5
commit 0f9dea2a0e
4 changed files with 112 additions and 21 deletions

View File

@@ -363,6 +363,9 @@ CTRL-O execute one command, return to Insert mode *i_CTRL-O*
CTRL-\ CTRL-O like CTRL-O but don't move the cursor *i_CTRL-\_CTRL-O* CTRL-\ CTRL-O like CTRL-O but don't move the cursor *i_CTRL-\_CTRL-O*
CTRL-L when 'insertmode' is set: go to Normal mode *i_CTRL-L* CTRL-L when 'insertmode' is set: go to Normal mode *i_CTRL-L*
CTRL-G u break undo sequence, start new change *i_CTRL-G_u* CTRL-G u break undo sequence, start new change *i_CTRL-G_u*
CTRL-G U don't break undo with next left/right cursor *i_CTRL-G_U*
movement (but only if the cursor stays
within same the line)
----------------------------------------------------------------------- -----------------------------------------------------------------------
Note: If the cursor keys take you out of Insert mode, check the 'noesckeys' Note: If the cursor keys take you out of Insert mode, check the 'noesckeys'
@@ -402,6 +405,29 @@ that, with CTRL-O u. Another example: >
This breaks undo at each line break. It also expands abbreviations before This breaks undo at each line break. It also expands abbreviations before
this. this.
An example for using CTRL-G U: >
inoremap <Left> <C-G>U<Left>
inoremap <Right> <C-G>U<Right>
inoremap <expr> <Home> col('.') == match(getline('.'), '\S') + 1 ?
\ repeat('<C-G>U<Left>', col('.') - 1) :
\ (col('.') < match(getline('.'), '\S') ?
\ repeat('<C-G>U<Right>', match(getline('.'), '\S') + 0) :
\ repeat('<C-G>U<Left>', col('.') - 1 - match(getline('.'), '\S')))
inoremap <expr> <End> repeat('<C-G>U<Right>', col('$') - col('.'))
inoremap ( ()<C-G>U<Left>
This makes it possible to use the cursor keys in Insert mode, without breaking
the undo sequence and therefore using |.| (redo) will work as expected.
Also entering a text like (with the "(" mapping from above): >
Lorem ipsum (dolor
will be repeatable by the |.|to the expected
Lorem ipsum (dolor)
Using CTRL-O splits undo: the text typed before and after it is undone Using CTRL-O splits undo: the text typed before and after it is undone
separately. If you want to avoid this (e.g., in a mapping) you might be able separately. If you want to avoid this (e.g., in a mapping) you might be able
to use CTRL-R = |i_CTRL-R|. E.g., to call a function: > to use CTRL-R = |i_CTRL-R|. E.g., to call a function: >

View File

@@ -228,6 +228,8 @@ static int ins_need_undo; /* call u_save() before inserting a
static int did_add_space = FALSE; /* auto_format() added an extra space static int did_add_space = FALSE; /* auto_format() added an extra space
under the cursor */ under the cursor */
static int dont_sync_undo = false; /* CTRL-G U prevents syncing undo for
the next left/right cursor */
/* /*
* edit(): Start inserting text. * edit(): Start inserting text.
@@ -611,6 +613,13 @@ edit (
* Get a character for Insert mode. Ignore K_IGNORE. * Get a character for Insert mode. Ignore K_IGNORE.
*/ */
lastc = c; /* remember previous char for CTRL-D */ lastc = c; /* remember previous char for CTRL-D */
// After using CTRL-G U the next cursor key will not break undo.
if (dont_sync_undo == MAYBE)
dont_sync_undo = true;
else
dont_sync_undo = false;
input_enable_events(); input_enable_events();
do { do {
c = safe_vgetc(); c = safe_vgetc();
@@ -988,7 +997,7 @@ doESCkey:
if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))
ins_s_left(); ins_s_left();
else else
ins_left(); ins_left(dont_sync_undo == false);
break; break;
case K_S_LEFT: /* <S-Left> */ case K_S_LEFT: /* <S-Left> */
@@ -1000,7 +1009,7 @@ doESCkey:
if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))
ins_s_right(); ins_s_right();
else else
ins_right(); ins_right(dont_sync_undo == false);
break; break;
case K_S_RIGHT: /* <S-Right> */ case K_S_RIGHT: /* <S-Right> */
@@ -5627,16 +5636,31 @@ static void redo_literal(int c)
AppendCharToRedobuff(c); AppendCharToRedobuff(c);
} }
/* // start_arrow() is called when an arrow key is used in insert mode.
* start_arrow() is called when an arrow key is used in insert mode. // For undo/redo it resembles hitting the <ESC> key.
* For undo/redo it resembles hitting the <ESC> key. static void start_arrow(pos_T *end_insert_pos /* can be NULL */)
*/
static void
start_arrow (
pos_T *end_insert_pos /* can be NULL */
)
{ {
if (!arrow_used) { /* something has been inserted */ start_arrow_common(end_insert_pos, true);
}
/// Like start_arrow() but with end_change argument.
/// Will prepare for redo of CTRL-G U if "end_change" is FALSE.
/// @param end_insert_pos can be NULL
/// @param end_change end undoable change
static void start_arrow_with_change(pos_T *end_insert_pos, bool end_change)
{
start_arrow_common(end_insert_pos, end_change);
if (!end_change) {
AppendCharToRedobuff(Ctrl_G);
AppendCharToRedobuff('U');
}
}
/// @param end_insert_pos can be NULL
/// @param end_change end undoable change
static void start_arrow_common(pos_T *end_insert_pos, bool end_change)
{
if (!arrow_used && end_change) { // something has been inserted
AppendToRedobuff(ESC_STR); AppendToRedobuff(ESC_STR);
stop_insert(end_insert_pos, FALSE, FALSE); stop_insert(end_insert_pos, FALSE, FALSE);
arrow_used = TRUE; /* this means we stopped the current insert */ arrow_used = TRUE; /* this means we stopped the current insert */
@@ -6903,6 +6927,13 @@ static void ins_ctrl_g(void)
Insstart = curwin->w_cursor; Insstart = curwin->w_cursor;
break; break;
// CTRL-G U: do not break undo with the next char.
case 'U':
// Allow one left/right cursor movement with the next char,
// without breaking undo.
dont_sync_undo = MAYBE;
break;
/* Unknown CTRL-G command, reserved for future expansion. */ /* Unknown CTRL-G command, reserved for future expansion. */
default: vim_beep(BO_CTRLG); default: vim_beep(BO_CTRLG);
} }
@@ -7649,7 +7680,7 @@ static void ins_mousescroll(int dir)
static void ins_left(void) static void ins_left(bool end_change)
{ {
pos_T tpos; pos_T tpos;
@@ -7658,7 +7689,10 @@ static void ins_left(void)
undisplay_dollar(); undisplay_dollar();
tpos = curwin->w_cursor; tpos = curwin->w_cursor;
if (oneleft() == OK) { if (oneleft() == OK) {
start_arrow(&tpos); start_arrow_with_change(&tpos, end_change);
if (!end_change) {
AppendCharToRedobuff(K_LEFT);
}
/* If exit reversed string, position is fixed */ /* If exit reversed string, position is fixed */
if (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol) if (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol)
revins_legal++; revins_legal++;
@@ -7669,6 +7703,7 @@ static void ins_left(void)
* previous line * previous line
*/ */
else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) { else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
// always break undo when moving upwards/downwards, else undo may break
start_arrow(&tpos); start_arrow(&tpos);
--(curwin->w_cursor.lnum); --(curwin->w_cursor.lnum);
coladvance((colnr_T)MAXCOL); coladvance((colnr_T)MAXCOL);
@@ -7676,6 +7711,7 @@ static void ins_left(void)
} else { } else {
vim_beep(BO_CRSR); vim_beep(BO_CRSR);
} }
dont_sync_undo = false;
} }
static void ins_home(int c) static void ins_home(int c)
@@ -7724,16 +7760,18 @@ static void ins_s_left(void)
} }
} }
static void ins_right(void) /// @param end_change end undoable change
static void ins_right(bool end_change)
{ {
if ((fdo_flags & FDO_HOR) && KeyTyped) if ((fdo_flags & FDO_HOR) && KeyTyped)
foldOpenCursor(); foldOpenCursor();
undisplay_dollar(); undisplay_dollar();
if (gchar_cursor() != NUL if (gchar_cursor() != NUL || virtual_active()) {
|| virtual_active() start_arrow_with_change(&curwin->w_cursor, end_change);
) { if (!end_change) {
start_arrow(&curwin->w_cursor); AppendCharToRedobuff(K_RIGHT);
curwin->w_set_curswant = TRUE; }
curwin->w_set_curswant = true;
if (virtual_active()) if (virtual_active())
oneright(); oneright();
else { else {
@@ -7758,6 +7796,7 @@ static void ins_right(void)
} else { } else {
vim_beep(BO_CRSR); vim_beep(BO_CRSR);
} }
dont_sync_undo = false;
} }
static void ins_s_right(void) static void ins_s_right(void)

View File

@@ -72,7 +72,7 @@ static char *features[] = {
// clang-format off // clang-format off
static int included_patches[] = { static int included_patches[] = {
// 850, // 850,
// 849, 849,
// 848, // 848,
// 847, // 847,
// 846, // 846,

View File

@@ -5,7 +5,7 @@ local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local execute, expect = helpers.execute, helpers.expect local execute, expect = helpers.execute, helpers.expect
describe('mapping', function() describe('mapping', function()
setup(clear) before_each(clear)
it('is working', function() it('is working', function()
insert([[ insert([[
@@ -44,4 +44,30 @@ describe('mapping', function()
+ +
+]]) +]])
end) end)
it('i_CTRL-G_U', function()
-- <c-g>U<cursor> works only within a single line
execute('imapclear')
execute('imap ( ()<c-g>U<left>')
feed('G2o<esc>ki<cr>Test1: text with a (here some more text<esc>k.')
-- test undo
feed('G2o<esc>ki<cr>Test2: text wit a (here some more text [und undo]<c-g>u<esc>k.u')
execute('imapclear')
execute('set whichwrap=<,>,[,]')
feed('G3o<esc>2k')
execute([[:exe ":norm! iTest3: text with a (parenthesis here\<C-G>U\<Right>new line here\<esc>\<up>\<up>."]])
expect([[
Test1: text with a (here some more text)
Test1: text with a (here some more text)
Test2: text wit a (here some more text [und undo])
new line here
Test3: text with a (parenthesis here
new line here
]])
end)
end) end)