emoji: add one-shot candidate search

This commit is contained in:
2026-08-11 22:24:59 +09:00
parent 82276bf37f
commit 0fff17c8b8
14 changed files with 891 additions and 346 deletions

View File

@@ -25,6 +25,7 @@ dictionary_candidates(struct ct *t)
dictlookup(&req, &res);
if(!CT_EQ_INT(t, 2, res.nkouho))
goto cleanup;
CT_EQ_INT(t, LangJP, res.lang);
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
checkstr(t, "candidate 1", "候補1", &res.kouho[0]);
checkstr(t, "candidate 2", "候補2", &res.kouho[1]);
@@ -44,6 +45,7 @@ dictionary_candidates(struct ct *t)
dictlookup(&req, &res);
if(!CT_EQ_INT(t, Maxkouho, res.nkouho))
goto cleanup;
CT_EQ_INT(t, LangJP, res.lang);
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
checkstr(t, "first capped candidate", "c00", &res.kouho[0]);
checkstr(t, "last capped candidate", "c31", &res.kouho[31]);
@@ -81,6 +83,7 @@ dictionary_misses_clear_result(struct ct *t)
if(res.nkouho != 0)
CT_ERRORF(t, "%s: want 0 candidates, got %d",
cases[i].name, res.nkouho);
CT_EQ_INT(t, LangJP, res.lang);
checkstr(t, cases[i].name, cases[i].pre, &res.key);
}
hmapfree(lang->dict);

View File

@@ -255,3 +255,177 @@ engine_language_switch_state(struct ct *t)
}
vi->map = saved;
}
typedef struct Emojifix Emojifix;
struct Emojifix
{
Im im;
Search search;
Hmap *dict;
int popup;
int visible;
};
static int
draindraw(Drawcmd *last)
{
Drawcmd dc = {0};
int n;
n = 0;
while(channbrecv(drawc, &dc) > 0){
n++;
if(last != nil)
*last = dc;
}
return n;
}
static void
setemoji(Hmap **dict, char *key, char *val)
{
Str s;
s = mkstr(key);
hmapset(dict, &s, val, strlen(val));
}
static void
emojibegin(Emojifix *f, int showpre)
{
Lang *l;
l = getlang(LangEMOJI);
f->im = im;
f->search = search;
f->dict = l->dict;
f->popup = popup;
f->visible = visible;
draindraw(nil);
l->dict = hmapalloc(8);
setemoji(&l->dict, "alpha", "α");
setemoji(&l->dict, "smil", "😀 😄");
setemoji(&l->dict, "smile", "😀 😄");
setemoji(&l->dict, "웃음", "😀 😄 😂");
init();
im.l = getlang(LangKO);
popup = showpre;
}
static void
emojiend(Emojifix *f)
{
Lang *l;
Hmap *dict;
l = getlang(LangEMOJI);
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)
{
for(; *keys != '\0'; keys++)
if(!CT_CHECK(t, keystroke((uchar)*keys, 0, com)))
return 0;
return 1;
}
void
engine_emoji_single_candidate(struct ct *t)
{
Drawcmd dc = {0};
Emojifix f;
Str com;
emojibegin(&f, 0);
sclear(&com);
CT_CHECK(t, keystroke('e', Mctrl, &com));
CT_CHECK(t, search.on);
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_EQ_INT(t, 1, im.nkouho);
checkstr(t, "single candidate", "α", &im.kouho[0]);
CT_CHECK(t, draindraw(&dc) > 0);
CT_EQ_INT(t, 0, dc.pre.n);
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_EQ_INT(t, LangKO, im.l->lang);
}
emojiend(&f);
}
void
engine_emoji_queries(struct ct *t)
{
Drawcmd dc = {0};
Emojifix f;
Str com;
emojibegin(&f, 1);
sclear(&com);
CT_CHECK(t, keystroke('e', Mctrl, &com));
if(typekeys(t, "smile", &com)){
checkstr(t, "English alias", "smile", &search.text);
CT_EQ_INT(t, 2, im.nkouho);
CT_CHECK(t, keystroke(Kback, 0, &com));
checkstr(t, "backspaced query", "smil", &search.text);
CT_EQ_INT(t, 2, im.nkouho);
CT_CHECK(t, keystroke('e', 0, &com));
CT_CHECK(t, keystroke('2', Malt, &com));
checkstr(t, "selected English alias", "😄", &com);
CT_EQ_INT(t, LangKO, im.l->lang);
}
sclear(&com);
CT_CHECK(t, keystroke('e', Mctrl, &com));
if(typekeys(t, "dntdma", &com)){
checkstr(t, "localized Korean query", "웃음", &search.text);
CT_EQ_INT(t, 3, im.nkouho);
CT_CHECK(t, draindraw(&dc) > 0);
checkstr(t, "popup query", "웃음", &dc.pre);
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_EQ_INT(t, LangKO, im.l->lang);
}
emojiend(&f);
}
void
engine_emoji_start_and_unknown(struct ct *t)
{
Emojifix f;
Str com;
emojibegin(&f, 0);
sclear(&com);
if(typekeys(t, "dkssud", &com)){
CT_CHECK(t, keystroke('e', Mctrl, &com));
checkstr(t, "committed Korean preedit", "안녕", &com);
CT_CHECK(t, search.on);
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_EQ_INT(t, LangKO, im.l->lang);
}
}
emojiend(&f);
}

View File

@@ -33,6 +33,9 @@ void engine_selects_visible_candidate(struct ct*);
void engine_commit_contract(struct ct*);
void engine_language_switch_state(struct ct*);
void engine_telex_history_bound(struct ct*);
void engine_emoji_single_candidate(struct ct*);
void engine_emoji_queries(struct ct*);
void engine_emoji_start_and_unknown(struct ct*);
void dictionary_candidates(struct ct*);
void dictionary_misses_clear_result(struct ct*);

View File

@@ -80,6 +80,9 @@ static const struct ct_test tests[] = {
{ "engine/commit-contract", engine_commit_contract },
{ "engine/language-switch-state", engine_language_switch_state },
{ "engine/telex-history-bound", engine_telex_history_bound },
{ "engine/emoji-single-candidate", engine_emoji_single_candidate },
{ "engine/emoji-queries", engine_emoji_queries },
{ "engine/emoji-start-and-unknown", engine_emoji_start_and_unknown },
{ "dict/candidates", dictionary_candidates },
{ "dict/misses-clear-result", dictionary_misses_clear_result },
};
@@ -89,7 +92,7 @@ threadmain(int argc, char **argv)
{
int i, status;
drawc = chancreate(sizeof(Drawcmd), 4);
drawc = chancreate(sizeof(Drawcmd), 1024);
keyc = chancreate(sizeof(Keyreq), 0);
dictreqc = chancreate(sizeof(Dictreq), 64);
dictresc = chancreate(sizeof(Dictres), 0);