From 0216cc3c8afcce5bf7d8a37ff09aee5b91686017 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Wed, 12 Aug 2026 19:19:26 +0900 Subject: [PATCH] hanja: add one-shot Korean search --- strans.c | 95 ++++++++++++----- tests/engine_test.c | 244 +++++++++++++++++++++++++++++++++++++------- tests/test.h | 4 + tests/unit_test.c | 4 + 4 files changed, 288 insertions(+), 59 deletions(-) diff --git a/strans.c b/strans.c index ce981e4..33f0b39 100644 --- a/strans.c +++ b/strans.c @@ -16,12 +16,20 @@ static int maplookup(Trie*, Str*, Str*); typedef struct Search Search; struct Search { - int on; + int kind; Str raw; Str text; }; static Search search; +enum +{ + SearchOff, + SearchEmoji, + SearchHanja, + Hanjakey = 0x08, +}; + uvlong ownernew(void) { @@ -115,12 +123,12 @@ show(void) int i, first, n; sclear(&dc.pre); - if(search.on) + if(search.kind) pre = search.text; else impre(&pre); if(popup){ - if(search.on || isjp(&im) || im.l->map == nil || + if(search.kind || isjp(&im) || im.l->map == nil || !mapget(im.l->map, &pre, &dc.pre)) dc.pre = pre; } @@ -146,7 +154,7 @@ reset(void) { sclear(&im.pre); sclear(&im.raw); - search.on = 0; + search.kind = SearchOff; sclear(&search.raw); sclear(&search.text); memset(&caret, 0, sizeof caret); @@ -460,13 +468,13 @@ addkouho(Str *s) } static void -emojilookup(Str *key, Dictres *res) +searchlookup(int lang, Str *key, Dictres *res) { Dictreq req; req.key = *key; req.pre = req.key; - req.lang = LangEMOJI; + req.lang = lang; dictlookup(&req, res); } @@ -482,13 +490,13 @@ emojiquery(void) transstr(im.l, &search.raw, &local); foldascii(&local); clearkouho(); - emojilookup(&raw, &res); + searchlookup(LangEMOJI, &raw, &res); rawhit = res.nkouho != 0; for(i = 0; i < res.nkouho; i++) addkouho(&res.kouho[i]); localhit = 0; if(scmp(&raw, &local) != 0){ - emojilookup(&local, &res); + searchlookup(LangEMOJI, &local, &res); localhit = res.nkouho != 0; for(i = 0; i < res.nkouho; i++) addkouho(&res.kouho[i]); @@ -497,10 +505,44 @@ emojiquery(void) show(); } +static void +hanjaquery(void) +{ + static Dictres res; + Str key; + + transstr(getlang(LangKO), &search.raw, &key); + search.text = key; + searchlookup(LangKO, &key, &res); + setkouho(&res); + show(); +} + +static void +searchquery(void) +{ + if(search.kind == SearchEmoji) + emojiquery(); + else if(search.kind == SearchHanja) + hanjaquery(); +} + +static int +searchkind(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; +} + static void endsearch(void) { - search.on = 0; + search.kind = SearchOff; sclear(&search.raw); sclear(&search.text); clearkouho(); @@ -508,14 +550,14 @@ endsearch(void) } static void -startsearch(Str *com) +startsearch(int kind, Str *com) { if(haspre(&im)) commit(com); sclear(&im.pre); sclear(&im.raw); clearkouho(); - search.on = 1; + search.kind = kind; sclear(&search.raw); sclear(&search.text); show(); @@ -529,7 +571,7 @@ commitsearch(Str *com) } static int -pickemoji(int n, Str *com) +picksearch(int n, Str *com) { if(n < 0 || n >= im.nkouho) return 0; @@ -559,7 +601,7 @@ searchkey(u32int ks, u32int mod, Str *com) n = ks - '1'; off = im.sel >= Maxdisp ? im.sel - Maxdisp + 1 : 0; if(n >= 0 && n < Maxdisp && off + n < im.nkouho && mod == 0){ - pickemoji(off + n, com); + picksearch(off + n, com); return 1; } if(ks == Ktab){ @@ -579,7 +621,7 @@ searchkey(u32int ks, u32int mod, Str *com) } if(ks == Kret){ if(im.nkouho > 0) - return pickemoji(im.sel >= 0 ? im.sel : 0, com); + return picksearch(im.sel >= 0 ? im.sel : 0, com); commitsearch(com); return 1; } @@ -587,7 +629,7 @@ searchkey(u32int ks, u32int mod, Str *com) if(search.raw.n == 0) return 1; spopr(&search.raw); - emojiquery(); + searchquery(); return 1; } if(ks == Kesc){ @@ -596,8 +638,12 @@ searchkey(u32int ks, u32int mod, Str *com) } if(mod & Mctrl){ c = ctrlkey(ks); - if(c == LangEMOJI){ - endsearch(); + n = searchkind(c, mod); + if(n != SearchOff){ + if(n == search.kind) + endsearch(); + else + startsearch(n, com); return 1; } if(c == 0x10){ @@ -618,8 +664,10 @@ searchkey(u32int ks, u32int mod, Str *com) commitsearch(com); return 0; } + if(search.kind == SearchHanja) + ks = kokey(ks, mod); sputr(&search.raw, ks); - emojiquery(); + searchquery(); return 1; } @@ -631,7 +679,7 @@ keystroke(u32int ks, u32int mod, Str *com) if(ismodkey(ks)) return 0; - if(search.on) + if(search.kind) return searchkey(ks, mod, com); if(ks == Kdown || ks == Kup){ if(im.nkouho == 0) @@ -690,8 +738,9 @@ keystroke(u32int ks, u32int mod, Str *com) } if(mod & Mctrl){ c = ctrlkey(ks); - if(c == LangEMOJI){ - startsearch(com); + n = searchkind(c, mod); + if(n != SearchOff){ + startsearch(n, com); return 1; } if(c == 0x10){ @@ -795,7 +844,7 @@ imhandlekey(Keyreq *kr) break; } if(kr->want && kr->owner == activeowner){ - if(search.on) + if(search.kind) res.preedit = search.text; else impre(&res.preedit); @@ -809,7 +858,7 @@ dictresult(Dictres *res) Str pre; impre(&pre); - if(search.on || res->lang != im.l->lang || + if(search.kind || 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 cd7e040..1f5ab7c 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -421,10 +421,10 @@ 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.on); + CT_CHECK(t, search.kind); CT_EQ_INT(t, LangEN, im.l->lang); CT_CHECK(t, keystroke(Kesc, 0, &com)); - CT_CHECK(t, !search.on); + CT_CHECK(t, !search.kind); CT_CHECK(t, keystroke('s', Mctrl, &com)); CT_EQ_INT(t, LangKO, im.l->lang); CT_EQ_PTR(t, nil, im.l->mapname); @@ -535,7 +535,7 @@ draindraw(Drawcmd *last) } static void -setemoji(Hmap **dict, char *key, char *val) +setdict(Hmap **dict, char *key, char *val) { Str s; @@ -556,21 +556,21 @@ emojibegin(Emojifix *f, int showpre) f->visible = visible; draindraw(nil); l->dict = hmapalloc(32); - setemoji(&l->dict, "a", "A B"); - setemoji(&l->dict, "ใ…", "B C"); - setemoji(&l->dict, "alpha", "ฮฑ"); - setemoji(&l->dict, "smil", "๐Ÿ˜€ ๐Ÿ˜„"); - setemoji(&l->dict, "smile", "๐Ÿ˜€ ๐Ÿ˜„"); - setemoji(&l->dict, "์›ƒ์Œ", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜‚"); - setemoji(&l->dict, "ใˆใŒใŠ", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜Š"); - setemoji(&l->dict, "ใ‚จใ‚ฌใ‚ช", "๐Ÿ˜€ ๐Ÿ˜„ ๐Ÿ˜Š"); - setemoji(&l->dict, "heart", "โค๏ธ"); - setemoji(&l->dict, "coffee", "โ˜•๏ธ"); - setemoji(&l->dict, "^", "ยน ยฒ ยณ โด โต โถ โท โธ โน โฝ"); - setemoji(&l->dict, "^0", "โฐ"); - setemoji(&l->dict, "_", "โ‚ โ‚‚ โ‚ƒ โ‚„ โ‚… โ‚† โ‚‡ โ‚ˆ โ‚‰ โ‚"); - setemoji(&l->dict, "<", "โ† โ‰ค โ™ฅ โ‰ "); - setemoji(&l->dict, "many", + 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", "c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12"); init(); im.l = getlang(LangKO); @@ -594,6 +594,52 @@ 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) +{ + 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, "๊ฐ€", "ๅฎถ"); + 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; +} + static int typekeys(struct ct *t, char *keys, Str *com) { @@ -779,12 +825,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.on); + CT_CHECK(t, search.kind); 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.on); + CT_CHECK(t, search.kind); CT_EQ_INT(t, 1, im.nkouho); checkstr(t, "single candidate", "ฮฑ", &im.kouho[0]); CT_CHECK(t, draindraw(&dc) > 0); @@ -792,7 +838,7 @@ 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.on); + CT_CHECK(t, !search.kind); CT_EQ_INT(t, LangKO, im.l->lang); show(); CT_CHECK(t, draindraw(&dc) > 0); @@ -854,7 +900,7 @@ 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.on); + CT_CHECK(t, !search.kind); CT_EQ_INT(t, LangKO, im.l->lang); } emojiend(&f); @@ -932,7 +978,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.on); + CT_CHECK(t, !search.kind); CT_EQ_INT(t, LangKO, im.l->lang); } @@ -952,7 +998,7 @@ 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.on); + CT_CHECK(t, search.kind); CT_EQ_INT(t, 0, im.nkouho); CT_CHECK(t, keystroke(Kret, 0, &com)); checkstr(t, "missing number text", "smile9", &com); @@ -1028,19 +1074,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.on); + CT_CHECK(t, search.kind); + 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|Mshift, &com)); + CT_CHECK(t, keystroke('E', Mctrl|Msuper, &com)); + CT_EQ_INT(t, SearchOff, search.kind); + 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.on); + CT_CHECK(t, search.kind); 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.on); + CT_CHECK(t, search.kind); CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com)); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); - CT_CHECK(t, !search.on); + CT_CHECK(t, !search.kind); sclear(&com); if(typekeys(t, "na", &com)){ shown = shownpre(&im); @@ -1070,7 +1123,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.on); + CT_CHECK(t, !search.kind); CT_EQ_PTR(t, &testvi, im.l); sclear(&com); if(typekeys(t, "as", &com)){ @@ -1095,14 +1148,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.on); + CT_CHECK(t, search.kind); 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.on); + CT_CHECK(t, !search.kind); CT_EQ_INT(t, LangKO, im.l->lang); } } @@ -1112,7 +1165,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.on); + CT_CHECK(t, !search.kind); CT_EQ_INT(t, LangKO, im.l->lang); } @@ -1121,12 +1174,12 @@ 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.on); + CT_CHECK(t, search.kind); 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.on); + CT_CHECK(t, !search.kind); emojiend(&f); } @@ -1157,12 +1210,12 @@ engine_emoji_dictionary_identity(struct ct *t) checkstr(t, "wrong preedit ignored", "old", &im.kouho[0]); res.key = im.pre; - search.on = 1; + search.kind = SearchEmoji; dictresult(&res); CT_EQ_INT(t, 1, im.nkouho); checkstr(t, "search response ignored", "old", &im.kouho[0]); - search.on = 0; + search.kind = SearchOff; res.kouho[0] = mkstr("right"); dictresult(&res); CT_EQ_INT(t, 1, im.nkouho); @@ -1171,6 +1224,125 @@ engine_emoji_dictionary_identity(struct ct *t) emojiend(&f); } +void +engine_hanja_search(struct ct *t) +{ + Hanjafix f; + Str com; + + hanjabegin(&f); + sclear(&com); + if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) + goto cleanup; + CT_EQ_INT(t, SearchHanja, search.kind); + CT_EQ_INT(t, LangKO, im.l->lang); + if(!typekeys(t, "gks", &com)) + goto cleanup; + checkstr(t, "Hanja reading", "ํ•œ", &search.text); + CT_EQ_INT(t, 2, im.nkouho); + CT_CHECK(t, keystroke(Kdown, 0, &com)); + 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, LangKO, im.l->lang); +cleanup: + hanjaend(&f); +} + +void +engine_hanja_unknown_and_cancel(struct ct *t) +{ + Hanjafix f; + Str com; + + hanjabegin(&f); + sclear(&com); + if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) + goto cleanup; + if(!typekeys(t, "rmf", &com)) + goto cleanup; + checkstr(t, "unknown Hanja reading", "๊ธ€", &search.text); + 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, LangKO, im.l->lang); + + sclear(&com); + if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) + goto cleanup; + if(!typekeys(t, "gks", &com)) + goto cleanup; + 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, LangKO, im.l->lang); + + sclear(&com); + CT_CHECK(t, keystroke('h', Mctrl, &com)); + CT_EQ_INT(t, SearchHanja, search.kind); + CT_CHECK(t, keystroke('h', Mctrl, &com)); + CT_EQ_INT(t, SearchOff, search.kind); +cleanup: + hanjaend(&f); +} + +void +engine_hanja_korean_keys(struct ct *t) +{ + Hanjafix f; + Str com; + + hanjabegin(&f); + sclear(&com); + CT_CHECK(t, keystroke('h', Mctrl, &com)); + CT_CHECK(t, keystroke('r', Mshift, &com)); + CT_CHECK(t, keystroke('k', 0, &com)); + checkstr(t, "shifted Hanja reading", "๊นŒ", &search.text); + CT_CHECK(t, keystroke(Kesc, 0, &com)); + + CT_CHECK(t, keystroke('h', Mctrl, &com)); + CT_CHECK(t, keystroke('R', 0, &com)); + CT_CHECK(t, keystroke('k', 0, &com)); + checkstr(t, "Caps Lock Hanja reading", "๊ฐ€", &search.text); + CT_EQ_INT(t, 1, im.nkouho); + CT_CHECK(t, keystroke(Kesc, 0, &com)); + hanjaend(&f); +} + +void +engine_hanja_korean_only(struct ct *t) +{ + Hmap *dict; + Lang *l; + Str com, shown; + + 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); + } + + l = getlang(LangKO); + dict = l->dict; + l->dict = nil; + init(); + im.l = l; + 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; +} + static u32int stressrand(u32int *state) { diff --git a/tests/test.h b/tests/test.h index f33e33f..5a8e867 100644 --- a/tests/test.h +++ b/tests/test.h @@ -54,6 +54,10 @@ void engine_emoji_navigation(struct ct*); void engine_emoji_preedit_languages(struct ct*); void engine_emoji_start_and_unknown(struct ct*); 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_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 624be50..a5d2fda 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -108,6 +108,10 @@ static const struct ct_test tests[] = { { "engine/emoji-preedit-languages", engine_emoji_preedit_languages }, { "engine/emoji-start-and-unknown", engine_emoji_start_and_unknown }, { "engine/emoji-dictionary-identity", engine_emoji_dictionary_identity }, + { "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/randomized-stress", engine_randomized_stress }, { "engine/full-boundary-passthrough", engine_full_boundary_passthrough }, { "dict/candidates", dictionary_candidates },