hanja: separate temporary search mode

This commit is contained in:
2026-08-12 21:23:10 +09:00
parent 4e1c15dde4
commit 136695a900
6 changed files with 216 additions and 195 deletions

View File

@@ -29,7 +29,7 @@ Ctrl+S Korean Hangul
Ctrl+T English passthrough Ctrl+T English passthrough
Ctrl+V Vietnamese Telex compatibility mode Ctrl+V Vietnamese Telex compatibility mode
Ctrl+E Emoji and symbol search 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 Ctrl+P Toggle popup preedit
``` ```
@@ -47,12 +47,13 @@ English aliases or the active language's input rules, including Japanese
Ctrl+E → smile → Enter → 😀 Ctrl+E → smile → Enter → 😀
``` ```
Hanja search is available only from Korean mode and returns to Korean mode Hanja search is temporary and returns to the active language after one
after one conversion. Press Ctrl+H, type exactly one Hangul syllable with conversion. Press Ctrl+H and type with that language's normal input rules,
the normal 2-beolsik Shift and Caps Lock behavior, then select with Up, Down, then select with Up, Down, Tab, Enter, or `1`-`9`. The bundled dictionary
Tab, Enter, or `1`-`9`. Escape cancels the search; when there is no match, currently indexes exactly one modern Hangul syllable. Incomplete jamo,
Enter commits the Hangul reading. It does not convert words or text already multiple syllables, and other languages therefore have no candidates; Enter
surrounding the cursor. commits the displayed reading unchanged. Escape cancels the search. It does
not convert words or text already surrounding the cursor.
## Build and test ## Build and test

1
dat.h
View File

@@ -13,6 +13,7 @@ enum
LangJP = 0x0e, LangJP = 0x0e,
LangJPK = 0x0b, LangJPK = 0x0b,
LangKO = 0x13, LangKO = 0x13,
LangHANJA = 0x08,
LangEMOJI = 0x05, LangEMOJI = 0x05,
LangVI = 0x16, LangVI = 0x16,

View File

@@ -16,20 +16,12 @@ static int maplookup(Trie*, Str*, Str*);
typedef struct Search Search; typedef struct Search Search;
struct Search struct Search
{ {
int kind; int lang;
Str raw; Str raw;
Str text; Str text;
}; };
static Search search; static Search search;
enum
{
SearchOff,
SearchEmoji,
SearchHanja,
Hanjakey = 0x08,
};
uvlong uvlong
ownernew(void) ownernew(void)
{ {
@@ -47,7 +39,8 @@ Lang langs[] = {
{LangEN, nil, nil, nil, nil, nil, nil, nil}, {LangEN, nil, nil, nil, nil, nil, nil, nil},
{LangJP, "hira", "kanji", transmap, backjp, dictqjp, nil, nil}, {LangJP, "hira", "kanji", transmap, backjp, dictqjp, nil, nil},
{LangJPK, "kata", nil, 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}, {LangEMOJI, nil, "emoji", nil, nil, nil, nil, nil},
{LangVI, "telex", nil, transvi, backvi, dictqmap, nil, nil}, {LangVI, "telex", nil, transvi, backvi, dictqmap, nil, nil},
}; };
@@ -123,12 +116,12 @@ show(void)
int i, first, n; int i, first, n;
sclear(&dc.pre); sclear(&dc.pre);
if(search.kind) if(search.lang)
pre = search.text; pre = search.text;
else else
impre(&pre); impre(&pre);
if(popup){ 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)) !mapget(im.l->map, &pre, &dc.pre))
dc.pre = pre; dc.pre = pre;
} }
@@ -154,7 +147,7 @@ reset(void)
{ {
sclear(&im.pre); sclear(&im.pre);
sclear(&im.raw); sclear(&im.raw);
search.kind = SearchOff; search.lang = 0;
sclear(&search.raw); sclear(&search.raw);
sclear(&search.text); sclear(&search.text);
memset(&caret, 0, sizeof caret); memset(&caret, 0, sizeof caret);
@@ -511,38 +504,39 @@ hanjaquery(void)
static Dictres res; static Dictres res;
Str key; Str key;
transstr(getlang(LangKO), &search.raw, &key); transstr(im.l, &search.raw, &key);
search.text = key; search.text = key;
searchlookup(LangKO, &key, &res); if(key.n == 1 && key.r[0] >= 0xac00 && key.r[0] <= 0xd7a3){
searchlookup(LangHANJA, &key, &res);
setkouho(&res); setkouho(&res);
}else
clearkouho();
show(); show();
} }
static void static void
searchquery(void) searchquery(void)
{ {
if(search.kind == SearchEmoji) if(search.lang == LangEMOJI)
emojiquery(); emojiquery();
else if(search.kind == SearchHanja) else if(search.lang == LangHANJA)
hanjaquery(); hanjaquery();
} }
static int static int
searchkind(Rune c, u32int mod) searchlang(Rune c, u32int mod)
{ {
if(c == LangEMOJI)
return SearchEmoji;
if(mod & (Malt|Msuper)) if(mod & (Malt|Msuper))
return SearchOff; return 0;
if(c == Hanjakey && im.l->lang == LangKO) if(c == LangEMOJI || c == LangHANJA)
return SearchHanja; return c;
return SearchOff; return 0;
} }
static void static void
endsearch(void) endsearch(void)
{ {
search.kind = SearchOff; search.lang = 0;
sclear(&search.raw); sclear(&search.raw);
sclear(&search.text); sclear(&search.text);
clearkouho(); clearkouho();
@@ -550,14 +544,14 @@ endsearch(void)
} }
static void static void
startsearch(int kind, Str *com) startsearch(int lang, Str *com)
{ {
if(haspre(&im)) if(haspre(&im))
commit(com); commit(com);
sclear(&im.pre); sclear(&im.pre);
sclear(&im.raw); sclear(&im.raw);
clearkouho(); clearkouho();
search.kind = kind; search.lang = lang;
sclear(&search.raw); sclear(&search.raw);
sclear(&search.text); sclear(&search.text);
show(); show();
@@ -636,11 +630,16 @@ searchkey(u32int ks, u32int mod, Str *com)
endsearch(); endsearch();
return 1; return 1;
} }
if(ks >= Kspec || (ks < ' ' && !(mod & Mctrl)) ||
(mod & (Malt|Msuper))){
commitsearch(com);
return 0;
}
if(mod & Mctrl){ if(mod & Mctrl){
c = ctrlkey(ks); c = ctrlkey(ks);
n = searchkind(c, mod); n = searchlang(c, mod);
if(n != SearchOff){ if(n != 0){
if(n == search.kind) if(n == search.lang)
endsearch(); endsearch();
else else
startsearch(n, com); startsearch(n, com);
@@ -656,15 +655,11 @@ searchkey(u32int ks, u32int mod, Str *com)
return 1; return 1;
return 0; return 0;
} }
if(ks >= Kspec || ks < ' ' || (mod & (Malt|Msuper))){
commitsearch(com);
return 0;
}
if(search.raw.n >= Maxrunes){ if(search.raw.n >= Maxrunes){
commitsearch(com); commitsearch(com);
return 0; return 0;
} }
if(search.kind == SearchHanja) if(im.l->lang == LangKO)
ks = kokey(ks, mod); ks = kokey(ks, mod);
sputr(&search.raw, ks); sputr(&search.raw, ks);
searchquery(); searchquery();
@@ -679,7 +674,7 @@ keystroke(u32int ks, u32int mod, Str *com)
if(ismodkey(ks)) if(ismodkey(ks))
return 0; return 0;
if(search.kind) if(search.lang)
return searchkey(ks, mod, com); return searchkey(ks, mod, com);
if(ks == Kdown || ks == Kup){ if(ks == Kdown || ks == Kup){
if(im.nkouho == 0) if(im.nkouho == 0)
@@ -738,8 +733,8 @@ keystroke(u32int ks, u32int mod, Str *com)
} }
if(mod & Mctrl){ if(mod & Mctrl){
c = ctrlkey(ks); c = ctrlkey(ks);
n = searchkind(c, mod); n = searchlang(c, mod);
if(n != SearchOff){ if(n != 0){
startsearch(n, com); startsearch(n, com);
return 1; return 1;
} }
@@ -844,7 +839,7 @@ imhandlekey(Keyreq *kr)
break; break;
} }
if(kr->want && kr->owner == activeowner){ if(kr->want && kr->owner == activeowner){
if(search.kind) if(search.lang)
res.preedit = search.text; res.preedit = search.text;
else else
impre(&res.preedit); impre(&res.preedit);
@@ -858,7 +853,7 @@ dictresult(Dictres *res)
Str pre; Str pre;
impre(&pre); impre(&pre);
if(search.kind || res->lang != im.l->lang || if(search.lang || res->lang != im.l->lang ||
scmp(&res->key, &pre) != 0) scmp(&res->key, &pre) != 0)
return; return;
setkouho(res); setkouho(res);

View File

@@ -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.pre.n);
CT_EQ_INT(t, 0, im.raw.n); CT_EQ_INT(t, 0, im.raw.n);
CT_CHECK(t, keystroke('e', Mctrl, &com)); 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_EQ_INT(t, LangEN, im.l->lang);
CT_CHECK(t, keystroke(Kesc, 0, &com)); 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_CHECK(t, keystroke('s', Mctrl, &com));
CT_EQ_INT(t, LangKO, im.l->lang); CT_EQ_INT(t, LangKO, im.l->lang);
CT_EQ_PTR(t, nil, im.l->mapname); CT_EQ_PTR(t, nil, im.l->mapname);
CT_EQ_PTR(t, nil, im.l->map); 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_EQ_PTR(t, nil, im.l->dictq);
CT_CHECK(t, strcmp(getlang(LangHANJA)->dictname, "hanja") == 0);
CT_CHECK(t, keystroke('r', 0, &com)); CT_CHECK(t, keystroke('r', 0, &com));
checkstr(t, "Korean without map", "", &im.pre); checkstr(t, "Korean without map", "", &im.pre);
CT_CHECK(t, keystroke(Kback, 0, &com)); CT_CHECK(t, keystroke(Kback, 0, &com));
@@ -509,11 +511,12 @@ engine_language_switch_state(struct ct *t)
vi->map = saved; vi->map = saved;
} }
typedef struct Emojifix Emojifix; typedef struct Searchfix Searchfix;
struct Emojifix struct Searchfix
{ {
Im im; Im im;
Search search; Search search;
Lang *dictlang;
Hmap *dict; Hmap *dict;
int popup; int popup;
int visible; int visible;
@@ -544,33 +547,37 @@ setdict(Hmap **dict, char *key, char *val)
} }
static void static void
emojibegin(Emojifix *f, int showpre) searchsave(Searchfix *f, int lang)
{ {
Lang *l;
l = getlang(LangEMOJI);
f->im = im; f->im = im;
f->search = search; f->search = search;
f->dict = l->dict; f->dictlang = getlang(lang);
f->dict = f->dictlang->dict;
f->popup = popup; f->popup = popup;
f->visible = visible; f->visible = visible;
draindraw(nil); draindraw(nil);
l->dict = hmapalloc(32); }
setdict(&l->dict, "a", "A B");
setdict(&l->dict, "", "B C"); static void
setdict(&l->dict, "alpha", "α"); emojibegin(Searchfix *f, int showpre)
setdict(&l->dict, "smil", "😀 😄"); {
setdict(&l->dict, "smile", "😀 😄"); searchsave(f, LangEMOJI);
setdict(&l->dict, "웃음", "😀 😄 😂"); f->dictlang->dict = hmapalloc(32);
setdict(&l->dict, "えがお", "😀 😄 😊"); setdict(&f->dictlang->dict, "a", "A B");
setdict(&l->dict, "エガオ", "😀 😄 😊"); setdict(&f->dictlang->dict, "", "B C");
setdict(&l->dict, "heart", "❤️"); setdict(&f->dictlang->dict, "alpha", "α");
setdict(&l->dict, "coffee", "☕️"); setdict(&f->dictlang->dict, "smil", "😀 😄");
setdict(&l->dict, "^", "¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁽"); setdict(&f->dictlang->dict, "smile", "😀 😄");
setdict(&l->dict, "^0", ""); setdict(&f->dictlang->dict, "웃음", "😀 😄 😂");
setdict(&l->dict, "_", "₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₍"); setdict(&f->dictlang->dict, "えがお", "😀 😄 😊");
setdict(&l->dict, "<", "← ≤ ♥ ≠"); setdict(&f->dictlang->dict, "エガオ", "😀 😄 😊");
setdict(&l->dict, "many", 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"); "c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12");
init(); init();
im.l = getlang(LangKO); im.l = getlang(LangKO);
@@ -578,14 +585,12 @@ emojibegin(Emojifix *f, int showpre)
} }
static void static void
emojiend(Emojifix *f) searchend(Searchfix *f)
{ {
Lang *l;
Hmap *dict; Hmap *dict;
l = getlang(LangEMOJI); dict = f->dictlang->dict;
dict = l->dict; f->dictlang->dict = f->dict;
l->dict = f->dict;
hmapfree(dict); hmapfree(dict);
draindraw(nil); draindraw(nil);
im = f->im; im = f->im;
@@ -594,50 +599,18 @@ emojiend(Emojifix *f)
visible = f->visible; visible = f->visible;
} }
typedef struct Hanjafix Hanjafix;
struct Hanjafix
{
Im im;
Search search;
Hmap *dict;
int popup;
int visible;
};
static void static void
hanjabegin(Hanjafix *f) hanjabegin(Searchfix *f, int lang)
{ {
Lang *l; searchsave(f, LangHANJA);
f->dictlang->dict = hmapalloc(8);
l = getlang(LangKO); setdict(&f->dictlang->dict, "", "漢 韓");
f->im = im; setdict(&f->dictlang->dict, "", "");
f->search = search; setdict(&f->dictlang->dict, "", "");
f->dict = l->dict; setdict(&f->dictlang->dict, "한글", "");
f->popup = popup; setdict(&f->dictlang->dict, "", "");
f->visible = visible;
draindraw(nil);
l->dict = hmapalloc(8);
setdict(&l->dict, "", "漢 韓");
setdict(&l->dict, "", "");
init(); init();
im.l = l; im.l = getlang(lang);
}
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 static int
@@ -817,7 +790,7 @@ void
engine_emoji_single_candidate(struct ct *t) engine_emoji_single_candidate(struct ct *t)
{ {
Drawcmd dc = {0}; Drawcmd dc = {0};
Emojifix f; Searchfix f;
Str com; Str com;
emojibegin(&f, 1); emojibegin(&f, 1);
@@ -825,12 +798,12 @@ engine_emoji_single_candidate(struct ct *t)
CT_CHECK(t, keystroke('p', Mctrl, &com)); CT_CHECK(t, keystroke('p', Mctrl, &com));
CT_EQ_INT(t, 0, popup); CT_EQ_INT(t, 0, popup);
CT_CHECK(t, keystroke('e', Mctrl, &com)); 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); CT_EQ_INT(t, LangKO, im.l->lang);
if(typekeys(t, "alpha", &com)){ if(typekeys(t, "alpha", &com)){
checkstr(t, "no automatic commit", "", &com); checkstr(t, "no automatic commit", "", &com);
checkstr(t, "raw English query", "alpha", &search.text); 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); CT_EQ_INT(t, 1, im.nkouho);
checkstr(t, "single candidate", "α", &im.kouho[0]); checkstr(t, "single candidate", "α", &im.kouho[0]);
CT_CHECK(t, draindraw(&dc) > 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_EQ_INT(t, 1, dc.nkouho);
CT_CHECK(t, keystroke(Kret, 0, &com)); CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "explicit candidate commit", "α", &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); CT_EQ_INT(t, LangKO, im.l->lang);
show(); show();
CT_CHECK(t, draindraw(&dc) > 0); CT_CHECK(t, draindraw(&dc) > 0);
CT_EQ_INT(t, 0, dc.pre.n); CT_EQ_INT(t, 0, dc.pre.n);
CT_EQ_INT(t, 0, dc.nkouho); CT_EQ_INT(t, 0, dc.nkouho);
} }
emojiend(&f); searchend(&f);
} }
void void
engine_emoji_queries(struct ct *t) engine_emoji_queries(struct ct *t)
{ {
Drawcmd dc = {0}; Drawcmd dc = {0};
Emojifix f; Searchfix f;
Str com; Str com;
emojibegin(&f, 1); emojibegin(&f, 1);
@@ -900,16 +873,16 @@ engine_emoji_queries(struct ct *t)
CT_EQ_INT(t, 3, dc.nkouho); CT_EQ_INT(t, 3, dc.nkouho);
CT_CHECK(t, keystroke(Kesc, 0, &com)); CT_CHECK(t, keystroke(Kesc, 0, &com));
checkstr(t, "cancel commit", "", &com); checkstr(t, "cancel commit", "", &com);
CT_CHECK(t, !search.kind); CT_CHECK(t, !search.lang);
CT_EQ_INT(t, LangKO, im.l->lang); CT_EQ_INT(t, LangKO, im.l->lang);
} }
emojiend(&f); searchend(&f);
} }
void void
engine_emoji_japanese_and_multirune(struct ct *t) engine_emoji_japanese_and_multirune(struct ct *t)
{ {
Emojifix f; Searchfix f;
Str com; Str com;
emojibegin(&f, 0); emojibegin(&f, 0);
@@ -950,7 +923,7 @@ engine_emoji_japanese_and_multirune(struct ct *t)
CT_CHECK(t, keystroke(Kret, 0, &com)); CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "multi-codepoint emoji", "☕️", &com); checkstr(t, "multi-codepoint emoji", "☕️", &com);
} }
emojiend(&f); searchend(&f);
} }
void void
@@ -966,7 +939,7 @@ engine_emoji_digit_aliases(struct ct *t)
{ "_2", '_', '2', "" }, { "_2", '_', '2', "" },
{ "<3", '<', '3', "" }, { "<3", '<', '3', "" },
}; };
Emojifix f; Searchfix f;
Str com; Str com;
int i; 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, im.nkouho >= cases[i].digit - '0');
CT_CHECK(t, keystroke(cases[i].digit, 0, &com)); CT_CHECK(t, keystroke(cases[i].digit, 0, &com));
checkstr(t, cases[i].name, cases[i].want, &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); 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)); CT_CHECK(t, keystroke('9', 0, &com));
checkstr(t, "missing number query", "smile9", &search.text); checkstr(t, "missing number query", "smile9", &search.text);
checkstr(t, "missing number commit", "", &com); checkstr(t, "missing number commit", "", &com);
CT_CHECK(t, search.kind); CT_CHECK(t, search.lang);
CT_EQ_INT(t, 0, im.nkouho); CT_EQ_INT(t, 0, im.nkouho);
CT_CHECK(t, keystroke(Kret, 0, &com)); CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "missing number text", "smile9", &com); checkstr(t, "missing number text", "smile9", &com);
} }
emojiend(&f); searchend(&f);
} }
void void
engine_emoji_navigation(struct ct *t) engine_emoji_navigation(struct ct *t)
{ {
Drawcmd dc = {0}; Drawcmd dc = {0};
Emojifix f; Searchfix f;
Str com; Str com;
int i; int i;
@@ -1054,13 +1027,13 @@ engine_emoji_navigation(struct ct *t)
CT_CHECK(t, keystroke(Kret, 0, &com)); CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "navigated candidate", "😄", &com); checkstr(t, "navigated candidate", "😄", &com);
} }
emojiend(&f); searchend(&f);
} }
void void
engine_emoji_preedit_languages(struct ct *t) engine_emoji_preedit_languages(struct ct *t)
{ {
Emojifix f; Searchfix f;
Str com, shown; Str com, shown;
int p; int p;
@@ -1074,26 +1047,26 @@ engine_emoji_preedit_languages(struct ct *t)
checkstr(t, "modifier preserves Japanese", "", &shown); checkstr(t, "modifier preserves Japanese", "", &shown);
CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com));
checkstr(t, "committed Japanese preedit", "", &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_EQ_INT(t, LangJP, im.l->lang);
CT_CHECK(t, keystroke('E', Mctrl|Malt, &com)); CT_CHECK(t, !keystroke('E', Mctrl|Malt, &com));
CT_EQ_INT(t, SearchOff, search.kind); CT_EQ_INT(t, 0, search.lang);
CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com));
CT_CHECK(t, keystroke('E', Mctrl|Msuper, &com)); CT_CHECK(t, !keystroke('E', Mctrl|Msuper, &com));
CT_EQ_INT(t, SearchOff, search.kind); CT_EQ_INT(t, 0, search.lang);
CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com));
CT_EQ_INT(t, LangJP, im.l->lang); CT_EQ_INT(t, LangJP, im.l->lang);
CT_CHECK(t, keystroke('a', 0, &com)); CT_CHECK(t, keystroke('a', 0, &com));
p = popup; p = popup;
CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com)); 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); checkstr(t, "modifier preserves search", "a", &search.text);
CT_CHECK(t, keystroke('P', Mctrl|Mshift, &com)); CT_CHECK(t, keystroke('P', Mctrl|Mshift, &com));
CT_EQ_INT(t, !p, popup); 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(Kmodfirst+2, 0, &com));
CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com)); CT_CHECK(t, keystroke('E', Mctrl|Mshift, &com));
CT_CHECK(t, !search.kind); CT_CHECK(t, !search.lang);
sclear(&com); sclear(&com);
if(typekeys(t, "na", &com)){ if(typekeys(t, "na", &com)){
shown = shownpre(&im); shown = shownpre(&im);
@@ -1123,7 +1096,7 @@ engine_emoji_preedit_languages(struct ct *t)
CT_CHECK(t, keystroke('e', Mctrl, &com)); CT_CHECK(t, keystroke('e', Mctrl, &com));
checkstr(t, "committed Telex preedit", "á", &com); checkstr(t, "committed Telex preedit", "á", &com);
CT_CHECK(t, keystroke(Kesc, 0, &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); CT_EQ_PTR(t, &testvi, im.l);
sclear(&com); sclear(&com);
if(typekeys(t, "as", &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); checkstr(t, "Telex raw resumed", "as", &im.raw);
} }
} }
emojiend(&f); searchend(&f);
} }
void void
engine_emoji_start_and_unknown(struct ct *t) engine_emoji_start_and_unknown(struct ct *t)
{ {
Emojifix f; Searchfix f;
Str com; Str com;
char want[Maxrunes+1]; char want[Maxrunes+1];
int i; int i;
@@ -1148,14 +1121,14 @@ engine_emoji_start_and_unknown(struct ct *t)
if(typekeys(t, "dkssud", &com)){ if(typekeys(t, "dkssud", &com)){
CT_CHECK(t, keystroke('e', Mctrl, &com)); CT_CHECK(t, keystroke('e', Mctrl, &com));
checkstr(t, "committed Korean preedit", "안녕", &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); CT_EQ_INT(t, LangKO, im.l->lang);
if(typekeys(t, "xyz", &com)){ if(typekeys(t, "xyz", &com)){
checkstr(t, "unknown displayed query", "xyz", &search.text); checkstr(t, "unknown displayed query", "xyz", &search.text);
CT_EQ_INT(t, 0, im.nkouho); CT_EQ_INT(t, 0, im.nkouho);
CT_CHECK(t, keystroke(Kret, 0, &com)); CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "unknown query commit", "안녕xyz", &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); 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)){ if(typekeys(t, "xyz", &com)){
CT_CHECK(t, !keystroke(Kspec|0x51, 0, &com)); CT_CHECK(t, !keystroke(Kspec|0x51, 0, &com));
checkstr(t, "special commits shown query", "xyz", &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); 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++) for(i = 0; i < Maxrunes; i++)
CT_CHECK(t, keystroke('q', 0, &com)); CT_CHECK(t, keystroke('q', 0, &com));
CT_EQ_INT(t, Maxrunes, search.raw.n); 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)); CT_CHECK(t, !keystroke('z', 0, &com));
memset(want, 'q', Maxrunes); memset(want, 'q', Maxrunes);
want[Maxrunes] = '\0'; want[Maxrunes] = '\0';
checkstr(t, "bounded query commit", want, &com); checkstr(t, "bounded query commit", want, &com);
CT_CHECK(t, !search.kind); CT_CHECK(t, !search.lang);
emojiend(&f); searchend(&f);
} }
void void
engine_emoji_dictionary_identity(struct ct *t) engine_emoji_dictionary_identity(struct ct *t)
{ {
Dictres res; Dictres res;
Emojifix f; Searchfix f;
emojibegin(&f, 0); emojibegin(&f, 0);
im.pre = mkstr("same"); im.pre = mkstr("same");
@@ -1210,31 +1183,31 @@ engine_emoji_dictionary_identity(struct ct *t)
checkstr(t, "wrong preedit ignored", "old", &im.kouho[0]); checkstr(t, "wrong preedit ignored", "old", &im.kouho[0]);
res.key = im.pre; res.key = im.pre;
search.kind = SearchEmoji; search.lang = LangEMOJI;
dictresult(&res); dictresult(&res);
CT_EQ_INT(t, 1, im.nkouho); CT_EQ_INT(t, 1, im.nkouho);
checkstr(t, "search response ignored", "old", &im.kouho[0]); checkstr(t, "search response ignored", "old", &im.kouho[0]);
search.kind = SearchOff; search.lang = 0;
res.kouho[0] = mkstr("right"); res.kouho[0] = mkstr("right");
dictresult(&res); dictresult(&res);
CT_EQ_INT(t, 1, im.nkouho); CT_EQ_INT(t, 1, im.nkouho);
CT_EQ_INT(t, -1, im.sel); CT_EQ_INT(t, -1, im.sel);
checkstr(t, "matching response accepted", "right", &im.kouho[0]); checkstr(t, "matching response accepted", "right", &im.kouho[0]);
emojiend(&f); searchend(&f);
} }
void void
engine_hanja_search(struct ct *t) engine_hanja_search(struct ct *t)
{ {
Hanjafix f; Searchfix f;
Str com; Str com;
hanjabegin(&f); hanjabegin(&f, LangKO);
sclear(&com); sclear(&com);
if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) if(!CT_CHECK(t, keystroke('h', Mctrl, &com)))
goto cleanup; goto cleanup;
CT_EQ_INT(t, SearchHanja, search.kind); CT_EQ_INT(t, LangHANJA, search.lang);
CT_EQ_INT(t, LangKO, im.l->lang); CT_EQ_INT(t, LangKO, im.l->lang);
if(!typekeys(t, "gks", &com)) if(!typekeys(t, "gks", &com))
goto cleanup; goto cleanup;
@@ -1244,19 +1217,19 @@ engine_hanja_search(struct ct *t)
CT_CHECK(t, keystroke(Kdown, 0, &com)); CT_CHECK(t, keystroke(Kdown, 0, &com));
CT_CHECK(t, keystroke(Kret, 0, &com)); CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "navigated Hanja", "", &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); CT_EQ_INT(t, LangKO, im.l->lang);
cleanup: cleanup:
hanjaend(&f); searchend(&f);
} }
void void
engine_hanja_unknown_and_cancel(struct ct *t) engine_hanja_unknown_and_cancel(struct ct *t)
{ {
Hanjafix f; Searchfix f;
Str com; Str com;
hanjabegin(&f); hanjabegin(&f, LangKO);
sclear(&com); sclear(&com);
if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) if(!CT_CHECK(t, keystroke('h', Mctrl, &com)))
goto cleanup; goto cleanup;
@@ -1266,9 +1239,29 @@ engine_hanja_unknown_and_cancel(struct ct *t)
CT_EQ_INT(t, 0, im.nkouho); CT_EQ_INT(t, 0, im.nkouho);
CT_CHECK(t, keystroke(Kret, 0, &com)); CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "unknown Hanja commit", "", &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); 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); sclear(&com);
if(!CT_CHECK(t, keystroke('h', Mctrl, &com))) if(!CT_CHECK(t, keystroke('h', Mctrl, &com)))
goto cleanup; goto cleanup;
@@ -1277,25 +1270,25 @@ engine_hanja_unknown_and_cancel(struct ct *t)
CT_EQ_INT(t, 2, im.nkouho); CT_EQ_INT(t, 2, im.nkouho);
CT_CHECK(t, keystroke(Kesc, 0, &com)); CT_CHECK(t, keystroke(Kesc, 0, &com));
checkstr(t, "cancelled Hanja", "", &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); CT_EQ_INT(t, LangKO, im.l->lang);
sclear(&com); sclear(&com);
CT_CHECK(t, keystroke('h', Mctrl, &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_CHECK(t, keystroke('h', Mctrl, &com));
CT_EQ_INT(t, SearchOff, search.kind); CT_EQ_INT(t, 0, search.lang);
cleanup: cleanup:
hanjaend(&f); searchend(&f);
} }
void void
engine_hanja_korean_keys(struct ct *t) engine_hanja_korean_keys(struct ct *t)
{ {
Hanjafix f; Searchfix f;
Str com; Str com;
hanjabegin(&f); hanjabegin(&f, LangKO);
sclear(&com); sclear(&com);
CT_CHECK(t, keystroke('h', Mctrl, &com)); CT_CHECK(t, keystroke('h', Mctrl, &com));
CT_CHECK(t, keystroke('r', Mshift, &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); checkstr(t, "Caps Lock Hanja reading", "", &search.text);
CT_EQ_INT(t, 1, im.nkouho); CT_EQ_INT(t, 1, im.nkouho);
CT_CHECK(t, keystroke(Kesc, 0, &com)); CT_CHECK(t, keystroke(Kesc, 0, &com));
hanjaend(&f); searchend(&f);
} }
void void
engine_hanja_korean_only(struct ct *t) engine_hanja_input_languages(struct ct *t)
{ {
Hmap *dict; Searchfix f;
Lang *l; Str com;
Str com, shown;
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(); init();
im.l = getlang(LangJP); im.l = getlang(LangJP);
sclear(&com); sclear(&com);
if(typekeys(t, "ka", &com)){ if(!CT_CHECK(t, keystroke('h', Mctrl, &com)))
CT_CHECK(t, !keystroke('h', Mctrl, &com)); goto cleanup;
CT_CHECK(t, !search.kind); 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); 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(); init();
im.l = l; im.l = getlang(LangEN);
sclear(&com); sclear(&com);
CT_CHECK(t, keystroke('h', Mctrl, &com)); CT_CHECK(t, keystroke('h', Mctrl, &com));
CT_EQ_INT(t, SearchHanja, search.kind); CT_CHECK(t, keystroke(L'', 0, &com));
CT_CHECK(t, keystroke(Kesc, 0, &com)); checkstr(t, "direct Hanja input", "", &search.text);
l->dict = dict; 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 static u32int
@@ -1364,7 +1388,7 @@ engine_randomized_stress(struct ct *t)
'u', 'w', 'y', '1', '2', '9', '\'', ' ', L'', 'u', 'w', 'y', '1', '2', '9', '\'', ' ', L'',
}; };
static Rune modes[] = { 't', 'n', 'k', 's', 'v' }; static Rune modes[] = { 't', 'n', 'k', 's', 'v' };
Emojifix f; Searchfix f;
Lang *vi; Lang *vi;
Trie *vimap; Trie *vimap;
Keyres res; Keyres res;
@@ -1452,7 +1476,7 @@ engine_randomized_stress(struct ct *t)
break; break;
} }
} }
emojiend(&f); searchend(&f);
vi->map = vimap; vi->map = vimap;
} }

View File

@@ -57,7 +57,7 @@ void engine_emoji_dictionary_identity(struct ct*);
void engine_hanja_search(struct ct*); void engine_hanja_search(struct ct*);
void engine_hanja_unknown_and_cancel(struct ct*); void engine_hanja_unknown_and_cancel(struct ct*);
void engine_hanja_korean_keys(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_randomized_stress(struct ct*);
void engine_full_boundary_passthrough(struct ct*); void engine_full_boundary_passthrough(struct ct*);
void dictionary_candidates(struct ct*); void dictionary_candidates(struct ct*);

View File

@@ -111,7 +111,7 @@ static const struct ct_test tests[] = {
{ "engine/hanja-search", engine_hanja_search }, { "engine/hanja-search", engine_hanja_search },
{ "engine/hanja-unknown-cancel", engine_hanja_unknown_and_cancel }, { "engine/hanja-unknown-cancel", engine_hanja_unknown_and_cancel },
{ "engine/hanja-korean-keys", engine_hanja_korean_keys }, { "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/randomized-stress", engine_randomized_stress },
{ "engine/full-boundary-passthrough", engine_full_boundary_passthrough }, { "engine/full-boundary-passthrough", engine_full_boundary_passthrough },
{ "dict/candidates", dictionary_candidates }, { "dict/candidates", dictionary_candidates },