From d9e27e8b0187b3da46ccce7a8c9888194b42e9b5 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 18 Aug 2026 13:54:42 +0900 Subject: [PATCH] engine: a Hanja reading answers with the words it begins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hanjaquery matched the reading exactly, so a word was reachable only once every syllable of it had been typed, and the popup went blank on the way there. Measured over map/hanja.dict's 187,304 readings: 95,024 proper prefixes of a word answer with nothing today, and 66,731 of those are the keystroke just before the word completes -- 대한민 is one, so 대한민국 looks absent until the last key lands. dictprefix walks the entry at a node before its children, so the reading's own conversions keep their place and the words follow: 34,441 readings gain candidates and none of the 187,304 has its existing order changed. A jamo reading is untouched, since the two keyspaces do not meet and ㅁ has no children. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 4 +++- strans.c | 9 +++++++-- tests/engine_test.c | 28 ++++++++++++++++++++++++++++ tests/test.h | 1 + tests/unit_test.c | 1 + 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2004fc..c68633b 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,9 @@ emoji's CLDR name and keywords in English, Korean and Japanese, and against ASCII aliases such as `->` and `<=`. `Ctrl+H` takes the syllable being composed as its query and composes on from it, converting a word as well as a syllable — 한자 gives 漢字, 대한민국 gives 大韓民國 — and `Esc` gives the -syllable back. A lone consonant is a reading too, and answers with the +syllable back. A reading is also the start of the longer words it begins, +whose conversions follow its own, so 대한 answers 大寒 first and 大韓民國 +further down. A lone consonant is a reading too, and answers with the symbol table a Korean keyboard's 한자 key has always offered: ㅁ gives ※ ○ △ ㈜, ㄴ the brackets 「」『』, ㄹ the units ℃ ㎏ ℓ, ㅇ the circled numbers ①②③. Text already committed belongs to the application and cannot be diff --git a/strans.c b/strans.c index 5d240b3..f390ed9 100644 --- a/strans.c +++ b/strans.c @@ -627,13 +627,18 @@ emojiquery(void) selectfirst(); } -/* The keys typed since Ctrl+H go on composing from the seed. */ +/* + * The keys typed since Ctrl+H go on composing from the seed. A reading + * is also the start of the longer words it begins, so its own + * conversions come first and theirs follow: 대한 answers 大寒 and, past + * them, 大韓民國. + */ static void hanjaquery(void) { transstr(im.l, &search.seed, &search.raw, &search.text); clearkouho(); - im.nkouho = dictlookup(getlang(LangHANJA)->dict, &search.text, + im.nkouho = dictprefix(getlang(LangHANJA)->dict, &search.text, im.kouho, Maxkouho); selectfirst(); } diff --git a/tests/engine_test.c b/tests/engine_test.c index 91abe25..ec635c2 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -1104,6 +1104,8 @@ hanjabegin(Searchfix *f, int lang) f->dictlang->dict = trienew(); setdict(f->dictlang->dict, "한", "漢 韓"); setdict(f->dictlang->dict, "가", "家"); + setdict(f->dictlang->dict, "민", "民"); + setdict(f->dictlang->dict, "민국", "民國"); init(); im.l = getlang(lang); } @@ -1933,6 +1935,32 @@ cleanup: searchend(&f); } +/* + * A reading is the start of the longer words it begins: its own + * conversions come first, and past them theirs. + */ +void +engine_hanja_word_prefix(struct ct *t) +{ + Searchfix f; + Str com; + + hanjabegin(&f, LangKO); + sclear(&com); + if(!CT_CHECK(t, keystroke('h', Mctrl, &com)) || + !typekeys(t, "als", &com)) + goto cleanup; + checkstr(t, "the reading typed so far", "민", &search.text); + CT_EQ_INT(t, 2, im.nkouho); + checkstr(t, "the reading's own conversion", "民", &im.kouho[0]); + checkstr(t, "the word it begins", "民國", &im.kouho[1]); + CT_CHECK(t, keystroke(Kdown, 0, &com)); + CT_CHECK(t, keystroke(Kret, 0, &com)); + checkstr(t, "a word longer than the reading", "民國", &com); +cleanup: + searchend(&f); +} + void engine_hanja_korean_keys(struct ct *t) { diff --git a/tests/test.h b/tests/test.h index 1f5387d..4e2eb7b 100644 --- a/tests/test.h +++ b/tests/test.h @@ -86,6 +86,7 @@ void engine_emoji_preedit_languages(struct ct*); void engine_emoji_start_and_unknown(struct ct*); void engine_hanja_search(struct ct*); void engine_hanja_unknown_and_cancel(struct ct*); +void engine_hanja_word_prefix(struct ct*); void engine_hanja_korean_keys(struct ct*); void engine_hanja_backspace(struct ct*); void engine_hanja_input_languages(struct ct*); diff --git a/tests/unit_test.c b/tests/unit_test.c index e0df955..f1203d3 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -107,6 +107,7 @@ static const struct ct_test tests[] = { { "engine/emoji-start-and-unknown", engine_emoji_start_and_unknown }, { "engine/hanja-search", engine_hanja_search }, { "engine/hanja-unknown-cancel", engine_hanja_unknown_and_cancel }, + { "engine/hanja-word-prefix", engine_hanja_word_prefix }, { "engine/hanja-korean-keys", engine_hanja_korean_keys }, { "engine/hanja-backspace", engine_hanja_backspace }, { "engine/hanja-input-languages", engine_hanja_input_languages },