# HG changeset patch # User Christian Brabandt # Date 1711703708 -3600 # Node ID f8f45612de4eb5506b7e8f80dce1ee820a477b1d # Parent 4e5721fb12d62d9f966941831bcc910f8877ec3e patch 9.1.0227: Recording may still be wrong in Select mode Commit: https://github.com/vim/vim/commit/ea95f1a5ad2455c7fd1eee2413ac7a3460ef4f8a Author: zeertzjq Date: Fri Mar 29 10:11:42 2024 +0100 patch 9.1.0227: Recording may still be wrong in Select mode Problem: Recording may still be wrong in Select mode (after 8.2.3993). Solution: Make sure a character isn't split between two buffer blocks. (zeertzjq) closes: #14326 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt diff --git a/src/getchar.c b/src/getchar.c --- a/src/getchar.c +++ b/src/getchar.c @@ -1283,35 +1283,72 @@ del_typebuf(int len, int offset) /* * Write typed characters to script file. - * If recording is on put the character in the recordbuffer. + * If recording is on put the character in the record buffer. */ static void gotchars(char_u *chars, int len) { char_u *s = chars; - int i; - static char_u buf[4]; - static int buflen = 0; + size_t i; + int c = NUL; + static int prev_c = NUL; + static char_u buf[MB_MAXBYTES * 3 + 4]; + static size_t buflen = 0; + static unsigned pending = 0; + static int in_special = FALSE; + static int in_mbyte = FALSE; int todo = len; - while (todo--) + for (; todo--; prev_c = c) { - buf[buflen++] = *s++; + c = buf[buflen++] = *s++; + if (pending > 0) + pending--; // When receiving a special key sequence, store it until we have all // the bytes and we can decide what to do with it. - if (buflen == 1 && buf[0] == K_SPECIAL) - continue; - if (buflen == 2) + if ((pending == 0 || in_mbyte) + && (c == K_SPECIAL +#ifdef FEAT_GUI + || c == CSI +#endif + )) + { + pending += 2; + if (!in_mbyte) + in_special = TRUE; + } + + if (pending > 0) continue; - if (buflen == 3 && buf[1] == KS_EXTRA - && (buf[2] == KE_FOCUSGAINED || buf[2] == KE_FOCUSLOST)) + + if (!in_mbyte) { - // Drop K_FOCUSGAINED and K_FOCUSLOST, they are not useful in a - // recording. - buflen = 0; - continue; + if (in_special) + { + in_special = FALSE; + if (prev_c == KS_MODIFIER) + // When receiving a modifier, wait for the modified key. + continue; + c = TO_SPECIAL(prev_c, c); + if (c == K_FOCUSGAINED || c == K_FOCUSLOST) + // Drop K_FOCUSGAINED and K_FOCUSLOST, they are not useful + // in a recording. + buflen = 0; + } + // When receiving a multibyte character, store it until we have all + // the bytes, so that it won't be split between two buffer blocks, + // and delete_buff_tail() will work properly. + pending = MB_BYTE2LEN_CHECK(c) - 1; + if (pending > 0) + { + in_mbyte = TRUE; + continue; + } } + else + // Stored all bytes of a multibyte character. + in_mbyte = FALSE; // Handle one byte at a time; no translation to be done. for (i = 0; i < buflen; ++i) @@ -1326,6 +1363,7 @@ gotchars(char_u *chars, int len) } buflen = 0; } + may_sync_undo(); #ifdef FEAT_EVAL 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 @@ -209,6 +209,58 @@ func Test_recording_with_select_mode() bwipe! endfunc +func Run_test_recording_with_select_mode_utf8() + new + + " Test with different text lengths: 5, 7, 9, 11, 13, 15, to check that + " a character isn't split between two buffer blocks. + for s in ['12345', '口=口', '口口口', '口=口=口', '口口=口口', '口口口口口'] + " 0x80 is K_SPECIAL + " 0x9B is CSI + " 哦: 0xE5 0x93 0xA6 + " 洛: 0xE6 0xB4 0x9B + " 固: 0xE5 0x9B 0xBA + " 四: 0xE5 0x9B 0x9B + " 最: 0xE6 0x9C 0x80 + " 倒: 0xE5 0x80 0x92 + " 倀: 0xE5 0x80 0x80 + for c in ['哦', '洛', '固', '四', '最', '倒', '倀'] + call setline(1, 'asdf') + call feedkeys($"qacc{s}\gH{c}\q", "tx") + call assert_equal(c, getline(1)) + call assert_equal($"cc{s}\gH{c}\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal(c, getline(1)) + + " Test with Shift modifier. + let shift_c = eval($'"\"') + call setline(1, 'asdf') + call feedkeys($"qacc{s}\gH{shift_c}\q", "tx") + call assert_equal(c, getline(1)) + call assert_equal($"cc{s}\gH{shift_c}\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal(c, getline(1)) + endfor + endfor + + bwipe! +endfunc + +func Test_recording_with_select_mode_utf8() + call Run_test_recording_with_select_mode_utf8() +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 + " Test for executing the last used register (@) func Test_last_used_exec_reg() " Test for the @: command diff --git a/src/testdir/test_utf8.vim b/src/testdir/test_utf8.vim --- a/src/testdir/test_utf8.vim +++ b/src/testdir/test_utf8.vim @@ -292,55 +292,4 @@ 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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 227, +/**/ 226, /**/ 225,