fix: normalize Korean shifted input

This commit is contained in:
2026-08-12 15:32:28 +09:00
parent 6dfbc08704
commit f0ba7fc5f6
5 changed files with 148 additions and 18 deletions

View File

@@ -159,6 +159,8 @@ dictqmap(Im *im)
clearkouho();
show();
if(im->l->map == nil)
return;
if(!mapget(im->l->map, &im->pre, &dict))
return;
dictsend(im, &dict);
@@ -374,6 +376,16 @@ ctrlkey(Rune c)
return c;
}
static Rune
kokey(Rune c, u32int mod)
{
if(c >= 'A' && c <= 'Z')
c += 'a' - 'A';
if((mod & Mshift) && c >= 'a' && c <= 'z')
c -= 'a' - 'A';
return c;
}
static int
ismodkey(u32int ks)
{
@@ -694,6 +706,8 @@ keystroke(u32int ks, u32int mod, Str *com)
reset();
return 1;
}
if(im.l->lang == LangKO)
ks = kokey(ks, mod);
n = dotrans(ks, com);
show();
return n;

View File

@@ -189,6 +189,63 @@ engine_telex_history_bound(struct ct *t)
CT_EQ_INT(t, 0, im.raw.n);
}
void
engine_korean_modifiers_and_backspace(struct ct *t)
{
static const struct {
Rune key;
char *plain;
char *shifted;
} cases[] = {
{ 'r', "", "" }, { 'e', "", "" },
{ 'q', "", "" }, { 't', "", "" },
{ 'w', "", "" }, { 'o', "", "" },
{ 'p', "", "" },
};
Str com;
int i;
for(i = 0; i < nelem(cases); i++){
init();
im.l = getlang(LangKO);
sclear(&com);
CT_CHECK(t, keystroke(cases[i].key, 0, &com));
checkstr(t, "plain Korean key", cases[i].plain, &im.pre);
init();
im.l = getlang(LangKO);
sclear(&com);
CT_CHECK(t, keystroke(cases[i].key, Mshift, &com));
checkstr(t, "shifted Korean key", cases[i].shifted, &im.pre);
init();
im.l = getlang(LangKO);
sclear(&com);
CT_CHECK(t, keystroke(cases[i].key - 'a' + 'A', 0, &com));
checkstr(t, "Caps Lock Korean key", cases[i].plain, &im.pre);
init();
im.l = getlang(LangKO);
sclear(&com);
CT_CHECK(t, keystroke(cases[i].key, Mshift, &com));
checkstr(t, "Shift+Caps Korean key", cases[i].shifted, &im.pre);
}
init();
im.l = getlang(LangKO);
sclear(&com);
if(typekeys(t, "rkr", &com)){
checkstr(t, "Korean engine preedit", "", &im.pre);
CT_CHECK(t, keystroke(Kback, 0, &com));
checkstr(t, "Korean final backspace", "", &im.pre);
CT_CHECK(t, keystroke(Kback, 0, &com));
checkstr(t, "Korean vowel backspace", "", &im.pre);
CT_CHECK(t, keystroke(Kback, 0, &com));
CT_EQ_INT(t, 0, im.pre.n);
CT_CHECK(t, !keystroke(Kback, 0, &com));
}
}
void
engine_language_switch_state(struct ct *t)
{

View File

@@ -1,5 +1,27 @@
#include "test.h"
static Str
kotrans(char *keys)
{
Emit e;
Im state;
Str out;
char *p;
memset(&state, 0, sizeof state);
state.l = getlang(LangKO);
sclear(&out);
for(p = keys; *p != '\0'; p++){
e = transko(&state, (uchar)*p);
sappend(&out, &e.s);
state.pre = e.next;
if(!e.eat)
sputr(&out, (uchar)*p);
}
sappend(&out, &state.pre);
return out;
}
void
korean_sequences(struct ct *t)
{
@@ -14,39 +36,70 @@ korean_sequences(struct ct *t)
{ "Rk", "" },
{ "rk1", "가1" },
};
Emit e;
Im state;
Str out;
Rune key;
char *p;
int i;
for(i = 0; i < nelem(cases); i++){
memset(&state, 0, sizeof state);
state.l = getlang(LangKO);
sclear(&out);
for(p = cases[i].keys; *p != '\0'; p++){
key = (uchar)*p;
e = transko(&state, key);
sappend(&out, &e.s);
state.pre = e.next;
if(!e.eat)
sputr(&out, key);
}
sappend(&out, &state.pre);
out = kotrans(cases[i].keys);
checkstr(t, cases[i].keys, cases[i].want, &out);
}
}
void
korean_compound_vowels(struct ct *t)
{
static const struct { char *keys, *want; } cases[] = {
{ "hk", "" }, { "ho", "" }, { "hl", "" },
{ "nj", "" }, { "np", "" }, { "nl", "" },
{ "ml", "" },
};
Str out;
int i;
for(i = 0; i < nelem(cases); i++){
out = kotrans(cases[i].keys);
checkstr(t, cases[i].keys, cases[i].want, &out);
}
}
void
korean_compound_finals(struct ct *t)
{
static const struct { char *keys, *want, *split; } cases[] = {
{ "rkrt", "", "각사" }, { "rksw", "", "간자" },
{ "rksg", "", "간하" }, { "rkfr", "", "갈가" },
{ "rkfa", "", "갈마" }, { "rkfq", "", "갈바" },
{ "rkft", "", "갈사" }, { "rkfx", "", "갈타" },
{ "rkfv", "", "갈파" }, { "rkfg", "", "갈하" },
{ "rkqt", "", "갑사" },
};
char keys[16];
Str out;
int i;
for(i = 0; i < nelem(cases); i++){
out = kotrans(cases[i].keys);
checkstr(t, cases[i].keys, cases[i].want, &out);
snprint(keys, sizeof keys, "%sk", cases[i].keys);
out = kotrans(keys);
checkstr(t, keys, cases[i].split, &out);
}
}
void
korean_backspace(struct ct *t)
{
static const struct { char *before, *after; } cases[] = {
{ "", "" },
{ "", "" },
{ "", "" },
{ "", "" }, { "", "" }, { "", "" },
{ "", "" }, { "", "" }, { "", "" },
{ "", "" },
{ "", "" },
{ "", "" },
{ "", "" }, { "", "" }, { "", "" },
{ "", "" }, { "", "" }, { "", "" },
{ "", "" }, { "", "" }, { "", "" },
{ "", "" }, { "", "" },
};
Im state;
int i;

View File

@@ -23,6 +23,8 @@ void trie_exact_prefix_and_duplicate(struct ct*);
void production_maps_load(struct ct*);
void transmap_states(struct ct*);
void korean_sequences(struct ct*);
void korean_compound_vowels(struct ct*);
void korean_compound_finals(struct ct*);
void korean_backspace(struct ct*);
void vietnamese_transitions(struct ct*);
void vietnamese_backspace(struct ct*);
@@ -33,6 +35,7 @@ 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_korean_modifiers_and_backspace(struct ct*);
void engine_japanese_readings(struct ct*);
void engine_japanese_candidates(struct ct*);
void engine_japanese_backspace_and_boundaries(struct ct*);

View File

@@ -72,6 +72,8 @@ static const struct ct_test tests[] = {
{ "map/production-lifecycle", production_maps_load },
{ "transmap/states", transmap_states },
{ "hangul/sequences", korean_sequences },
{ "hangul/compound-vowels", korean_compound_vowels },
{ "hangul/compound-finals", korean_compound_finals },
{ "hangul/backspace", korean_backspace },
{ "telex/transitions", vietnamese_transitions },
{ "telex/backspace", vietnamese_backspace },
@@ -82,6 +84,7 @@ 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/korean-modifiers-backspace", engine_korean_modifiers_and_backspace },
{ "engine/japanese-readings", engine_japanese_readings },
{ "engine/japanese-candidates", engine_japanese_candidates },
{ "engine/japanese-backspace-boundaries", engine_japanese_backspace_and_boundaries },