engine: Backspace gives the reach back before what was typed

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) <noreply@anthropic.com>
This commit is contained in:
2026-08-18 16:02:52 +09:00
parent b08f3bb7a2
commit d3d46c8cb1
2 changed files with 17 additions and 0 deletions

View File

@@ -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{

View File

@@ -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);