From 852e129f62b2e9f9bf0d1f8148a6f32c942042a8 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 17 Aug 2026 01:24:14 +0900 Subject: [PATCH] engine: the Hanja search composes from its seed, and Escape gives it back MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- README.md | 3 ++- fn.h | 2 +- strans.c | 30 +++++++++++++++++------------- tests/engine_test.c | 21 ++++++++++++++++++++- tests/ko_test.c | 2 +- tests/vi_test.c | 2 +- 6 files changed, 42 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 366c67b..f2bdae0 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/fn.h b/fn.h index 0605aa3..006241c 100644 --- a/fn.h +++ b/fn.h @@ -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); diff --git a/strans.c b/strans.c index 0076964..da1c7ad 100644 --- a/strans.c +++ b/strans.c @@ -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; } diff --git a/tests/engine_test.c b/tests/engine_test.c index 05c1475..032d006 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -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); } diff --git a/tests/ko_test.c b/tests/ko_test.c index 78d76fb..cef59a2 100644 --- a/tests/ko_test.c +++ b/tests/ko_test.c @@ -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; } diff --git a/tests/vi_test.c b/tests/vi_test.c index 7d21584..dea8cef 100644 --- a/tests/vi_test.c +++ b/tests/vi_test.c @@ -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); }