From 8f1efb018bff6ae95fae62e5ae943d6478e9c547 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 24 Jan 2022 12:46:52 +0800 Subject: vim-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 vim/vim#9462) https://github.com/vim/vim/commit/c88e977862ba6477a3b5b28706c45f96069a3073 --- src/nvim/getchar.c | 58 ++++++++++++++++++++++++++----------- src/nvim/normal.c | 7 ++++- src/nvim/testdir/test_registers.vim | 11 +++++++ 3 files changed, 58 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index ef590adb3a..72f7f9cd5c 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -284,9 +284,19 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle } } -/* - * Add number "n" to buffer "buf". - */ +/// 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 += (size_t)slen; + } +} + +/// Add number "n" to buffer "buf". static void add_num_buff(buffheader_T *buf, long n) { char number[32]; @@ -967,31 +977,31 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent return OK; } -/* - * Put character "c" back into the typeahead buffer. - * 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. - */ -void ins_char_typebuf(int c, int modifier) +/// Put character "c" back into the typeahead buffer. +/// 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. +/// @return the length of what was inserted +int ins_char_typebuf(int c, int modifier) { char_u buf[MB_MAXBYTES + 4]; - int idx = 0; + int len = 0; if (modifier != 0) { buf[0] = K_SPECIAL; buf[1] = KS_MODIFIER; buf[2] = (char_u)modifier; buf[3] = NUL; - idx = 3; + len = 3; } if (IS_SPECIAL(c)) { - buf[idx] = K_SPECIAL; - buf[idx + 1] = (char_u)K_SECOND(c); - buf[idx + 2] = (char_u)K_THIRD(c); - buf[idx + 3] = NUL; + buf[len] = K_SPECIAL; + buf[len + 1] = (char_u)K_SECOND(c); + buf[len + 2] = (char_u)K_THIRD(c); + buf[len + 3] = NUL; } else { - char_u *p = buf + idx; + char_u *p = buf + len; int char_len = utf_char2bytes(c, p); + len += char_len; // If the character contains K_SPECIAL bytes they need escaping. for (int i = char_len; --i >= 0; p++) { if ((uint8_t)(*p) == K_SPECIAL) { @@ -999,11 +1009,13 @@ void ins_char_typebuf(int c, int modifier) *p++ = K_SPECIAL; *p++ = KS_SPECIAL; *p = KE_FILLER; + len += 2; } } *p = NUL; } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); + return len; } /// Return TRUE if the typeahead buffer was changed (while waiting for a @@ -1163,6 +1175,18 @@ static void gotchars(const char_u *chars, size_t len) maptick++; } +/// 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 -= (size_t)len; + } +} + /* * Sync undo. Called when typed characters are obtained from the typeahead * buffer, or when a menu is used. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index c3b7e81d17..f802dce20d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1010,7 +1010,12 @@ static int normal_execute(VimState *state, int key) // 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(s->c, mod_mask); + int len = ins_char_typebuf(s->c, mod_mask); + + // When recording the character will be recorded again, remove the + // previously recording. + ungetchars(len); + if (restart_edit != 0) { s->c = 'd'; } else { diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 4371828a72..1be61d1519 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -121,6 +121,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 -- cgit From 5c897b6d0ceb7e7d40c0b740ab36d38bf3a4162d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 24 Jan 2022 12:46:52 +0800 Subject: vim-patch:8.2.4002: first char typed in Select mode can be wrong Problem: First char typed in Select mode can be wrong. Solution: Escape special bytes in the input buffer. (closes vim/vim#9469) https://github.com/vim/vim/commit/6cac77016b1636e04073e8348b7cee02259ef928 The `buf` should already be large enough, but I'll change its size anyway in case future patches change the meaning of `MB_MAXBYTES` macro. `fix_input_buffer()` cannot be used here because of the `using_script()` check, and there is already equivalent code in its place. --- src/nvim/getchar.c | 2 +- src/nvim/testdir/test_utf8.vim | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 72f7f9cd5c..95720c498a 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -984,7 +984,7 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent /// @return the length of what was inserted int ins_char_typebuf(int c, int modifier) { - char_u buf[MB_MAXBYTES + 4]; + char_u buf[MB_MAXBYTES * 3 + 4]; int len = 0; if (modifier != 0) { buf[0] = K_SPECIAL; diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index 0818c2e4b0..36776d5a64 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -1,5 +1,6 @@ " Tests for Unicode manipulations +source check.vim source view_util.vim " Visual block Insert adjusts for multi-byte char @@ -148,4 +149,55 @@ func Test_print_overlong() bwipe! endfunc +func Test_recording_with_select_mode_utf8() + call Run_test_recording_with_select_mode_utf8() +endfunc + +func Run_test_recording_with_select_mode_utf8() + new + + " No escaping + call feedkeys("qacc12345\gH哦\q", "tx") + call assert_equal("哦", getline(1)) + call assert_equal("cc12345\gH哦\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("哦", getline(1)) + + " 固 is 0xE5 0x9B 0xBA where 0x9B is CSI + call feedkeys("qacc12345\gH固\q", "tx") + call assert_equal("固", getline(1)) + call assert_equal("cc12345\gH固\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("固", getline(1)) + + " 四 is 0xE5 0x9B 0x9B where 0x9B is CSI + call feedkeys("qacc12345\gH四\q", "tx") + call assert_equal("四", getline(1)) + call assert_equal("cc12345\gH四\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("四", getline(1)) + + " 倒 is 0xE5 0x80 0x92 where 0x80 is K_SPECIAL + call feedkeys("qacc12345\gH倒\q", "tx") + call assert_equal("倒", getline(1)) + call assert_equal("cc12345\gH倒\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("倒", getline(1)) + + bwipe! +endfunc + +" This must be done as one of the last tests, because it starts the GUI, which +" cannot be undone. +func Test_zz_recording_with_select_mode_utf8_gui() + CheckCanRunGui + + gui -f + call Run_test_recording_with_select_mode_utf8() +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit