diff options
-rw-r--r-- | runtime/doc/insert.txt | 26 | ||||
-rw-r--r-- | src/nvim/edit.c | 77 | ||||
-rw-r--r-- | src/nvim/version.c | 2 | ||||
-rw-r--r-- | test/functional/legacy/mapping_spec.lua | 28 |
4 files changed, 112 insertions, 21 deletions
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 2572aa1e91..a48ad0185d 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -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-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 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' @@ -402,6 +405,29 @@ that, with CTRL-O u. Another example: > This breaks undo at each line break. It also expands abbreviations before 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 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: > diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 2d6dcf4f80..b2f9601f82 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -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 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. @@ -611,6 +613,13 @@ edit ( * Get a character for Insert mode. Ignore K_IGNORE. */ 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(); do { c = safe_vgetc(); @@ -988,7 +997,7 @@ doESCkey: if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) ins_s_left(); else - ins_left(); + ins_left(dont_sync_undo == false); break; case K_S_LEFT: /* <S-Left> */ @@ -1000,7 +1009,7 @@ doESCkey: if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) ins_s_right(); else - ins_right(); + ins_right(dont_sync_undo == false); break; case K_S_RIGHT: /* <S-Right> */ @@ -5627,16 +5636,31 @@ static void redo_literal(int c) AppendCharToRedobuff(c); } -/* - * start_arrow() is called when an arrow key is used in insert mode. - * For undo/redo it resembles hitting the <ESC> key. - */ -static void -start_arrow ( - pos_T *end_insert_pos /* can be NULL */ -) +// start_arrow() is called when an arrow key is used in insert mode. +// For undo/redo it resembles hitting the <ESC> key. +static void start_arrow(pos_T *end_insert_pos /* can be NULL */) +{ + 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) { - if (!arrow_used) { /* something has been inserted */ + 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); stop_insert(end_insert_pos, FALSE, FALSE); arrow_used = TRUE; /* this means we stopped the current insert */ @@ -6903,6 +6927,13 @@ static void ins_ctrl_g(void) Insstart = curwin->w_cursor; 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. */ 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; @@ -7658,7 +7689,10 @@ static void ins_left(void) undisplay_dollar(); tpos = curwin->w_cursor; 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 (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol) revins_legal++; @@ -7669,6 +7703,7 @@ static void ins_left(void) * previous line */ 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); --(curwin->w_cursor.lnum); coladvance((colnr_T)MAXCOL); @@ -7676,6 +7711,7 @@ static void ins_left(void) } else { vim_beep(BO_CRSR); } + dont_sync_undo = false; } 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) foldOpenCursor(); undisplay_dollar(); - if (gchar_cursor() != NUL - || virtual_active() - ) { - start_arrow(&curwin->w_cursor); - curwin->w_set_curswant = TRUE; + if (gchar_cursor() != NUL || virtual_active()) { + start_arrow_with_change(&curwin->w_cursor, end_change); + if (!end_change) { + AppendCharToRedobuff(K_RIGHT); + } + curwin->w_set_curswant = true; if (virtual_active()) oneright(); else { @@ -7758,6 +7796,7 @@ static void ins_right(void) } else { vim_beep(BO_CRSR); } + dont_sync_undo = false; } static void ins_s_right(void) diff --git a/src/nvim/version.c b/src/nvim/version.c index 3b80336817..7cc72705b6 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -72,7 +72,7 @@ static char *features[] = { // clang-format off static int included_patches[] = { // 850, - // 849, + 849, // 848, // 847, // 846, diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua index 899f7423d0..4d377904f9 100644 --- a/test/functional/legacy/mapping_spec.lua +++ b/test/functional/legacy/mapping_spec.lua @@ -5,7 +5,7 @@ local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local execute, expect = helpers.execute, helpers.expect describe('mapping', function() - setup(clear) + before_each(clear) it('is working', function() insert([[ @@ -44,4 +44,30 @@ describe('mapping', function() + +]]) 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) |