Skip to content

Commit 8b5f65a

Browse files
committed
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)
1 parent 5adfea1 commit 8b5f65a

5 files changed

Lines changed: 115 additions & 10 deletions

File tree

runtime/doc/insert.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,9 @@ CTRL-O execute one command, return to Insert mode *i_CTRL-O*
377377
CTRL-\ CTRL-O like CTRL-O but don't move the cursor *i_CTRL-\_CTRL-O*
378378
CTRL-L when 'insertmode' is set: go to Normal mode *i_CTRL-L*
379379
CTRL-G u break undo sequence, start new change *i_CTRL-G_u*
380+
CTRL-G U don't break undo with next left/right cursor *i_CTRL-G_U*
381+
movement (but only if the cursor stays
382+
within same the line)
380383
-----------------------------------------------------------------------
381384

382385
Note: If the cursor keys take you out of Insert mode, check the 'noesckeys'
@@ -416,6 +419,28 @@ that, with CTRL-O u. Another example: >
416419
This breaks undo at each line break. It also expands abbreviations before
417420
this.
418421

422+
An example for using CTRL-G U: >
423+
424+
inoremap <Left> <C-G>U<Left>
425+
inoremap <Right> <C-G>U<Right>
426+
inoremap <expr> <Home> col('.') == match(getline('.'), '\S') + 1 ?
427+
\ repeat('<C-G>U<Left>', col('.') - 1) :
428+
\ (col('.') < match(getline('.'), '\S') ?
429+
\ repeat('<C-G>U<Right>', match(getline('.'), '\S') + 0) :
430+
\ repeat('<C-G>U<Left>', col('.') - 1 - match(getline('.'), '\S')))
431+
inoremap <expr> <End> repeat('<C-G>U<Right>', col('$') - col('.'))
432+
inoremap ( ()<C-G>U<Left>
433+
434+
This makes it possible to use the cursor keys in Insert mode, without breaking
435+
the undo sequence and therefore using |.| (redo) will work as expected.
436+
Also entering a text like (with the "(" mapping from above): >
437+
438+
Lorem ipsum (dolor
439+
440+
will be repeatable by the |.|to the expected
441+
442+
Lorem ipsum (dolor)
443+
419444
Using CTRL-O splits undo: the text typed before and after it is undone
420445
separately. If you want to avoid this (e.g., in a mapping) you might be able
421446
to use CTRL-R = |i_CTRL-R|. E.g., to call a function: >

src/edit.c

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ static void internal_format __ARGS((int textwidth, int second_indent, int flags,
202202
static void check_auto_format __ARGS((int));
203203
static void redo_literal __ARGS((int c));
204204
static void start_arrow __ARGS((pos_T *end_insert_pos));
205+
static void start_arrow_with_change __ARGS((pos_T *end_insert_pos, int change));
206+
static void start_arrow_common __ARGS((pos_T *end_insert_pos, int change));
205207
#ifdef FEAT_SPELL
206208
static void check_spell_redraw __ARGS((void));
207209
static void spell_back_to_badword __ARGS((void));
@@ -241,11 +243,11 @@ static void ins_mousescroll __ARGS((int dir));
241243
#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
242244
static void ins_tabline __ARGS((int c));
243245
#endif
244-
static void ins_left __ARGS((void));
246+
static void ins_left __ARGS((int end_change));
245247
static void ins_home __ARGS((int c));
246248
static void ins_end __ARGS((int c));
247249
static void ins_s_left __ARGS((void));
248-
static void ins_right __ARGS((void));
250+
static void ins_right __ARGS((int end_change));
249251
static void ins_s_right __ARGS((void));
250252
static void ins_up __ARGS((int startcol));
251253
static void ins_pageup __ARGS((void));
@@ -297,6 +299,8 @@ static int ins_need_undo; /* call u_save() before inserting a
297299

298300
static int did_add_space = FALSE; /* auto_format() added an extra space
299301
under the cursor */
302+
static int dont_sync_undo = FALSE; /* CTRL-G U prevents syncing undo for
303+
the next left/right cursor */
300304

301305
/*
302306
* edit(): Start inserting text.
@@ -767,6 +771,12 @@ edit(cmdchar, startln, count)
767771
*/
768772
if (c != K_CURSORHOLD)
769773
lastc = c; /* remember the previous char for CTRL-D */
774+
775+
/* After using CTRL-G U the next cursor key will not break undo. */
776+
if (dont_sync_undo == MAYBE)
777+
dont_sync_undo = TRUE;
778+
else
779+
dont_sync_undo = FALSE;
770780
do
771781
{
772782
c = safe_vgetc();
@@ -1237,7 +1247,7 @@ edit(cmdchar, startln, count)
12371247
if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))
12381248
ins_s_left();
12391249
else
1240-
ins_left();
1250+
ins_left(dont_sync_undo == FALSE);
12411251
break;
12421252

12431253
case K_S_LEFT: /* <S-Left> */
@@ -1249,7 +1259,7 @@ edit(cmdchar, startln, count)
12491259
if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))
12501260
ins_s_right();
12511261
else
1252-
ins_right();
1262+
ins_right(dont_sync_undo == FALSE);
12531263
break;
12541264

12551265
case K_S_RIGHT: /* <S-Right> */
@@ -6787,9 +6797,34 @@ redo_literal(c)
67876797
*/
67886798
static void
67896799
start_arrow(end_insert_pos)
6790-
pos_T *end_insert_pos; /* can be NULL */
6800+
pos_T *end_insert_pos; /* can be NULL */
6801+
{
6802+
start_arrow_common(end_insert_pos, TRUE);
6803+
}
6804+
6805+
/*
6806+
* Like start_arrow() but with end_change argument.
6807+
* Will prepare for redo of CTRL-G U if "end_change" is FALSE.
6808+
*/
6809+
static void
6810+
start_arrow_with_change(end_insert_pos, end_change)
6811+
pos_T *end_insert_pos; /* can be NULL */
6812+
int end_change; /* end undoable change */
67916813
{
6792-
if (!arrow_used) /* something has been inserted */
6814+
start_arrow_common(end_insert_pos, end_change);
6815+
if (!end_change)
6816+
{
6817+
AppendCharToRedobuff(Ctrl_G);
6818+
AppendCharToRedobuff('U');
6819+
}
6820+
}
6821+
6822+
static void
6823+
start_arrow_common(end_insert_pos, end_change)
6824+
pos_T *end_insert_pos; /* can be NULL */
6825+
int end_change; /* end undoable change */
6826+
{
6827+
if (!arrow_used && end_change) /* something has been inserted */
67936828
{
67946829
AppendToRedobuff(ESC_STR);
67956830
stop_insert(end_insert_pos, FALSE, FALSE);
@@ -8359,6 +8394,13 @@ ins_ctrl_g()
83598394
Insstart = curwin->w_cursor;
83608395
break;
83618396

8397+
/* CTRL-G U: do not break undo with the next char */
8398+
case 'U':
8399+
/* Allow one left/right cursor movement with the next char,
8400+
* without breaking undo. */
8401+
dont_sync_undo = MAYBE;
8402+
break;
8403+
83628404
/* Unknown CTRL-G command, reserved for future expansion. */
83638405
default: vim_beep(BO_CTRLG);
83648406
}
@@ -9440,7 +9482,8 @@ ins_horscroll()
94409482
#endif
94419483

94429484
static void
9443-
ins_left()
9485+
ins_left(end_change)
9486+
int end_change; /* end undoable change */
94449487
{
94459488
pos_T tpos;
94469489

@@ -9457,7 +9500,11 @@ ins_left()
94579500
* break undo. K_LEFT is inserted in im_correct_cursor(). */
94589501
if (!im_is_preediting())
94599502
#endif
9460-
start_arrow(&tpos);
9503+
{
9504+
start_arrow_with_change(&tpos, end_change);
9505+
if (!end_change)
9506+
AppendCharToRedobuff(K_LEFT);
9507+
}
94619508
#ifdef FEAT_RIGHTLEFT
94629509
/* If exit reversed string, position is fixed */
94639510
if (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol)
@@ -9472,13 +9519,15 @@ ins_left()
94729519
*/
94739520
else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1)
94749521
{
9522+
/* always break undo when moving upwards/downwards, else undo may break */
94759523
start_arrow(&tpos);
94769524
--(curwin->w_cursor.lnum);
94779525
coladvance((colnr_T)MAXCOL);
94789526
curwin->w_set_curswant = TRUE; /* so we stay at the end */
94799527
}
94809528
else
94819529
vim_beep(BO_CRSR);
9530+
dont_sync_undo = FALSE;
94829531
}
94839532

94849533
static void
@@ -9542,7 +9591,8 @@ ins_s_left()
95429591
}
95439592

95449593
static void
9545-
ins_right()
9594+
ins_right(end_change)
9595+
int end_change; /* end undoable change */
95469596
{
95479597
#ifdef FEAT_FOLDING
95489598
if ((fdo_flags & FDO_HOR) && KeyTyped)
@@ -9555,7 +9605,9 @@ ins_right()
95559605
#endif
95569606
)
95579607
{
9558-
start_arrow(&curwin->w_cursor);
9608+
start_arrow_with_change(&curwin->w_cursor, end_change);
9609+
if (!end_change)
9610+
AppendCharToRedobuff(K_RIGHT);
95599611
curwin->w_set_curswant = TRUE;
95609612
#ifdef FEAT_VIRTUALEDIT
95619613
if (virtual_active())
@@ -9589,6 +9641,7 @@ ins_right()
95899641
}
95909642
else
95919643
vim_beep(BO_CRSR);
9644+
dont_sync_undo = FALSE;
95929645
}
95939646

95949647
static void

src/testdir/test_mapping.in

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ o+
4545
:/^a b
4646
0qqdw.ifooqj0@q:unmap .
4747

48+
:" <c-g>U<cursor> works only within a single line
49+
:imapclear
50+
:imap ( ()<c-g>U<left>
51+
G2oki
52+
Test1: text with a (here some more textk.
53+
:" test undo
54+
G2oki
55+
Test2: text wit a (here some more text [und undo]uk.u
56+
:"
57+
:imapclear
58+
:set whichwrap=<,>,[,]
59+
G3o2k
60+
:exe ":norm! iTest3: text with a (parenthesis here\<C-G>U\<Right>new line here\<esc>\<up>\<up>."
61+
62+
4863

4964
:/^test/,$w! test.out
5065
:qa!

src/testdir/test_mapping.ok

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,13 @@ vmap works
1010
+
1111
+
1212
+
13+
14+
Test1: text with a (here some more text)
15+
Test1: text with a (here some more text)
16+
17+
18+
Test2: text wit a (here some more text [und undo])
19+
20+
new line here
21+
Test3: text with a (parenthesis here
22+
new line here

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,8 @@ static char *(features[]) =
741741

742742
static int included_patches[] =
743743
{ /* Add new patch number below this line */
744+
/**/
745+
849,
744746
/**/
745747
848,
746748
/**/

0 commit comments

Comments
 (0)