engine: the Hanja search composes from its seed, and Escape gives it back

Ctrl+H took the pending syllable as the query, but the keys typed after
it started a composition of their own: gk, Ctrl+H, s showed 하ㄴ, and r,
Ctrl+H, k showed ㄱㅏ.  transstr now composes from a pending text, so
the search goes on where the syllable left off.  Escape ended the search
and dropped the syllable it had taken; it puts it back as pending text.
This commit is contained in:
2026-08-17 01:24:14 +09:00
parent 1285476b41
commit 852e129f62
6 changed files with 42 additions and 18 deletions

View File

@@ -38,7 +38,8 @@ syllable and goes on to the application, so it still leaves insert mode.
Hiragana mode composes a complete reading before offering Kanji candidates.
Katakana mode does not perform Kanji conversion. Emoji and Hanja searches
return to the previous mode after use; switching language during a search
commits the shown query. The Emoji search matches the typed keys, and what
commits the shown query; `Esc` in a Hanja search gives the syllable back
to the composition. The Emoji search matches the typed keys, and what
they spell in the current language, against a prefix of every emoji's
CLDR name and keywords in English, Korean, and Japanese, and against ASCII
symbol aliases such as `->` and `<=`. `Ctrl+H` takes the syllable being composed as its

2
fn.h
View File

@@ -31,7 +31,7 @@ void popuplayout(Drawcmd*, int, int, Popup*);
void popupdraw(u32int*, Drawcmd*, Popup*);
void imthread(void*);
void impre(Im*, Str*);
void transstr(Lang*, Str*, Str*);
void transstr(Lang*, Str*, Str*, Str*);
Emit transmap(Im*, Rune);
Emit transko(Im*, Rune);
Emit transvi(Im*, Rune);

View File

@@ -526,21 +526,27 @@ foldascii(Str *s)
s->r[i] += 'a' - 'A';
}
/* What typing raw in language l would produce, composition included. */
/*
* What typing raw in language l would produce, composition included,
* after pre, which may be nil, was already pending.
*/
void
transstr(Lang *l, Str *raw, Str *out)
transstr(Lang *l, Str *pre, Str *raw, Str *out)
{
Im q;
Emit e;
int i;
sclear(out);
if(l->trans == nil){
*out = *raw;
return;
}
memset(&q, 0, sizeof q);
q.l = l;
if(pre != nil)
q.pre = *pre;
sclear(out);
if(l->trans == nil){
sappend(out, &q.pre);
sappend(out, raw);
return;
}
for(i = 0; i < raw->n; i++){
e = l->trans(&q, raw->r[i]);
sappend(out, &e.s);
@@ -561,7 +567,7 @@ emojiquery(void)
raw = search.raw;
foldascii(&raw);
transstr(im.l, &search.raw, &local);
transstr(im.l, nil, &search.raw, &local);
foldascii(&local);
clearkouho();
n = dictlookup(getlang(LangEMOJI), &raw, kouho, Maxkouho);
@@ -579,14 +585,11 @@ emojiquery(void)
selectfirst();
}
/* The keys typed since Ctrl+H go on composing from the seed. */
static void
hanjaquery(void)
{
Str key;
transstr(im.l, &search.raw, &key);
search.text = search.seed;
sappend(&search.text, &key);
transstr(im.l, &search.seed, &search.raw, &search.text);
clearkouho();
im.nkouho = dictlookup(getlang(LangHANJA), &search.text, im.kouho,
Maxkouho);
@@ -711,6 +714,7 @@ searchkey(u32int ks, u32int mod, Str *com)
return 1;
}
if(ks == Kesc){
im.pre = search.seed; /* the syllable Ctrl+H took is pending again */
endsearch();
return 1;
}

View File

@@ -1101,7 +1101,7 @@ engine_japanese_readings(struct ct *t)
}
for(i = 0; i < nelem(replay); i++){
raw = mkstr(replay[i].raw);
transstr(getlang(LangJP), &raw, &shown);
transstr(getlang(LangJP), nil, &raw, &shown);
checkstr(t, replay[i].raw, replay[i].want, &shown);
}
}
@@ -1891,6 +1891,25 @@ engine_hanja_backspace(struct ct *t)
CT_CHECK(t, keystroke(Kback, 0, &com));
CT_EQ_INT(t, 0, search.lang);
CT_EQ_INT(t, LangKO, im.l->lang);
/* Escape gives the seed back; keys typed compose from it. */
if(!typekeys(t, "gks", &com))
goto cleanup;
CT_CHECK(t, keystroke('h', Mctrl, &com));
CT_CHECK(t, keystroke(Kesc, 0, &com));
CT_EQ_INT(t, 0, search.lang);
checkstr(t, "seed pending again", "", &im.pre);
checkstr(t, "nothing committed", "", &com);
CT_CHECK(t, keystroke(Kback, 0, &com));
CT_CHECK(t, keystroke('h', Mctrl, &com));
checkstr(t, "seed 하", "", &search.text);
CT_CHECK(t, keystroke('s', 0, &com));
checkstr(t, "key composes with the seed", "", &search.text);
CT_EQ_INT(t, 2, im.nkouho);
CT_CHECK(t, keystroke(Kesc, 0, &com));
checkstr(t, "seed alone comes back", "", &im.pre);
CT_CHECK(t, !keystroke(Kesc, 0, &com));
checkstr(t, "Korean Escape commits", "", &com);
CT_EQ_INT(t, 0, im.pre.n);
cleanup:
searchend(&f);
}

View File

@@ -8,7 +8,7 @@ kotrans(char *keys)
Str out, raw;
raw = mkstr(keys);
transstr(getlang(LangKO), &raw, &out);
transstr(getlang(LangKO), nil, &raw, &out);
return out;
}

View File

@@ -8,7 +8,7 @@ typevi(struct ct *t, char *keys, char *want)
Str out, raw;
raw = mkstr(keys);
transstr(&testvi, &raw, &out);
transstr(&testvi, nil, &raw, &out);
checkstr(t, keys, want, &out);
}