From 136695a900c0bd586952e91fa4c3941854e824df Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Wed, 12 Aug 2026 21:23:10 +0900 Subject: [PATCH] hanja: separate temporary search mode --- README.md | 15 ++- dat.h | 1 + strans.c | 77 +++++------ tests/engine_test.c | 314 ++++++++++++++++++++++++-------------------- tests/test.h | 2 +- tests/unit_test.c | 2 +- 6 files changed, 216 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index f99c6c3..a62c840 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Ctrl+S Korean Hangul Ctrl+T English passthrough Ctrl+V Vietnamese Telex compatibility mode Ctrl+E Emoji and symbol search -Ctrl+H One-shot Hanja search (from Korean mode) +Ctrl+H One-shot Hanja search Ctrl+P Toggle popup preedit ``` @@ -47,12 +47,13 @@ English aliases or the active language's input rules, including Japanese Ctrl+E โ†’ smile โ†’ Enter โ†’ ๐Ÿ˜€ ``` -Hanja search is available only from Korean mode and returns to Korean mode -after one conversion. Press Ctrl+H, type exactly one Hangul syllable with -the normal 2-beolsik Shift and Caps Lock behavior, then select with Up, Down, -Tab, Enter, or `1`-`9`. Escape cancels the search; when there is no match, -Enter commits the Hangul reading. It does not convert words or text already -surrounding the cursor. +Hanja search is temporary and returns to the active language after one +conversion. Press Ctrl+H and type with that language's normal input rules, +then select with Up, Down, Tab, Enter, or `1`-`9`. The bundled dictionary +currently indexes exactly one modern Hangul syllable. Incomplete jamo, +multiple syllables, and other languages therefore have no candidates; Enter +commits the displayed reading unchanged. Escape cancels the search. It does +not convert words or text already surrounding the cursor. ## Build and test diff --git a/dat.h b/dat.h index e4a61b4..07b2c5c 100644 --- a/dat.h +++ b/dat.h @@ -13,6 +13,7 @@ enum LangJP = 0x0e, LangJPK = 0x0b, LangKO = 0x13, + LangHANJA = 0x08, LangEMOJI = 0x05, LangVI = 0x16, diff --git a/strans.c b/strans.c index ff1ad27..83bbc54 100644 --- a/strans.c +++ b/strans.c @@ -16,20 +16,12 @@ static int maplookup(Trie*, Str*, Str*); typedef struct Search Search; struct Search { - int kind; + int lang; Str raw; Str text; }; static Search search; -enum -{ - SearchOff, - SearchEmoji, - SearchHanja, - Hanjakey = 0x08, -}; - uvlong ownernew(void) { @@ -47,7 +39,8 @@ Lang langs[] = { {LangEN, nil, nil, nil, nil, nil, nil, nil}, {LangJP, "hira", "kanji", transmap, backjp, dictqjp, nil, nil}, {LangJPK, "kata", nil, transmap, backjp, dictqjp, nil, nil}, - {LangKO, nil, "hanja", transko, backko, nil, nil, nil}, + {LangKO, nil, nil, transko, backko, nil, nil, nil}, + {LangHANJA, nil, "hanja", nil, nil, nil, nil, nil}, {LangEMOJI, nil, "emoji", nil, nil, nil, nil, nil}, {LangVI, "telex", nil, transvi, backvi, dictqmap, nil, nil}, }; @@ -123,12 +116,12 @@ show(void) int i, first, n; sclear(&dc.pre); - if(search.kind) + if(search.lang) pre = search.text; else impre(&pre); if(popup){ - if(search.kind || isjp(&im) || im.l->map == nil || + if(search.lang || isjp(&im) || im.l->map == nil || !mapget(im.l->map, &pre, &dc.pre)) dc.pre = pre; } @@ -154,7 +147,7 @@ reset(void) { sclear(&im.pre); sclear(&im.raw); - search.kind = SearchOff; + search.lang = 0; sclear(&search.raw); sclear(&search.text); memset(&caret, 0, sizeof caret); @@ -511,38 +504,39 @@ hanjaquery(void) static Dictres res; Str key; - transstr(getlang(LangKO), &search.raw, &key); + transstr(im.l, &search.raw, &key); search.text = key; - searchlookup(LangKO, &key, &res); - setkouho(&res); + if(key.n == 1 && key.r[0] >= 0xac00 && key.r[0] <= 0xd7a3){ + searchlookup(LangHANJA, &key, &res); + setkouho(&res); + }else + clearkouho(); show(); } static void searchquery(void) { - if(search.kind == SearchEmoji) + if(search.lang == LangEMOJI) emojiquery(); - else if(search.kind == SearchHanja) + else if(search.lang == LangHANJA) hanjaquery(); } static int -searchkind(Rune c, u32int mod) +searchlang(Rune c, u32int mod) { - if(c == LangEMOJI) - return SearchEmoji; if(mod & (Malt|Msuper)) - return SearchOff; - if(c == Hanjakey && im.l->lang == LangKO) - return SearchHanja; - return SearchOff; + return 0; + if(c == LangEMOJI || c == LangHANJA) + return c; + return 0; } static void endsearch(void) { - search.kind = SearchOff; + search.lang = 0; sclear(&search.raw); sclear(&search.text); clearkouho(); @@ -550,14 +544,14 @@ endsearch(void) } static void -startsearch(int kind, Str *com) +startsearch(int lang, Str *com) { if(haspre(&im)) commit(com); sclear(&im.pre); sclear(&im.raw); clearkouho(); - search.kind = kind; + search.lang = lang; sclear(&search.raw); sclear(&search.text); show(); @@ -636,11 +630,16 @@ searchkey(u32int ks, u32int mod, Str *com) endsearch(); return 1; } + if(ks >= Kspec || (ks < ' ' && !(mod & Mctrl)) || + (mod & (Malt|Msuper))){ + commitsearch(com); + return 0; + } if(mod & Mctrl){ c = ctrlkey(ks); - n = searchkind(c, mod); - if(n != SearchOff){ - if(n == search.kind) + n = searchlang(c, mod); + if(n != 0){ + if(n == search.lang) endsearch(); else startsearch(n, com); @@ -656,15 +655,11 @@ searchkey(u32int ks, u32int mod, Str *com) return 1; return 0; } - if(ks >= Kspec || ks < ' ' || (mod & (Malt|Msuper))){ - commitsearch(com); - return 0; - } if(search.raw.n >= Maxrunes){ commitsearch(com); return 0; } - if(search.kind == SearchHanja) + if(im.l->lang == LangKO) ks = kokey(ks, mod); sputr(&search.raw, ks); searchquery(); @@ -679,7 +674,7 @@ keystroke(u32int ks, u32int mod, Str *com) if(ismodkey(ks)) return 0; - if(search.kind) + if(search.lang) return searchkey(ks, mod, com); if(ks == Kdown || ks == Kup){ if(im.nkouho == 0) @@ -738,8 +733,8 @@ keystroke(u32int ks, u32int mod, Str *com) } if(mod & Mctrl){ c = ctrlkey(ks); - n = searchkind(c, mod); - if(n != SearchOff){ + n = searchlang(c, mod); + if(n != 0){ startsearch(n, com); return 1; } @@ -844,7 +839,7 @@ imhandlekey(Keyreq *kr) break; } if(kr->want && kr->owner == activeowner){ - if(search.kind) + if(search.lang) res.preedit = search.text; else impre(&res.preedit); @@ -858,7 +853,7 @@ dictresult(Dictres *res) Str pre; impre(&pre); - if(search.kind || res->lang != im.l->lang || + if(search.lang || res->lang != im.l->lang || scmp(&res->key, &pre) != 0) return; setkouho(res); diff --git a/tests/engine_test.c b/tests/engine_test.c index 1f5ab7c..656a6f3 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -421,15 +421,17 @@ engine_direct_language_modes(struct ct *t) CT_EQ_INT(t, 0, im.pre.n); CT_EQ_INT(t, 0, im.raw.n); CT_CHECK(t, keystroke('e', Mctrl, &com)); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); CT_EQ_INT(t, LangEN, im.l->lang); CT_CHECK(t, keystroke(Kesc, 0, &com)); - CT_CHECK(t, !search.kind); + CT_CHECK(t, !search.lang); CT_CHECK(t, keystroke('s', Mctrl, &com)); CT_EQ_INT(t, LangKO, im.l->lang); CT_EQ_PTR(t, nil, im.l->mapname); CT_EQ_PTR(t, nil, im.l->map); + CT_EQ_PTR(t, nil, im.l->dictname); CT_EQ_PTR(t, nil, im.l->dictq); + CT_CHECK(t, strcmp(getlang(LangHANJA)->dictname, "hanja") == 0); CT_CHECK(t, keystroke('r', 0, &com)); checkstr(t, "Korean without map", "ใ„ฑ", &im.pre); CT_CHECK(t, keystroke(Kback, 0, &com)); @@ -509,11 +511,12 @@ engine_language_switch_state(struct ct *t) vi->map = saved; } -typedef struct Emojifix Emojifix; -struct Emojifix +typedef struct Searchfix Searchfix; +struct Searchfix { Im im; Search search; + Lang *dictlang; Hmap *dict; int popup; int visible; @@ -544,33 +547,37 @@ setdict(Hmap **dict, char *key, char *val) } static void -emojibegin(Emojifix *f, int showpre) +searchsave(Searchfix *f, int lang) { - Lang *l; - - l = getlang(LangEMOJI); f->im = im; f->search = search; - f->dict = l->dict; + f->dictlang = getlang(lang); + f->dict = f->dictlang->dict; f->popup = popup; f->visible = visible; draindraw(nil); - l->dict = hmapalloc(32); - setdict(&l->dict, "a", "A B"); - setdict(&l->dict, "ใ…", "B C"); - setdict(&l->dict, "alpha", "ฮฑ"); - setdict(&l->dict, "smil", "๐Ÿ˜€ ๐Ÿ˜„"); - setdict(&l->dict, "smile", "๐Ÿ˜€ ๐Ÿ˜„"); - setdict(&l->dict, "์›ƒ์Œ", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜‚"); - setdict(&l->dict, "ใˆใŒใŠ", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜Š"); - setdict(&l->dict, "ใ‚จใ‚ฌใ‚ช", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜Š"); - setdict(&l->dict, "heart", "โค๏ธ"); - setdict(&l->dict, "coffee", "โ˜•๏ธ"); - setdict(&l->dict, "^", "ยน ยฒ ยณ โด โต โถ โท โธ โน โฝ"); - setdict(&l->dict, "^0", "โฐ"); - setdict(&l->dict, "_", "โ‚ โ‚‚ โ‚ƒ โ‚„ โ‚… โ‚† โ‚‡ โ‚ˆ โ‚‰ โ‚"); - setdict(&l->dict, "<", "โ† โ‰ค โ™ฅ โ‰ "); - setdict(&l->dict, "many", +} + +static void +emojibegin(Searchfix *f, int showpre) +{ + searchsave(f, LangEMOJI); + f->dictlang->dict = hmapalloc(32); + setdict(&f->dictlang->dict, "a", "A B"); + setdict(&f->dictlang->dict, "ใ…", "B C"); + setdict(&f->dictlang->dict, "alpha", "ฮฑ"); + setdict(&f->dictlang->dict, "smil", "๐Ÿ˜€ ๐Ÿ˜„"); + setdict(&f->dictlang->dict, "smile", "๐Ÿ˜€ ๐Ÿ˜„"); + setdict(&f->dictlang->dict, "์›ƒ์Œ", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜‚"); + setdict(&f->dictlang->dict, "ใˆใŒใŠ", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜Š"); + setdict(&f->dictlang->dict, "ใ‚จใ‚ฌใ‚ช", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜Š"); + setdict(&f->dictlang->dict, "heart", "โค๏ธ"); + setdict(&f->dictlang->dict, "coffee", "โ˜•๏ธ"); + setdict(&f->dictlang->dict, "^", "ยน ยฒ ยณ โด โต โถ โท โธ โน โฝ"); + setdict(&f->dictlang->dict, "^0", "โฐ"); + setdict(&f->dictlang->dict, "_", "โ‚ โ‚‚ โ‚ƒ โ‚„ โ‚… โ‚† โ‚‡ โ‚ˆ โ‚‰ โ‚"); + setdict(&f->dictlang->dict, "<", "โ† โ‰ค โ™ฅ โ‰ "); + setdict(&f->dictlang->dict, "many", "c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12"); init(); im.l = getlang(LangKO); @@ -578,14 +585,12 @@ emojibegin(Emojifix *f, int showpre) } static void -emojiend(Emojifix *f) +searchend(Searchfix *f) { - Lang *l; Hmap *dict; - l = getlang(LangEMOJI); - dict = l->dict; - l->dict = f->dict; + dict = f->dictlang->dict; + f->dictlang->dict = f->dict; hmapfree(dict); draindraw(nil); im = f->im; @@ -594,50 +599,18 @@ emojiend(Emojifix *f) visible = f->visible; } -typedef struct Hanjafix Hanjafix; -struct Hanjafix -{ - Im im; - Search search; - Hmap *dict; - int popup; - int visible; -}; - static void -hanjabegin(Hanjafix *f) +hanjabegin(Searchfix *f, int lang) { - Lang *l; - - l = getlang(LangKO); - f->im = im; - f->search = search; - f->dict = l->dict; - f->popup = popup; - f->visible = visible; - draindraw(nil); - l->dict = hmapalloc(8); - setdict(&l->dict, "ํ•œ", "ๆผข ้Ÿ“"); - setdict(&l->dict, "๊ฐ€", "ๅฎถ"); + searchsave(f, LangHANJA); + f->dictlang->dict = hmapalloc(8); + setdict(&f->dictlang->dict, "ํ•œ", "ๆผข ้Ÿ“"); + setdict(&f->dictlang->dict, "๊ฐ€", "ๅฎถ"); + setdict(&f->dictlang->dict, "ใ„ฑ", "ๅ‡"); + setdict(&f->dictlang->dict, "ํ•œ๊ธ€", "ๆ–‡"); + setdict(&f->dictlang->dict, "ใ‹", "ไปฎ"); init(); - im.l = l; -} - -static void -hanjaend(Hanjafix *f) -{ - Lang *l; - Hmap *dict; - - l = getlang(LangKO); - dict = l->dict; - l->dict = f->dict; - hmapfree(dict); - draindraw(nil); - im = f->im; - search = f->search; - popup = f->popup; - visible = f->visible; + im.l = getlang(lang); } static int @@ -817,7 +790,7 @@ void engine_emoji_single_candidate(struct ct *t) { Drawcmd dc = {0}; - Emojifix f; + Searchfix f; Str com; emojibegin(&f, 1); @@ -825,12 +798,12 @@ engine_emoji_single_candidate(struct ct *t) CT_CHECK(t, keystroke('p', Mctrl, &com)); CT_EQ_INT(t, 0, popup); CT_CHECK(t, keystroke('e', Mctrl, &com)); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); CT_EQ_INT(t, LangKO, im.l->lang); if(typekeys(t, "alpha", &com)){ checkstr(t, "no automatic commit", "", &com); checkstr(t, "raw English query", "alpha", &search.text); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); CT_EQ_INT(t, 1, im.nkouho); checkstr(t, "single candidate", "ฮฑ", &im.kouho[0]); CT_CHECK(t, draindraw(&dc) > 0); @@ -838,21 +811,21 @@ engine_emoji_single_candidate(struct ct *t) CT_EQ_INT(t, 1, dc.nkouho); CT_CHECK(t, keystroke(Kret, 0, &com)); checkstr(t, "explicit candidate commit", "ฮฑ", &com); - CT_CHECK(t, !search.kind); + CT_CHECK(t, !search.lang); CT_EQ_INT(t, LangKO, im.l->lang); show(); CT_CHECK(t, draindraw(&dc) > 0); CT_EQ_INT(t, 0, dc.pre.n); CT_EQ_INT(t, 0, dc.nkouho); } - emojiend(&f); + searchend(&f); } void engine_emoji_queries(struct ct *t) { Drawcmd dc = {0}; - Emojifix f; + Searchfix f; Str com; emojibegin(&f, 1); @@ -900,16 +873,16 @@ engine_emoji_queries(struct ct *t) CT_EQ_INT(t, 3, dc.nkouho); CT_CHECK(t, keystroke(Kesc, 0, &com)); checkstr(t, "cancel commit", "", &com); - CT_CHECK(t, !search.kind); + CT_CHECK(t, !search.lang); CT_EQ_INT(t, LangKO, im.l->lang); } - emojiend(&f); + searchend(&f); } void engine_emoji_japanese_and_multirune(struct ct *t) { - Emojifix f; + Searchfix f; Str com; emojibegin(&f, 0); @@ -950,7 +923,7 @@ engine_emoji_japanese_and_multirune(struct ct *t) CT_CHECK(t, keystroke(Kret, 0, &com)); checkstr(t, "multi-codepoint emoji", "โ˜•๏ธ", &com); } - emojiend(&f); + searchend(&f); } void @@ -966,7 +939,7 @@ engine_emoji_digit_aliases(struct ct *t) { "_2", '_', '2', "โ‚‚" }, { "<3", '<', '3', "โ™ฅ" }, }; - Emojifix f; + Searchfix f; Str com; int i; @@ -978,7 +951,7 @@ engine_emoji_digit_aliases(struct ct *t) CT_CHECK(t, im.nkouho >= cases[i].digit - '0'); CT_CHECK(t, keystroke(cases[i].digit, 0, &com)); checkstr(t, cases[i].name, cases[i].want, &com); - CT_CHECK(t, !search.kind); + CT_CHECK(t, !search.lang); CT_EQ_INT(t, LangKO, im.l->lang); } @@ -998,19 +971,19 @@ engine_emoji_digit_aliases(struct ct *t) CT_CHECK(t, keystroke('9', 0, &com)); checkstr(t, "missing number query", "smile9", &search.text); checkstr(t, "missing number commit", "", &com); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); CT_EQ_INT(t, 0, im.nkouho); CT_CHECK(t, keystroke(Kret, 0, &com)); checkstr(t, "missing number text", "smile9", &com); } - emojiend(&f); + searchend(&f); } void engine_emoji_navigation(struct ct *t) { Drawcmd dc = {0}; - Emojifix f; + Searchfix f; Str com; int i; @@ -1054,13 +1027,13 @@ engine_emoji_navigation(struct ct *t) CT_CHECK(t, keystroke(Kret, 0, &com)); checkstr(t, "navigated candidate", "๐Ÿ˜„", &com); } - emojiend(&f); + searchend(&f); } void engine_emoji_preedit_languages(struct ct *t) { - Emojifix f; + Searchfix f; Str com, shown; int p; @@ -1074,26 +1047,26 @@ engine_emoji_preedit_languages(struct ct *t) checkstr(t, "modifier preserves Japanese", "ใ‹", &shown); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); checkstr(t, "committed Japanese preedit", "ใ‹", &com); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); CT_EQ_INT(t, LangJP, im.l->lang); - CT_CHECK(t, keystroke('E', Mctrl|Malt, &com)); - CT_EQ_INT(t, SearchOff, search.kind); + CT_CHECK(t, !keystroke('E', Mctrl|Malt, &com)); + CT_EQ_INT(t, 0, search.lang); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); - CT_CHECK(t, keystroke('E', Mctrl|Msuper, &com)); - CT_EQ_INT(t, SearchOff, search.kind); + CT_CHECK(t, !keystroke('E', Mctrl|Msuper, &com)); + CT_EQ_INT(t, 0, search.lang); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); CT_EQ_INT(t, LangJP, im.l->lang); CT_CHECK(t, keystroke('a', 0, &com)); p = popup; CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com)); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); checkstr(t, "modifier preserves search", "a", &search.text); CT_CHECK(t, keystroke('P', Mctrl|Mshift, &com)); CT_EQ_INT(t, !p, popup); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com)); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); - CT_CHECK(t, !search.kind); + CT_CHECK(t, !search.lang); sclear(&com); if(typekeys(t, "na", &com)){ shown = shownpre(&im); @@ -1123,7 +1096,7 @@ engine_emoji_preedit_languages(struct ct *t) CT_CHECK(t, keystroke('e', Mctrl, &com)); checkstr(t, "committed Telex preedit", "รก", &com); CT_CHECK(t, keystroke(Kesc, 0, &com)); - CT_CHECK(t, !search.kind); + CT_CHECK(t, !search.lang); CT_EQ_PTR(t, &testvi, im.l); sclear(&com); if(typekeys(t, "as", &com)){ @@ -1132,13 +1105,13 @@ engine_emoji_preedit_languages(struct ct *t) checkstr(t, "Telex raw resumed", "as", &im.raw); } } - emojiend(&f); + searchend(&f); } void engine_emoji_start_and_unknown(struct ct *t) { - Emojifix f; + Searchfix f; Str com; char want[Maxrunes+1]; int i; @@ -1148,14 +1121,14 @@ engine_emoji_start_and_unknown(struct ct *t) if(typekeys(t, "dkssud", &com)){ CT_CHECK(t, keystroke('e', Mctrl, &com)); checkstr(t, "committed Korean preedit", "์•ˆ๋…•", &com); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); CT_EQ_INT(t, LangKO, im.l->lang); if(typekeys(t, "xyz", &com)){ checkstr(t, "unknown displayed query", "xyz", &search.text); CT_EQ_INT(t, 0, im.nkouho); CT_CHECK(t, keystroke(Kret, 0, &com)); checkstr(t, "unknown query commit", "์•ˆ๋…•xyz", &com); - CT_CHECK(t, !search.kind); + CT_CHECK(t, !search.lang); CT_EQ_INT(t, LangKO, im.l->lang); } } @@ -1165,7 +1138,7 @@ engine_emoji_start_and_unknown(struct ct *t) if(typekeys(t, "xyz", &com)){ CT_CHECK(t, !keystroke(Kspec|0x51, 0, &com)); checkstr(t, "special commits shown query", "xyz", &com); - CT_CHECK(t, !search.kind); + CT_CHECK(t, !search.lang); CT_EQ_INT(t, LangKO, im.l->lang); } @@ -1174,20 +1147,20 @@ engine_emoji_start_and_unknown(struct ct *t) for(i = 0; i < Maxrunes; i++) CT_CHECK(t, keystroke('q', 0, &com)); CT_EQ_INT(t, Maxrunes, search.raw.n); - CT_CHECK(t, search.kind); + CT_CHECK(t, search.lang); CT_CHECK(t, !keystroke('z', 0, &com)); memset(want, 'q', Maxrunes); want[Maxrunes] = '\0'; checkstr(t, "bounded query commit", want, &com); - CT_CHECK(t, !search.kind); - emojiend(&f); + CT_CHECK(t, !search.lang); + searchend(&f); } void engine_emoji_dictionary_identity(struct ct *t) { Dictres res; - Emojifix f; + Searchfix f; emojibegin(&f, 0); im.pre = mkstr("same"); @@ -1210,31 +1183,31 @@ engine_emoji_dictionary_identity(struct ct *t) checkstr(t, "wrong preedit ignored", "old", &im.kouho[0]); res.key = im.pre; - search.kind = SearchEmoji; + search.lang = LangEMOJI; dictresult(&res); CT_EQ_INT(t, 1, im.nkouho); checkstr(t, "search response ignored", "old", &im.kouho[0]); - search.kind = SearchOff; + search.lang = 0; res.kouho[0] = mkstr("right"); dictresult(&res); CT_EQ_INT(t, 1, im.nkouho); CT_EQ_INT(t, -1, im.sel); checkstr(t, "matching response accepted", "right", &im.kouho[0]); - emojiend(&f); + searchend(&f); } void engine_hanja_search(struct ct *t) { - Hanjafix f; + Searchfix f; Str com; - hanjabegin(&f); + hanjabegin(&f, LangKO); sclear(&com); if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) goto cleanup; - CT_EQ_INT(t, SearchHanja, search.kind); + CT_EQ_INT(t, LangHANJA, search.lang); CT_EQ_INT(t, LangKO, im.l->lang); if(!typekeys(t, "gks", &com)) goto cleanup; @@ -1244,19 +1217,19 @@ engine_hanja_search(struct ct *t) CT_CHECK(t, keystroke(Kdown, 0, &com)); CT_CHECK(t, keystroke(Kret, 0, &com)); checkstr(t, "navigated Hanja", "้Ÿ“", &com); - CT_EQ_INT(t, SearchOff, search.kind); + CT_EQ_INT(t, 0, search.lang); CT_EQ_INT(t, LangKO, im.l->lang); cleanup: - hanjaend(&f); + searchend(&f); } void engine_hanja_unknown_and_cancel(struct ct *t) { - Hanjafix f; + Searchfix f; Str com; - hanjabegin(&f); + hanjabegin(&f, LangKO); sclear(&com); if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) goto cleanup; @@ -1266,9 +1239,29 @@ engine_hanja_unknown_and_cancel(struct ct *t) CT_EQ_INT(t, 0, im.nkouho); CT_CHECK(t, keystroke(Kret, 0, &com)); checkstr(t, "unknown Hanja commit", "๊ธ€", &com); - CT_EQ_INT(t, SearchOff, search.kind); + CT_EQ_INT(t, 0, search.lang); CT_EQ_INT(t, LangKO, im.l->lang); + sclear(&com); + if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) + goto cleanup; + if(!typekeys(t, "r", &com)) + goto cleanup; + checkstr(t, "incomplete Hanja reading", "ใ„ฑ", &search.text); + CT_EQ_INT(t, 0, im.nkouho); + CT_CHECK(t, keystroke(Kret, 0, &com)); + checkstr(t, "incomplete Hanja commit", "ใ„ฑ", &com); + + sclear(&com); + if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) + goto cleanup; + if(!typekeys(t, "gksrmf", &com)) + goto cleanup; + checkstr(t, "multiple Hanja syllables", "ํ•œ๊ธ€", &search.text); + CT_EQ_INT(t, 0, im.nkouho); + CT_CHECK(t, keystroke(Kret, 0, &com)); + checkstr(t, "multiple Hanja syllable commit", "ํ•œ๊ธ€", &com); + sclear(&com); if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) goto cleanup; @@ -1277,25 +1270,25 @@ engine_hanja_unknown_and_cancel(struct ct *t) CT_EQ_INT(t, 2, im.nkouho); CT_CHECK(t, keystroke(Kesc, 0, &com)); checkstr(t, "cancelled Hanja", "", &com); - CT_EQ_INT(t, SearchOff, search.kind); + CT_EQ_INT(t, 0, search.lang); CT_EQ_INT(t, LangKO, im.l->lang); sclear(&com); CT_CHECK(t, keystroke('h', Mctrl, &com)); - CT_EQ_INT(t, SearchHanja, search.kind); + CT_EQ_INT(t, LangHANJA, search.lang); CT_CHECK(t, keystroke('h', Mctrl, &com)); - CT_EQ_INT(t, SearchOff, search.kind); + CT_EQ_INT(t, 0, search.lang); cleanup: - hanjaend(&f); + searchend(&f); } void engine_hanja_korean_keys(struct ct *t) { - Hanjafix f; + Searchfix f; Str com; - hanjabegin(&f); + hanjabegin(&f, LangKO); sclear(&com); CT_CHECK(t, keystroke('h', Mctrl, &com)); CT_CHECK(t, keystroke('r', Mshift, &com)); @@ -1309,38 +1302,69 @@ engine_hanja_korean_keys(struct ct *t) checkstr(t, "Caps Lock Hanja reading", "๊ฐ€", &search.text); CT_EQ_INT(t, 1, im.nkouho); CT_CHECK(t, keystroke(Kesc, 0, &com)); - hanjaend(&f); + searchend(&f); } void -engine_hanja_korean_only(struct ct *t) +engine_hanja_input_languages(struct ct *t) { - Hmap *dict; - Lang *l; - Str com, shown; + Searchfix f; + Str com; + + hanjabegin(&f, LangEN); + sclear(&com); + if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) + goto cleanup; + if(!typekeys(t, "gks", &com)) + goto cleanup; + checkstr(t, "English Hanja input", "gks", &search.text); + CT_EQ_INT(t, 0, im.nkouho); + CT_EQ_INT(t, LangEN, im.l->lang); + CT_CHECK(t, keystroke(Kret, 0, &com)); + checkstr(t, "English Hanja commit", "gks", &com); + CT_EQ_INT(t, LangEN, im.l->lang); init(); im.l = getlang(LangJP); sclear(&com); - if(typekeys(t, "ka", &com)){ - CT_CHECK(t, !keystroke('h', Mctrl, &com)); - CT_CHECK(t, !search.kind); - CT_EQ_INT(t, LangJP, im.l->lang); - shown = shownpre(&im); - checkstr(t, "non-Korean Ctrl-H reset", "", &shown); - checkstr(t, "non-Korean Ctrl-H commit", "", &com); - } + if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) + goto cleanup; + if(!typekeys(t, "ka", &com)) + goto cleanup; + checkstr(t, "Japanese Hanja input", "ใ‹", &search.text); + CT_EQ_INT(t, 0, im.nkouho); + CT_EQ_INT(t, LangJP, im.l->lang); + CT_CHECK(t, keystroke(Kret, 0, &com)); + checkstr(t, "Japanese Hanja commit", "ใ‹", &com); + CT_EQ_INT(t, LangJP, im.l->lang); - l = getlang(LangKO); - dict = l->dict; - l->dict = nil; init(); - im.l = l; + im.l = getlang(LangEN); sclear(&com); CT_CHECK(t, keystroke('h', Mctrl, &com)); - CT_EQ_INT(t, SearchHanja, search.kind); - CT_CHECK(t, keystroke(Kesc, 0, &com)); - l->dict = dict; + CT_CHECK(t, keystroke(L'ํ•œ', 0, &com)); + checkstr(t, "direct Hanja input", "ํ•œ", &search.text); + CT_EQ_INT(t, 2, im.nkouho); + CT_CHECK(t, keystroke(Kret, 0, &com)); + checkstr(t, "direct Hanja selection", "ๆผข", &com); + CT_EQ_INT(t, LangEN, im.l->lang); + + init(); + im.l = getlang(LangJP); + sclear(&com); + CT_CHECK(t, keystroke('h', Mctrl, &com)); + if(!typekeys(t, "ka", &com)) + goto cleanup; + CT_CHECK(t, !keystroke('e', Mctrl|Malt, &com)); + checkstr(t, "modified search boundary", "ใ‹", &com); + CT_EQ_INT(t, 0, search.lang); + CT_EQ_INT(t, LangJP, im.l->lang); + sclear(&com); + CT_CHECK(t, !keystroke('h', Mctrl|Msuper, &com)); + CT_EQ_INT(t, 0, search.lang); + CT_EQ_INT(t, LangJP, im.l->lang); +cleanup: + searchend(&f); } static u32int @@ -1364,7 +1388,7 @@ engine_randomized_stress(struct ct *t) 'u', 'w', 'y', '1', '2', '9', '\'', ' ', L'ๆ—ฅ', }; static Rune modes[] = { 't', 'n', 'k', 's', 'v' }; - Emojifix f; + Searchfix f; Lang *vi; Trie *vimap; Keyres res; @@ -1452,7 +1476,7 @@ engine_randomized_stress(struct ct *t) break; } } - emojiend(&f); + searchend(&f); vi->map = vimap; } diff --git a/tests/test.h b/tests/test.h index 5a8e867..59fd14a 100644 --- a/tests/test.h +++ b/tests/test.h @@ -57,7 +57,7 @@ void engine_emoji_dictionary_identity(struct ct*); void engine_hanja_search(struct ct*); void engine_hanja_unknown_and_cancel(struct ct*); void engine_hanja_korean_keys(struct ct*); -void engine_hanja_korean_only(struct ct*); +void engine_hanja_input_languages(struct ct*); void engine_randomized_stress(struct ct*); void engine_full_boundary_passthrough(struct ct*); void dictionary_candidates(struct ct*); diff --git a/tests/unit_test.c b/tests/unit_test.c index a5d2fda..4f7c26d 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -111,7 +111,7 @@ static const struct ct_test tests[] = { { "engine/hanja-search", engine_hanja_search }, { "engine/hanja-unknown-cancel", engine_hanja_unknown_and_cancel }, { "engine/hanja-korean-keys", engine_hanja_korean_keys }, - { "engine/hanja-korean-only", engine_hanja_korean_only }, + { "engine/hanja-input-languages", engine_hanja_input_languages }, { "engine/randomized-stress", engine_randomized_stress }, { "engine/full-boundary-passthrough", engine_full_boundary_passthrough }, { "dict/candidates", dictionary_candidates },