Terminal: decode UTF-8 input in kte; visual-line edits keep characters whole
- TerminalInputHandler reads bytes with getch() and assembles UTF-8 itself, waiting up to a second for a character's continuation bytes. The frontend reads the rest of an input batch with a zero timeout, and get_wch() then gave up on a character whose bytes had not all arrived; ncurses discarded the rest as an invalid sequence, so text pasted over a slow link silently lost characters and what followed them (typing 'ab\xc3', then '\xa9cd' 50 ms later saved 'ab'). - Visual-line insert, delete, backspace and newline carried the cursor's byte column to the other selected lines, splitting multibyte characters there (the saved file became invalid UTF-8). The column is now carried as a character count. - Visual-line delete-char left the cursor on the last selected line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
@@ -1400,3 +1400,44 @@ TEST(Audit_VisualYank_CursorStaysInBuffer)
|
||||
(void) h.Exec(CommandId::Undo);
|
||||
ASSERT_EQ(h.Text(), std::string("abc def\n"));
|
||||
}
|
||||
|
||||
|
||||
// Visual-line edits apply at the cursor's column on every selected line; a
|
||||
// byte column taken from an ASCII line fell inside the CJK characters of
|
||||
// the next line and split them. The column is now carried in characters.
|
||||
TEST(Audit_VisualLine_EditsKeepUtf8Whole)
|
||||
{
|
||||
const std::string cjk = "\xE4\xB8\x96\xE7\x95\x8C"; // two 3-byte characters
|
||||
{
|
||||
TestHarness h;
|
||||
h.Buf().insert_text(0, 0, "abcdef\n" + cjk + "\n");
|
||||
h.Buf().SetCursor(0, 1);
|
||||
ASSERT_TRUE(h.Exec(CommandId::VisualLineModeToggle));
|
||||
ASSERT_TRUE(h.Exec(CommandId::MoveUp));
|
||||
ASSERT_TRUE(h.Exec(CommandId::MoveRight));
|
||||
ASSERT_TRUE(h.Exec(CommandId::InsertText, "X"));
|
||||
ASSERT_EQ(h.Text(), std::string("aXbcdef\n\xE4\xB8\x96X\xE7\x95\x8C\n"));
|
||||
}
|
||||
{
|
||||
TestHarness h;
|
||||
h.Buf().insert_text(0, 0, "abcdef\n" + cjk + "\n");
|
||||
h.Buf().SetCursor(0, 1);
|
||||
ASSERT_TRUE(h.Exec(CommandId::VisualLineModeToggle));
|
||||
ASSERT_TRUE(h.Exec(CommandId::MoveUp));
|
||||
ASSERT_TRUE(h.Exec(CommandId::DeleteChar));
|
||||
ASSERT_EQ(h.Text(), std::string("bcdef\n\xE7\x95\x8C\n"));
|
||||
ASSERT_TRUE(h.Exec(CommandId::MoveRight));
|
||||
ASSERT_TRUE(h.Exec(CommandId::Backspace));
|
||||
ASSERT_EQ(h.Text(), std::string("cdef\n\n"));
|
||||
}
|
||||
{
|
||||
TestHarness h;
|
||||
h.Buf().insert_text(0, 0, "abcdef\n" + cjk + "\n");
|
||||
h.Buf().SetCursor(0, 1);
|
||||
ASSERT_TRUE(h.Exec(CommandId::VisualLineModeToggle));
|
||||
ASSERT_TRUE(h.Exec(CommandId::MoveUp));
|
||||
ASSERT_TRUE(h.Exec(CommandId::MoveRight));
|
||||
ASSERT_TRUE(h.Exec(CommandId::Newline));
|
||||
ASSERT_EQ(h.Text(), std::string("a\nbcdef\n\xE4\xB8\x96\n\xE7\x95\x8C\n"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user