From d3d46c8cb1806425ffa587046c79d5fef75f7675 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 18 Aug 2026 16:02:52 +0900 Subject: [PATCH] engine: Backspace gives the reach back before what was typed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit searchkey popped search.raw and then search.seed but never touched search.back, so once the typed syllable was gone the query was nothing but the client's own text: an empty preedit above a full candidate list drawn from syllables the user had not selected, and Enter rewrote them. With 상 written and 태 typed, Ctrl+H offered 狀態; one Backspace left the query 상 and 128 candidates, and Enter replaced the 상 with 上 and dropped the 태 altogether. Undo now runs backwards through what happened -- the keys typed since Ctrl+H, then the reach Ctrl+H made, then the syllable that seeded it -- so the same Backspace narrows 상태 to 태 and offers 太, which is also the only way there was ever going to be to convert the syllable alone. Co-Authored-By: Claude Opus 5 (1M context) --- strans.c | 2 ++ tests/engine_test.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/strans.c b/strans.c index 6135023..94b6f9f 100644 --- a/strans.c +++ b/strans.c @@ -848,6 +848,8 @@ searchkey(u32int ks, u32int mod, Str *com) if(ks == Kback){ if(search.raw.n != 0) spopr(&search.raw); + else if(search.back.n != 0) + sclear(&search.back); /* the reach undone, in its turn */ else if(search.seed.n != 0) spopr(&search.seed); else{ diff --git a/tests/engine_test.c b/tests/engine_test.c index c9468dd..3691c7f 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -2005,6 +2005,21 @@ engine_hanja_reaches_back(struct ct *t) CT_EQ_INT(t, 0, takeback); } + /* Backspace gives the reach back before it eats what was typed. */ + surround = mkstr("민"); + im.pre = mkstr("국"); + sclear(&com); + CT_CHECK(t, keystroke('h', Mctrl, &com)); + checkstr(t, "the reach joined the reading", "민국", &search.text); + CT_CHECK(t, keystroke(Kback, 0, &com)); + checkstr(t, "the client's syllable goes back first", "국", + &search.text); + CT_EQ_INT(t, 0, search.back.n); + sclear(&com); + CT_CHECK(t, keystroke(Kret, 0, &com)); + checkstr(t, "only what was typed is committed", "국", &com); + CT_EQ_INT(t, 0, takeback); + /* Nothing pending is no reading, and reaches into nothing. */ surround = mkstr("민"); sclear(&im.pre);