fix: preserve complete Japanese readings

This commit is contained in:
2026-08-12 15:28:16 +09:00
parent 5ae0945015
commit ec7e6eb992
7 changed files with 319 additions and 32 deletions

View File

@@ -2,6 +2,8 @@
#include "../strans.c"
#include "test.h"
static int typekeys(struct ct*, char*, Str*);
void
vietnamese_state_lifetime(struct ct *t)
{
@@ -73,7 +75,7 @@ engine_backspace_clears_candidates(struct ct *t)
im.sel = 0;
CT_CHECK(t, keystroke(Kback, 0, &com));
checkstr(t, "backspace commit", "", &com);
checkstr(t, "raw preedit after backspace", "n", &im.pre);
checkstr(t, "pending romaji after backspace", "n", &im.raw);
shown = shownpre(&im);
checkstr(t, "shown preedit after backspace", "", &shown);
CT_EQ_INT(t, 0, im.nkouho);
@@ -113,10 +115,11 @@ engine_commit_contract(struct ct *t)
init();
im.l = getlang(LangJP);
im.pre = mkstr("ka");
im.nkouho = 1;
im.sel = -1;
sclear(&com);
if(!typekeys(t, "ka", &com))
return;
CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "return commit", "", &com);
CT_EQ_INT(t, 0, im.pre.n);
@@ -126,12 +129,13 @@ engine_commit_contract(struct ct *t)
init();
im.l = getlang(LangJP);
im.pre = mkstr("ka");
sclear(&com);
if(!typekeys(t, "ka", &com))
return;
im.kouho[0] = mkstr("c1");
im.kouho[1] = mkstr("c2");
im.nkouho = 2;
im.sel = 1;
sclear(&com);
CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "selected candidate", "c2", &com);
CT_EQ_INT(t, 0, im.pre.n);
@@ -140,10 +144,11 @@ engine_commit_contract(struct ct *t)
init();
im.l = getlang(LangJP);
im.pre = mkstr("ka");
sclear(&com);
if(!typekeys(t, "ka", &com))
return;
im.nkouho = 1;
im.sel = -1;
sclear(&com);
CT_CHECK(t, !keystroke('q', 0, &com));
checkstr(t, "uneaten-key commit", "", &com);
CT_EQ_INT(t, 0, im.pre.n);
@@ -153,8 +158,9 @@ engine_commit_contract(struct ct *t)
init();
im.l = getlang(LangJP);
im.pre = mkstr("ka");
sclear(&com);
if(!typekeys(t, "ka", &com))
return;
CT_CHECK(t, keystroke(0xf008, 0, &com));
CT_EQ_INT(t, 2, com.n);
CT_EQ_UINT(t, 0x304b, com.r[0]);
@@ -346,6 +352,135 @@ typekeys(struct ct *t, char *keys, Str *com)
return 1;
}
void
engine_japanese_readings(struct ct *t)
{
static const struct {
char *keys;
char *want;
} cases[] = {
{ "nya", "にゃ" },
{ "n'ya", "んや" },
{ "nnya", "んにゃ" },
{ "kin'youbi", "きんようび" },
};
Str com, shown;
int i;
for(i = 0; i < nelem(cases); i++){
init();
im.l = getlang(LangJP);
sclear(&com);
if(!typekeys(t, cases[i].keys, &com))
continue;
checkstr(t, cases[i].keys, "", &com);
shown = shownpre(&im);
checkstr(t, cases[i].keys, cases[i].want, &shown);
CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "committed reading", cases[i].want, &com);
}
}
static int
newestrequest(Dictreq *req)
{
int n;
n = 0;
while(channbrecv(dictreqc, req) > 0)
n++;
return n;
}
void
engine_japanese_candidates(struct ct *t)
{
Dictreq req;
Dictres res;
Hmap *saved;
Lang *jp;
Str com, key, shown;
jp = getlang(LangJP);
saved = jp->dict;
jp->dict = hmapalloc(8);
key = mkstr("かんじ");
hmapset(&jp->dict, &key, "漢字 幹事", strlen("漢字 幹事"));
init();
im.l = jp;
sclear(&com);
if(!typekeys(t, "kanji", &com))
goto cleanup;
shown = shownpre(&im);
checkstr(t, "complete Japanese reading", "かんじ", &shown);
if(!CT_CHECK(t, newestrequest(&req) > 0))
goto cleanup;
checkstr(t, "dictionary lookup key", "かんじ", &req.key);
checkstr(t, "dictionary request identity", "かんじ", &req.pre);
dictlookup(&req, &res);
dictresult(&res);
if(!CT_EQ_INT(t, 2, im.nkouho))
goto cleanup;
checkstr(t, "candidate one", "漢字", &im.kouho[0]);
checkstr(t, "candidate two", "幹事", &im.kouho[1]);
CT_CHECK(t, keystroke(Kdown, 0, &com));
CT_EQ_INT(t, 0, im.sel);
CT_CHECK(t, keystroke(Kdown, 0, &com));
CT_EQ_INT(t, 1, im.sel);
CT_CHECK(t, keystroke(Kret, 0, &com));
checkstr(t, "selected Kanji", "幹事", &com);
cleanup:
newestrequest(&req);
hmapfree(jp->dict);
jp->dict = saved;
}
void
engine_japanese_backspace_and_boundaries(struct ct *t)
{
Str com, shown;
init();
im.l = getlang(LangJP);
sclear(&com);
if(typekeys(t, "kanji", &com)){
CT_CHECK(t, keystroke(Kback, 0, &com));
shown = shownpre(&im);
checkstr(t, "backspace pending i", "かんj", &shown);
CT_CHECK(t, keystroke(Kback, 0, &com));
shown = shownpre(&im);
checkstr(t, "backspace pending j", "かん", &shown);
CT_CHECK(t, keystroke(Kback, 0, &com));
shown = shownpre(&im);
checkstr(t, "backspace accumulated kana", "", &shown);
}
init();
im.l = getlang(LangJP);
sclear(&com);
if(typekeys(t, "kanji", &com)){
CT_CHECK(t, keystroke('k', Mctrl, &com));
checkstr(t, "Hiragana mode boundary", "かんじ", &com);
CT_EQ_INT(t, LangJPK, im.l->lang);
CT_EQ_INT(t, 0, im.pre.n);
CT_EQ_INT(t, 0, im.raw.n);
sclear(&com);
if(typekeys(t, "ka", &com)){
CT_CHECK(t, keystroke('n', Mctrl, &com));
checkstr(t, "Katakana mode boundary", "", &com);
CT_EQ_INT(t, LangJP, im.l->lang);
}
}
init();
im.l = getlang(LangJP);
sclear(&com);
if(typekeys(t, "kanji", &com)){
CT_CHECK(t, keystroke(' ', 0, &com));
checkstr(t, "real input boundary", "かんじ ", &com);
}
}
void
engine_emoji_single_candidate(struct ct *t)
{
@@ -556,7 +691,8 @@ engine_emoji_preedit_languages(struct ct *t)
sclear(&com);
if(typekeys(t, "ka", &com)){
CT_CHECK(t, !keystroke(Kmodfirst+2, 0, &com));
checkstr(t, "modifier preserves Japanese", "ka", &im.pre);
shown = shownpre(&im);
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);

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_japanese_readings(struct ct*);
void engine_japanese_candidates(struct ct*);
void engine_japanese_backspace_and_boundaries(struct ct*);
void engine_emoji_single_candidate(struct ct*);
void engine_emoji_queries(struct ct*);
void engine_emoji_digit_aliases(struct ct*);

View File

@@ -24,8 +24,18 @@ checkstr(struct ct *t, char *where, char *want, Str *got)
Str
shownpre(Im *state)
{
Str shown;
Str mapped, shown;
if(state->l->lang == LangJP || state->l->lang == LangJPK){
shown = state->pre;
if(state->raw.n == 0)
return shown;
if(mapget(state->l->map, &state->raw, &mapped))
sappend(&shown, &mapped);
else
sappend(&shown, &state->raw);
return shown;
}
if(state->l->map != nil && mapget(state->l->map, &state->pre, &shown))
return shown;
return state->pre;

View File

@@ -76,10 +76,14 @@ transmap_states(struct ct *t)
};
Emit e;
Im state;
Trie *fixture, *saved;
int i;
fixture = trieopen("data/hira.map");
memset(&state, 0, sizeof state);
state.l = getlang(LangJP);
saved = state.l->map;
state.l->map = fixture;
for(i = 0; i < nelem(cases); i++){
state.pre = mkstr(cases[i].pre);
e = transmap(&state, cases[i].key);
@@ -90,4 +94,6 @@ transmap_states(struct ct *t)
checkstr(t, "next", cases[i].next, &e.next);
checkstr(t, "mapped", cases[i].mapped, &e.dict);
}
trieclose(fixture);
state.l->map = saved;
}

View File

@@ -46,12 +46,14 @@ erealloc(void *p, ulong n)
static void
testmapinit(void)
{
Lang *jp;
Lang *jp, *kata;
jp = getlang(LangJP);
if(jp == nil)
kata = getlang(LangJPK);
if(jp == nil || kata == nil)
die("test language is not registered");
jp->map = trieopen("data/hira.map");
jp->map = trieopen("../map/hira.map");
kata->map = trieopen("../map/kata.map");
memset(&testvi, 0, sizeof testvi);
testvi.lang = LangVI;
testvi.mapname = "telex";
@@ -80,6 +82,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/japanese-readings", engine_japanese_readings },
{ "engine/japanese-candidates", engine_japanese_candidates },
{ "engine/japanese-backspace-boundaries", engine_japanese_backspace_and_boundaries },
{ "engine/emoji-single-candidate", engine_emoji_single_candidate },
{ "engine/emoji-queries", engine_emoji_queries },
{ "engine/emoji-digit-aliases", engine_emoji_digit_aliases },