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 <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
zeertzjq
2024-03-29 10:11:42 +01:00
committed by Christian Brabandt
parent abedca96ef
commit ea95f1a5ad
4 changed files with 108 additions and 67 deletions

View File

@ -1283,36 +1283,73 @@ 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)
continue;
if (buflen == 3 && buf[1] == KS_EXTRA
&& (buf[2] == KE_FOCUSGAINED || buf[2] == KE_FOCUSLOST))
if ((pending == 0 || in_mbyte)
&& (c == K_SPECIAL
#ifdef FEAT_GUI
|| c == CSI
#endif
))
{
// Drop K_FOCUSGAINED and K_FOCUSLOST, they are not useful in a
// recording.
buflen = 0;
continue;
pending += 2;
if (!in_mbyte)
in_special = TRUE;
}
if (pending > 0)
continue;
if (!in_mbyte)
{
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)
updatescript(buf[i]);
@ -1326,6 +1363,7 @@ gotchars(char_u *chars, int len)
}
buflen = 0;
}
may_sync_undo();
#ifdef FEAT_EVAL

View File

@ -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}\<Esc>gH{c}\<Esc>q", "tx")
call assert_equal(c, getline(1))
call assert_equal($"cc{s}\<Esc>gH{c}\<Esc>", @a)
call setline(1, 'asdf')
normal! @a
call assert_equal(c, getline(1))
" Test with Shift modifier.
let shift_c = eval($'"\<S-{c}>"')
call setline(1, 'asdf')
call feedkeys($"qacc{s}\<Esc>gH{shift_c}\<Esc>q", "tx")
call assert_equal(c, getline(1))
call assert_equal($"cc{s}\<Esc>gH{shift_c}\<Esc>", @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

View File

@ -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\<Esc>gH哦\<Esc>q", "tx")
call assert_equal("哦", getline(1))
call assert_equal("cc12345\<Esc>gH哦\<Esc>", @a)
call setline(1, 'asdf')
normal! @a
call assert_equal("哦", getline(1))
" 固 is 0xE5 0x9B 0xBA where 0x9B is CSI
call feedkeys("qacc12345\<Esc>gH固\<Esc>q", "tx")
call assert_equal("固", getline(1))
call assert_equal("cc12345\<Esc>gH固\<Esc>", @a)
call setline(1, 'asdf')
normal! @a
call assert_equal("固", getline(1))
" 四 is 0xE5 0x9B 0x9B where 0x9B is CSI
call feedkeys("qacc12345\<Esc>gH四\<Esc>q", "tx")
call assert_equal("四", getline(1))
call assert_equal("cc12345\<Esc>gH四\<Esc>", @a)
call setline(1, 'asdf')
normal! @a
call assert_equal("四", getline(1))
" 倒 is 0xE5 0x80 0x92 where 0x80 is K_SPECIAL
call feedkeys("qacc12345\<Esc>gH倒\<Esc>q", "tx")
call assert_equal("倒", getline(1))
call assert_equal("cc12345\<Esc>gH倒\<Esc>", @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

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
227,
/**/
226,
/**/