fix(engine): send clients the composed Telex text, not the keys

For Vietnamese the preedit is the map key ('as', 'oong'); only the popup
and the commit path looked it up. Clients with inline preedit therefore
showed 'as' while Enter committed 'á'. impre() now maps once for every
reader, and snapshot() no longer maps a second time.
This commit is contained in:
2026-08-16 15:57:55 +09:00
parent c7718ece52
commit ebcec3af6b
4 changed files with 52 additions and 10 deletions

View File

@@ -134,12 +134,13 @@ haspre(Im *p)
return p->pre.n != 0 || (isjp(p) && p->raw.n != 0); return p->pre.n != 0 || (isjp(p) && p->raw.n != 0);
} }
/* The preedit as clients see it: Telex keys read back through the map. */
static void static void
impre(Str *s) impre(Str *s)
{ {
if(isjp(&im)) if(isjp(&im))
jpreading(&im, s); jpreading(&im, s);
else else if(!mapget(im.l->map, &im.pre, s))
*s = im.pre; *s = im.pre;
} }
@@ -167,11 +168,8 @@ snapshot(Drawcmd *dc)
pre = search.text; pre = search.text;
else else
impre(&pre); impre(&pre);
if(!(activecap & Cclientpreedit)){ if(!(activecap & Cclientpreedit))
if(search.lang || isjp(&im) || im.l->map == nil ||
!mapget(im.l->map, &pre, &dc->pre))
dc->pre = pre; dc->pre = pre;
}
first = pagefirst(); first = pagefirst();
n = im.nkouho - first; n = im.nkouho - first;
if(n > Maxdisp) n = Maxdisp; if(n > Maxdisp) n = Maxdisp;
@@ -220,10 +218,7 @@ dictsend(Im *im, Str *key)
req.key = *key; req.key = *key;
req.lang = im->l->lang; req.lang = im->l->lang;
req.seq = dictseq; req.seq = dictseq;
if(isjp(im)) impre(&req.pre);
jpreading(im, &req.pre);
else
req.pre = im->pre;
/* imthread is the sole producer; discard every obsolete queued lookup. */ /* imthread is the sole producer; discard every obsolete queued lookup. */
while(channbrecv(dictreqc, &old) > 0) while(channbrecv(dictreqc, &old) > 0)
; ;

View File

@@ -417,6 +417,51 @@ engine_active_owner_caret(struct ct *t)
CT_CHECK(t, !caret.valid); CT_CHECK(t, !caret.valid);
} }
void
engine_vietnamese_client_preedit(struct ct *t)
{
static const struct {
char *keys;
char *shown;
} cases[] = {
{ "as", "á" },
{ "aa", "â" },
{ "ddoong", "đông" },
{ "hoaf", "hoà" },
{ "quas", "quá" },
};
Drawcmd dc;
Keyres res;
Str com, shown;
char *k, owner;
int i;
for(i = 0; i < nelem(cases); i++){
init();
im.l = &testvi;
draindraw(nil);
sclear(&com);
for(k = cases[i].keys; *k != '\0'; k++){
res = ownerrequestcap(&owner, Cclientpreedit,
Keypress, *k, 0);
sappend(&com, &res.commit);
if(!res.eaten)
sputr(&com, *k);
}
/* What the client shows, what the popup shows, and what
* Enter commits are the same composed text. */
shown = com;
sappend(&shown, &res.preedit);
checkstr(t, "client preedit", cases[i].shown, &shown);
activecap = 0;
redraw();
if(CT_CHECK(t, draindraw(&dc) > 0))
CT_EQ_INT(t, 0, scmp(&dc.pre, &res.preedit));
CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "commit", cases[i].shown, &com);
}
}
void void
engine_commit_contract(struct ct *t) engine_commit_contract(struct ct *t)
{ {

View File

@@ -39,6 +39,7 @@ void engine_active_owner_reset(struct ct*);
void engine_rejects_stale_dictionary_results(struct ct*); void engine_rejects_stale_dictionary_results(struct ct*);
void engine_active_owner_caret(struct ct*); void engine_active_owner_caret(struct ct*);
void engine_popup_preedit_capability(struct ct*); void engine_popup_preedit_capability(struct ct*);
void engine_vietnamese_client_preedit(struct ct*);
void engine_commit_contract(struct ct*); void engine_commit_contract(struct ct*);
void engine_language_switch_state(struct ct*); void engine_language_switch_state(struct ct*);
void engine_telex_history_bound(struct ct*); void engine_telex_history_bound(struct ct*);

View File

@@ -94,6 +94,7 @@ static const struct ct_test tests[] = {
{ "engine/rejects-stale-dictionary", engine_rejects_stale_dictionary_results }, { "engine/rejects-stale-dictionary", engine_rejects_stale_dictionary_results },
{ "engine/active-owner-caret", engine_active_owner_caret }, { "engine/active-owner-caret", engine_active_owner_caret },
{ "engine/popup-preedit-capability", engine_popup_preedit_capability }, { "engine/popup-preedit-capability", engine_popup_preedit_capability },
{ "engine/vietnamese-client-preedit", engine_vietnamese_client_preedit },
{ "engine/commit-contract", engine_commit_contract }, { "engine/commit-contract", engine_commit_contract },
{ "engine/language-switch-state", engine_language_switch_state }, { "engine/language-switch-state", engine_language_switch_state },
{ "engine/telex-history-bound", engine_telex_history_bound }, { "engine/telex-history-bound", engine_telex_history_bound },