# HG changeset patch # User Bram Moolenaar # Date 1641218405 -3600 # Node ID 9975bd408d6c458d97b987a324ed295bbdb7c558 # Parent 05290f930ed6e5e1d111b44bbb3f623ae35eb6ec patch 8.2.3993: when recording a change in Select mode char appears twice Commit: https://github.com/vim/vim/commit/c88e977862ba6477a3b5b28706c45f96069a3073 Author: Bram Moolenaar Date: Mon Jan 3 13:47:50 2022 +0000 patch 8.2.3993: when recording a change in Select mode char appears twice Problem: When recording a change in Select mode the first typed character appears twice. Solution: When putting the character back into typeahead remove it from recorded characters. (closes #9462) diff --git a/src/getchar.c b/src/getchar.c --- a/src/getchar.c +++ b/src/getchar.c @@ -242,6 +242,22 @@ add_buff( } /* + * Delete "slen" bytes from the end of "buf". + * Only works when it was just added. + */ + static void +delete_buff_tail(buffheader_T *buf, int slen) +{ + int len = (int)STRLEN(buf->bh_curr->b_str); + + if (len >= slen) + { + buf->bh_curr->b_str[len - slen] = NUL; + buf->bh_space += slen; + } +} + +/* * Add number "n" to buffer "buf". */ static void @@ -1100,12 +1116,13 @@ ins_typebuf( * Can be used for a character obtained by vgetc() that needs to be put back. * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to * the char. + * Returns the length of what was inserted. */ - void + int ins_char_typebuf(int c, int modifier) { char_u buf[MB_MAXBYTES + 4]; - int idx = 0; + int len = 0; if (modifier != 0) { @@ -1113,19 +1130,23 @@ ins_char_typebuf(int c, int modifier) buf[1] = KS_MODIFIER; buf[2] = modifier; buf[3] = NUL; - idx = 3; + len = 3; } if (IS_SPECIAL(c)) { - buf[idx] = K_SPECIAL; - buf[idx + 1] = K_SECOND(c); - buf[idx + 2] = K_THIRD(c); - buf[idx + 3] = NUL; - idx += 3; + buf[len] = K_SPECIAL; + buf[len + 1] = K_SECOND(c); + buf[len + 2] = K_THIRD(c); + buf[len + 3] = NUL; + len += 3; } else - buf[(*mb_char2bytes)(c, buf + idx) + idx] = NUL; + { + len += (*mb_char2bytes)(c, buf + len); + buf[len] = NUL; + } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); + return len; } /* @@ -1302,6 +1323,22 @@ gotchars(char_u *chars, int len) } /* + * Undo the last gotchars() for "len" bytes. To be used when putting a typed + * character back into the typeahead buffer, thus gotchars() will be called + * again. + * Only affects recorded characters. + */ + void +ungetchars(int len) +{ + if (reg_recording != 0) + { + delete_buff_tail(&recordbuff, len); + last_recorded_len -= len; + } +} + +/* * Sync undo. Called when typed characters are obtained from the typeahead * buffer, or when a menu is used. * Do not sync: diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -592,12 +592,19 @@ normal_cmd( && VIsual_select && (vim_isprintc(c) || c == NL || c == CAR || c == K_KENTER)) { + int len; + // Fake a "c"hange command. When "restart_edit" is set (e.g., because // 'insertmode' is set) fake a "d"elete command, Insert mode will // restart automatically. // Insert the typed character in the typeahead buffer, so that it can // be mapped in Insert mode. Required for ":lmap" to work. - ins_char_typebuf(vgetc_char, vgetc_mod_mask); + len = ins_char_typebuf(vgetc_char, vgetc_mod_mask); + + // When recording the character will be recorded again, remove the + // previously recording. + ungetchars(len); + if (restart_edit != 0) c = 'd'; else diff --git a/src/proto/getchar.pro b/src/proto/getchar.pro --- a/src/proto/getchar.pro +++ b/src/proto/getchar.pro @@ -24,11 +24,12 @@ int start_redo_ins(void); void stop_redo_ins(void); int noremap_keys(void); int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, int silent); -void ins_char_typebuf(int c, int modifier); +int ins_char_typebuf(int c, int modifier); int typebuf_changed(int tb_change_cnt); int typebuf_typed(void); int typebuf_maplen(void); void del_typebuf(int len, int offset); +void ungetchars(int len); int save_typebuf(void); void save_typeahead(tasave_T *tp); void restore_typeahead(tasave_T *tp, int overwrite); diff --git a/src/testdir/test_registers.vim b/src/testdir/test_registers.vim --- a/src/testdir/test_registers.vim +++ b/src/testdir/test_registers.vim @@ -197,6 +197,17 @@ func Test_recording_esc_sequence() endif endfunc +func Test_recording_with_select_mode() + new + call feedkeys("qacc12345\gH98765\q", "tx") + call assert_equal("98765", getline(1)) + call assert_equal("cc12345\gH98765\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("98765", getline(1)) + bwipe! +endfunc + " Test for executing the last used register (@) func Test_last_used_exec_reg() " Test for the @: command diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3993, +/**/ 3992, /**/ 3991,