fix: tighten emoji lookup contracts

This commit is contained in:
2026-08-12 15:36:37 +09:00
parent d0def814ef
commit fabce14933
9 changed files with 157 additions and 9 deletions

2
dict.c
View File

@@ -33,7 +33,7 @@ dictlookup(Dictreq *req, Dictres *res)
while(p < e && *p != ' ') while(p < e && *p != ' ')
p++; p++;
sinit(&tmp, sp, p - sp); sinit(&tmp, sp, p - sp);
if(scmp(&tmp, &req->key) != 0) if(req->lang == LangEMOJI || scmp(&tmp, &req->key) != 0)
res->kouho[res->nkouho++] = tmp; res->kouho[res->nkouho++] = tmp;
if(p < e) if(p < e)
p++; p++;

View File

@@ -75,9 +75,14 @@ bl 😊
blu 😊 blu 😊
blus 😊 blus 😊
blush 😊 blush 😊
c χ c χ ☕️
ch χ ch χ
chi χ chi χ
co ☕️
cof ☕️
coff ☕️
coffe ☕️
coffee ☕️
d ° δ Δ ↓ d ° δ Δ ↓
de Δ ° δ de Δ ° δ
deg ° deg °
@@ -198,6 +203,65 @@ zet ζ
zeta ζ zeta ζ
~ ≈ ~ ≈
~= ≈ ~= ≈
あ ❤️
あい ❤️
い 👍
いい 👍
いいね 👍
え 😀 😄 😊
えが 😀 😄 😊
えがお 😀 😄 😊
こ ☕️
こー ☕️
こーひ ☕️
こーひー ☕️
さ 👍
さん 👍
さんせ 👍
さんせい 👍
ば 😂
ばく 😂
ばくし 😂
ばくしょ 😂
ばくしょう 😂
ほ 😄 😊
ほほ 😄 😊
ほほえ 😄 😊
ほほえみ 😄 😊
わ 😀 😂
わら 😀 😂
わらい 😀 😂
ア ❤️
アイ ❤️
イ 👍
イイ 👍
イイネ 👍
エ 😀 😄 😊
エガ 😀 😄 😊
エガオ 😀 😄 😊
コ ☕️
コー ☕️
コーヒ ☕️
コーヒー ☕️
サ 👍
サン 👍
サンセ 👍
サンセイ 👍
ハ ❤️
ハー ❤️
ハート ❤️
バ 😂
バク 😂
バクシ 😂
バクショ 😂
バクショウ 😂
ホ 😄 😊
ホホ 😄 😊
ホホエ 😄 😊
ホホエミ 😄 😊
ワ 😀 😂
ワラ 😀 😂
ワライ 😀 😂
따 👍 따 👍
따봉 👍 따봉 👍
미 😄 😊 미 😄 😊
@@ -220,6 +284,8 @@ zeta ζ
좋아요 👍 좋아요 👍
찬 👍 찬 👍
찬성 👍 찬성 👍
커 ☕️
커피 ☕️
폭 😂 폭 😂
폭소 😂 폭소 😂
하 ❤️ 하 ❤️

View File

@@ -103,9 +103,10 @@
ζ zeta ζ zeta
# Small multilingual emoji seed. Shared aliases intentionally produce choices. # Small multilingual emoji seed. Shared aliases intentionally produce choices.
😀 웃음 웃다 스마일 smile grin 😀 웃음 웃다 스마일 えがお エガオ わらい ワライ smile grin
😄 웃음 미소 스마일 smile happy 😄 웃음 미소 스마일 えがお エガオ ほほえみ ホホエミ smile happy
😂 웃음 폭소 웃겨 joy lol 😂 웃음 폭소 웃겨 ばくしょう バクショウ わらい ワライ joy lol
😊 미소 방긋 smile blush happy 😊 미소 방긋 ほほえみ ホホエミ えがお エガオ smile blush happy
❤️ 사랑 하트 heart love ❤️ 사랑 하트 あい アイ ハート heart love
👍 좋아요 찬성 따봉 엄지 thumbsup like 👍 좋아요 찬성 따봉 엄지 いいね イイネ さんせい サンセイ thumbsup like
☕️ 커피 こーひー コーヒー coffee

View File

@@ -38,7 +38,7 @@ def read(path):
for field in fields[1:]: for field in fields[1:]:
alias = fold(field) alias = fold(field)
if (not alias or len(alias) > MAXRUNES or hascontrol(alias) if (not alias or len(alias) > MAXRUNES or hascontrol(alias)
or alias != alias.strip()): or alias != alias.strip() or alias.startswith(";")):
raise ValueError(f"{path}:{lineno}: bad alias") raise ValueError(f"{path}:{lineno}: bad alias")
entries.append((result, alias)) entries.append((result, alias))
return entries return entries

View File

@@ -89,3 +89,28 @@ dictionary_misses_clear_result(struct ct *t)
hmapfree(lang->dict); hmapfree(lang->dict);
lang->dict = saved; lang->dict = saved;
} }
void
dictionary_emoji_identity(struct ct *t)
{
Dictreq req;
Dictres res;
Hmap *saved;
Lang *lang;
Str key;
lang = getlang(LangEMOJI);
saved = lang->dict;
lang->dict = hmapalloc(2);
key = mkstr("é");
hmapset(&lang->dict, &key, "é", strlen("é"));
memset(&req, 0, sizeof req);
req.key = key;
req.pre = key;
req.lang = LangEMOJI;
dictlookup(&req, &res);
if(CT_EQ_INT(t, 1, res.nkouho))
checkstr(t, "emoji identity candidate", "é", &res.kouho[0]);
hmapfree(lang->dict);
lang->dict = saved;
}

View File

@@ -404,6 +404,10 @@ emojibegin(Emojifix *f, int showpre)
setemoji(&l->dict, "smil", "😀 😄"); setemoji(&l->dict, "smil", "😀 😄");
setemoji(&l->dict, "smile", "😀 😄"); setemoji(&l->dict, "smile", "😀 😄");
setemoji(&l->dict, "웃음", "😀 😄 😂"); setemoji(&l->dict, "웃음", "😀 😄 😂");
setemoji(&l->dict, "えがお", "😀 😄 😊");
setemoji(&l->dict, "エガオ", "😀 😄 😊");
setemoji(&l->dict, "heart", "❤️");
setemoji(&l->dict, "coffee", "☕️");
setemoji(&l->dict, "^", "¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁽"); setemoji(&l->dict, "^", "¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁽");
setemoji(&l->dict, "^0", ""); setemoji(&l->dict, "^0", "");
setemoji(&l->dict, "_", "₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₍"); setemoji(&l->dict, "_", "₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₍");
@@ -698,6 +702,53 @@ engine_emoji_queries(struct ct *t)
emojiend(&f); emojiend(&f);
} }
void
engine_emoji_japanese_and_multirune(struct ct *t)
{
Emojifix f;
Str com;
emojibegin(&f, 0);
init();
im.l = getlang(LangJP);
sclear(&com);
CT_CHECK(t, keystroke('e', Mctrl, &com));
if(typekeys(t, "egao", &com)){
checkstr(t, "localized Hiragana query", "えがお", &search.text);
CT_EQ_INT(t, 3, im.nkouho);
CT_CHECK(t, keystroke(Kesc, 0, &com));
}
init();
im.l = getlang(LangJPK);
sclear(&com);
CT_CHECK(t, keystroke('e', Mctrl, &com));
if(typekeys(t, "egao", &com)){
checkstr(t, "localized Katakana query", "エガオ", &search.text);
CT_EQ_INT(t, 3, im.nkouho);
CT_CHECK(t, keystroke(Kesc, 0, &com));
}
init();
im.l = getlang(LangEN);
sclear(&com);
CT_CHECK(t, keystroke('e', Mctrl, &com));
if(typekeys(t, "heart", &com)){
CT_EQ_INT(t, 1, im.nkouho);
CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "variation-selector emoji", "❤️", &com);
}
sclear(&com);
CT_CHECK(t, keystroke('e', Mctrl, &com));
if(typekeys(t, "coffee", &com)){
CT_EQ_INT(t, 1, im.nkouho);
CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "multi-codepoint emoji", "☕️", &com);
}
emojiend(&f);
}
void void
engine_emoji_digit_aliases(struct ct *t) engine_emoji_digit_aliases(struct ct *t)
{ {

View File

@@ -66,6 +66,7 @@ class MkemojiTest(unittest.TestCase):
"x\t a\n", "x\t a\n",
"\0\ta\n", "\0\ta\n",
"x\ta\0b\n", "x\ta\0b\n",
"x\t;hidden\n",
"x" * 65 + "\ta\n", "x" * 65 + "\ta\n",
"x\t" + "a" * 65 + "\n", "x\t" + "a" * 65 + "\n",
] ]

View File

@@ -43,6 +43,7 @@ void engine_japanese_backspace_and_boundaries(struct ct*);
void engine_katakana_sequences(struct ct*); void engine_katakana_sequences(struct ct*);
void engine_emoji_single_candidate(struct ct*); void engine_emoji_single_candidate(struct ct*);
void engine_emoji_queries(struct ct*); void engine_emoji_queries(struct ct*);
void engine_emoji_japanese_and_multirune(struct ct*);
void engine_emoji_digit_aliases(struct ct*); void engine_emoji_digit_aliases(struct ct*);
void engine_emoji_navigation(struct ct*); void engine_emoji_navigation(struct ct*);
void engine_emoji_preedit_languages(struct ct*); void engine_emoji_preedit_languages(struct ct*);
@@ -50,6 +51,7 @@ void engine_emoji_start_and_unknown(struct ct*);
void engine_emoji_dictionary_identity(struct ct*); void engine_emoji_dictionary_identity(struct ct*);
void dictionary_candidates(struct ct*); void dictionary_candidates(struct ct*);
void dictionary_misses_clear_result(struct ct*); void dictionary_misses_clear_result(struct ct*);
void dictionary_emoji_identity(struct ct*);
void ipc_masks_modifiers(struct ct*); void ipc_masks_modifiers(struct ct*);
#endif #endif

View File

@@ -92,6 +92,7 @@ static const struct ct_test tests[] = {
{ "engine/katakana-sequences", engine_katakana_sequences }, { "engine/katakana-sequences", engine_katakana_sequences },
{ "engine/emoji-single-candidate", engine_emoji_single_candidate }, { "engine/emoji-single-candidate", engine_emoji_single_candidate },
{ "engine/emoji-queries", engine_emoji_queries }, { "engine/emoji-queries", engine_emoji_queries },
{ "engine/emoji-japanese-multirune", engine_emoji_japanese_and_multirune },
{ "engine/emoji-digit-aliases", engine_emoji_digit_aliases }, { "engine/emoji-digit-aliases", engine_emoji_digit_aliases },
{ "engine/emoji-navigation", engine_emoji_navigation }, { "engine/emoji-navigation", engine_emoji_navigation },
{ "engine/emoji-preedit-languages", engine_emoji_preedit_languages }, { "engine/emoji-preedit-languages", engine_emoji_preedit_languages },
@@ -99,6 +100,7 @@ static const struct ct_test tests[] = {
{ "engine/emoji-dictionary-identity", engine_emoji_dictionary_identity }, { "engine/emoji-dictionary-identity", engine_emoji_dictionary_identity },
{ "dict/candidates", dictionary_candidates }, { "dict/candidates", dictionary_candidates },
{ "dict/misses-clear-result", dictionary_misses_clear_result }, { "dict/misses-clear-result", dictionary_misses_clear_result },
{ "dict/emoji-identity", dictionary_emoji_identity },
{ "ipc/masks-modifiers", ipc_masks_modifiers }, { "ipc/masks-modifiers", ipc_masks_modifiers },
}; };