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. Hiragana mode composes a complete reading before offering Kanji candidates.
Katakana mode does not perform Kanji conversion. Emoji and Hanja searches Katakana mode does not perform Kanji conversion. Emoji and Hanja searches
return to the previous mode after use; switching language during a search 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 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 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 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 popupdraw(u32int*, Drawcmd*, Popup*);
void imthread(void*); void imthread(void*);
void impre(Im*, Str*); void impre(Im*, Str*);
void transstr(Lang*, Str*, Str*); void transstr(Lang*, Str*, Str*, Str*);
Emit transmap(Im*, Rune); Emit transmap(Im*, Rune);
Emit transko(Im*, Rune); Emit transko(Im*, Rune);
Emit transvi(Im*, Rune); Emit transvi(Im*, Rune);

View File

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

View File

@@ -1101,7 +1101,7 @@ engine_japanese_readings(struct ct *t)
} }
for(i = 0; i < nelem(replay); i++){ for(i = 0; i < nelem(replay); i++){
raw = mkstr(replay[i].raw); 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); 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_CHECK(t, keystroke(Kback, 0, &com));
CT_EQ_INT(t, 0, search.lang); CT_EQ_INT(t, 0, search.lang);
CT_EQ_INT(t, LangKO, im.l->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: cleanup:
searchend(&f); searchend(&f);
} }

View File

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

View File

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