diff --git a/strans.c b/strans.c index a137409..c1c1e39 100644 --- a/strans.c +++ b/strans.c @@ -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; diff --git a/tests/engine_test.c b/tests/engine_test.c index e75306b..390184e 100644 --- a/tests/engine_test.c +++ b/tests/engine_test.c @@ -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) { diff --git a/tests/ko_test.c b/tests/ko_test.c index 405d60a..0c42c57 100644 --- a/tests/ko_test.c +++ b/tests/ko_test.c @@ -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; diff --git a/tests/test.h b/tests/test.h index 00832f5..7641cac 100644 --- a/tests/test.h +++ b/tests/test.h @@ -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*); diff --git a/tests/unit_test.c b/tests/unit_test.c index 97662db..667e509 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -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 },