From 0f77e0e9ac64c18af31d29b71086591e83af9c52 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 18 Aug 2026 16:25:55 +0900 Subject: [PATCH] engine: the chosen candidate is what the client shows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The preedit sent back to the owner was always impre(), the reading, so かく stayed underlined in the application while the popup showed 書く and Enter inserted 書く. Nothing was hidden -- the popup had it -- but the text did not stand where it was going to land, and mozc, fcitx5 and kime all put the candidate inline. Only the reply changes. impre() still answers the reading everywhere it is the reading that is wanted: commitim, the okurigana mark, the seed Ctrl+H takes, and snapshot, so a popup that draws the preedit itself keeps the reading above the list where it says something the highlighted row does not. Consulting im.sel here is safe where consulting it inside impre() would not be: transstr memsets a local Im, whose sel is 0 and not -1, and this reply only ever reads the engine's own. Nothing in the suite pinned the old answer, so the test is new. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 21 +++++++++++---------- strans.c | 2 ++ tests/engine_test.c | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9afe82e..8950a4c 100644 --- a/README.md +++ b/README.md @@ -33,16 +33,17 @@ Japanese composes a whole reading and then offers its Kanji with none chosen. `Space` and `Tab` step through the candidates and wrap (`Shift` reverses), the reading in Katakana last, so a word the dictionary lacks converts with one `Space`; `Up`/`Down` and `PageUp`/`PageDown` move without -wrapping. `Enter` commits the chosen candidate, or the reading, as `0` and -typing on always do. Once a candidate is chosen `1`-`9` take that row of -the page shown; before that the digits type. `Backspace` deletes the last -kana shown, and romaji that made no kana stays as typed until it is mended; -`Esc` cancels the reading, and on a chosen candidate both go back to it. -The dictionary keys verbs and adjectives by stem and okurigana, so `kaku` -offers かく's readings and then 書く and its kin — typing the okurigana -with `Shift`, as SKK does, `kaKu`, asks for that split first. Katakana -mode does not convert, so there `Space` and `Tab` commit the reading and -go on to the application. +wrapping. A chosen candidate takes the reading's place in the preedit, so +it stands where it will land, and `Enter` commits it, or the reading, as +`0` and typing on always do. Once a candidate is chosen `1`-`9` take that +row of the page shown; before that the digits type. `Backspace` deletes +the last kana shown, and romaji that made no kana stays as typed until it +is mended; `Esc` cancels the reading, and on a chosen candidate both go +back to it. The dictionary keys verbs and adjectives by stem and +okurigana, so `kaku` offers かく's readings and then 書く and its kin — +typing the okurigana with `Shift`, as SKK does, `kaKu`, asks for that +split first. Katakana mode does not convert, so there `Space` and `Tab` +commit the reading and go on to the application. Korean composes one syllable at a time. `Enter`, `Tab`, `Esc` and any key that is not a jamo commit the syllable and go on to the application, so diff --git a/strans.c b/strans.c index ade368e..926a43e 100644 --- a/strans.c +++ b/strans.c @@ -1097,6 +1097,8 @@ imhandlekey(Keyreq *kr) if(kr->owner == activeowner){ if(search.lang) searchpre(&res.preedit); + else if(im.sel >= 0) + res.preedit = im.kouho[im.sel]; /* what Enter would commit */ else impre(&im, &res.preedit); } diff --git a/tests/engine_test.c b/tests/engine_test.c index 2c3e60d..dff1bb7 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -411,6 +411,7 @@ engine_commit_contract(struct ct *t) { 'd', "ㅇ", "ㅇㅇㅇ" }, { 'k', "ㅏ", "ㅏㅏㅏ" }, }; + Drawcmd dc; Keyres res; Str all, com; char owner; @@ -495,6 +496,26 @@ engine_commit_contract(struct ct *t) CT_CHECK(t, !keystroke(0xf008, 0, &com)); checkstr(t, "non-ASCII key commit", "か", &com); CT_EQ_INT(t, 0, im.pre.n); + + /* A chosen candidate is what the client shows and what Enter + * commits; the popup keeps the reading above the list. */ + init(); + im.l = getlang(LangJP); + draindraw(nil); + ownerrequestcap(&owner, 1, Keypress, 'k', 0); + res = ownerrequestcap(&owner, 1, Keypress, 'a', 0); + checkstr(t, "the reading before a candidate", "か", &res.preedit); + im.kouho[0] = mkstr("蚊"); + im.nkouho = 1; + res = ownerrequestcap(&owner, 1, Keypress, Kdown, 0); + checkstr(t, "the candidate the client shows", "蚊", &res.preedit); + clientpre = 0; + redraw(); + if(CT_CHECK(t, draindraw(&dc) > 0)) + checkstr(t, "the popup keeps the reading", "か", &dc.pre); + res = ownerrequestcap(&owner, 1, Keypress, Kret, 0); + checkstr(t, "Enter commits what was shown", "蚊", &res.commit); + CT_EQ_INT(t, 0, res.preedit.n); } void