engine: one trans contract for every language
Japanese was never dispatched through the Lang table: dotrans and transstr both special-cased it into transjp, which edited Im in place and wrote commits through a side argument, while transko popped and cleared a pre that every caller overwrote anyway, and Vietnamese kept its key history in dotrans. Emit now carries the new raw state next to the new pre; transjp, transko and transvi are pure functions of (Im, key), and dotrans and transstr have a single path. Vietnamese history bookkeeping lives in vi.c, where the full-history flush no longer resets the caret as a side effect; istone was toneidx() >= 0.
This commit is contained in:
6
dat.h
6
dat.h
@@ -48,12 +48,18 @@ struct Str
|
||||
int n;
|
||||
};
|
||||
|
||||
/*
|
||||
* What one key does to a composition: text to commit, the new pending
|
||||
* text, and the new raw state (Telex key history, or pending Japanese
|
||||
* romaji), given the old ones in Im.
|
||||
*/
|
||||
typedef struct Emit Emit;
|
||||
struct Emit
|
||||
{
|
||||
int eat;
|
||||
Str s;
|
||||
Str next;
|
||||
Str raw;
|
||||
};
|
||||
|
||||
typedef struct Tnode Tnode;
|
||||
|
||||
14
ko.c
14
ko.c
@@ -189,6 +189,10 @@ issyl(Rune r)
|
||||
return r >= Sbase && r < Sbase + Ncho*Njung*Njong;
|
||||
}
|
||||
|
||||
/*
|
||||
* The pending text is at most one syllable; a key either extends it,
|
||||
* commits it and starts another, or commits it and passes through.
|
||||
*/
|
||||
Emit
|
||||
transko(Im *im, Rune c)
|
||||
{
|
||||
@@ -199,10 +203,7 @@ transko(Im *im, Rune c)
|
||||
memset(&e, 0, sizeof e);
|
||||
jm = keytojamo(c);
|
||||
if(jm == 0){
|
||||
if(im->pre.n > 0){
|
||||
e.s = im->pre;
|
||||
sclear(&im->pre);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -217,14 +218,12 @@ transko(Im *im, Rune c)
|
||||
ci = Choidx(last);
|
||||
ji = Jungidx(jm);
|
||||
if(ci >= 0 && ji >= 0){
|
||||
spopr(&im->pre);
|
||||
sputr(&e.next, compose(ci, ji, 0));
|
||||
return e;
|
||||
}
|
||||
if(Jungidx(last) >= 0 && ji >= 0){
|
||||
comb = combine(cvow, nelem(cvow), last, jm);
|
||||
if(comb){
|
||||
spopr(&im->pre);
|
||||
sputr(&e.next, comb);
|
||||
return e;
|
||||
}
|
||||
@@ -239,7 +238,6 @@ transko(Im *im, Rune c)
|
||||
if(joi == 0){
|
||||
ni = Jongidx(jm);
|
||||
if(ni > 0){
|
||||
spopr(&im->pre);
|
||||
sputr(&e.next, compose(ci, ji, ni));
|
||||
return e;
|
||||
}
|
||||
@@ -248,7 +246,6 @@ transko(Im *im, Rune c)
|
||||
comb = combine(cvow, nelem(cvow), jung[ji], jm);
|
||||
if(comb){
|
||||
ni = Jungidx(comb);
|
||||
spopr(&im->pre);
|
||||
sputr(&e.next, compose(ci, ni, 0));
|
||||
return e;
|
||||
}
|
||||
@@ -260,7 +257,6 @@ transko(Im *im, Rune c)
|
||||
if(comb){
|
||||
ni = Jongidx(comb);
|
||||
if(ni > 0){
|
||||
spopr(&im->pre);
|
||||
sputr(&e.next, compose(ci, ji, ni));
|
||||
return e;
|
||||
}
|
||||
@@ -271,14 +267,12 @@ transko(Im *im, Rune c)
|
||||
if(splitpair(cjong, nelem(cjong), jc, &stay, &next)){
|
||||
si = Jongidx(stay);
|
||||
ni = Choidx(next);
|
||||
spopr(&im->pre);
|
||||
sputr(&e.s, compose(ci, ji, si));
|
||||
sputr(&e.next, compose(ni, vi, 0));
|
||||
return e;
|
||||
}
|
||||
ni = Choidx(jc);
|
||||
if(ni >= 0){
|
||||
spopr(&im->pre);
|
||||
sputr(&e.s, compose(ci, ji, 0));
|
||||
sputr(&e.next, compose(ni, vi, 0));
|
||||
return e;
|
||||
|
||||
136
strans.c
136
strans.c
@@ -7,6 +7,7 @@ static void *activeowner;
|
||||
static int activecap;
|
||||
static Caret caret;
|
||||
static int candidatechosen;
|
||||
static Emit transjp(Im*, Rune);
|
||||
static void backjp(Im*);
|
||||
static int maplookup(Trie*, Str*, Str*);
|
||||
|
||||
@@ -21,8 +22,8 @@ static Search search;
|
||||
|
||||
Lang langs[] = {
|
||||
{LangEN, nil, nil, nil, nil, nil, nil},
|
||||
{LangJP, "hira", "kanji", transmap, backjp, nil, nil},
|
||||
{LangJPK, "kata", nil, transmap, backjp, nil, nil},
|
||||
{LangJP, "hira", "kanji", transjp, backjp, nil, nil},
|
||||
{LangJPK, "kata", nil, transjp, backjp, nil, nil},
|
||||
{LangKO, nil, nil, transko, backko, nil, nil},
|
||||
{LangHANJA, nil, "hanja", nil, nil, nil, nil},
|
||||
{LangEMOJI, nil, "emoji", nil, nil, nil, nil},
|
||||
@@ -106,28 +107,29 @@ isjp(Im *p)
|
||||
/*
|
||||
* Japanese keeps completed kana in pre and an ambiguous romaji syllable in
|
||||
* raw. A mapped raw suffix is part of the reading even while it remains
|
||||
* pending (notably n, which may still grow into nya).
|
||||
* pending (notably n, which may still grow into nya). Returns whether
|
||||
* the reading is complete.
|
||||
*/
|
||||
static int
|
||||
jpreading(Im *p, Str *s)
|
||||
jpreading(Trie *map, Str *pre, Str *raw, Str *s)
|
||||
{
|
||||
Str mapped;
|
||||
|
||||
*s = p->pre;
|
||||
if(p->raw.n == 0)
|
||||
*s = *pre;
|
||||
if(raw->n == 0)
|
||||
return 1;
|
||||
if(mapget(p->l->map, &p->raw, &mapped)){
|
||||
if(mapget(map, raw, &mapped)){
|
||||
sappend(s, &mapped);
|
||||
return 1;
|
||||
}
|
||||
sappend(s, &p->raw);
|
||||
sappend(s, raw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
haspre(Im *p)
|
||||
{
|
||||
return p->pre.n != 0 || (isjp(p) && p->raw.n != 0);
|
||||
return p->pre.n != 0 || p->raw.n != 0;
|
||||
}
|
||||
|
||||
/* The preedit as clients see it: Telex keys read back through the map. */
|
||||
@@ -135,7 +137,7 @@ static void
|
||||
impre(Str *s)
|
||||
{
|
||||
if(isjp(&im))
|
||||
jpreading(&im, s);
|
||||
jpreading(im.l->map, &im.pre, &im.raw, s);
|
||||
else if(!mapget(im.l->map, &im.pre, s))
|
||||
*s = im.pre;
|
||||
}
|
||||
@@ -200,7 +202,7 @@ dictqjp(void)
|
||||
Str reading;
|
||||
|
||||
clearkouho();
|
||||
if(!jpreading(&im, &reading) || reading.n == 0)
|
||||
if(!jpreading(im.l->map, &im.pre, &im.raw, &reading) || reading.n == 0)
|
||||
return;
|
||||
im.nkouho = dictlookup(im.l, &reading, im.kouho, Maxkouho);
|
||||
selectfirst();
|
||||
@@ -223,13 +225,11 @@ commitim(Im *p, Str *com)
|
||||
{
|
||||
Str val;
|
||||
|
||||
if(isjp(p)){
|
||||
jpreading(p, &val);
|
||||
if(isjp(p))
|
||||
jpreading(p->l->map, &p->pre, &p->raw, &val);
|
||||
else if(!mapget(p->l->map, &p->pre, &val))
|
||||
val = p->pre;
|
||||
sappend(com, &val);
|
||||
}else if(p->l->map != nil && mapget(p->l->map, &p->pre, &val))
|
||||
sappend(com, &val);
|
||||
else
|
||||
sappend(com, &p->pre);
|
||||
sclear(&p->pre);
|
||||
sclear(&p->raw);
|
||||
}
|
||||
@@ -266,79 +266,65 @@ keeptail(Str *s)
|
||||
s->n--;
|
||||
}
|
||||
|
||||
static int
|
||||
transjp(Im *p, Rune c, Str *com)
|
||||
static Emit
|
||||
transjp(Im *p, Rune c)
|
||||
{
|
||||
Emit e;
|
||||
Str key, mapped;
|
||||
|
||||
if(c == '\'' && p->raw.n == 1 && p->raw.r[0] == 'n'){
|
||||
if(mapget(p->l->map, &p->raw, &mapped))
|
||||
sappend(&p->pre, &mapped);
|
||||
sclear(&p->raw);
|
||||
return 1;
|
||||
memset(&e, 0, sizeof e);
|
||||
e.eat = 1;
|
||||
e.next = p->pre;
|
||||
e.raw = p->raw;
|
||||
if(c == '\'' && e.raw.n == 1 && e.raw.r[0] == 'n'){
|
||||
if(mapget(p->l->map, &e.raw, &mapped))
|
||||
sappend(&e.next, &mapped);
|
||||
sclear(&e.raw);
|
||||
return e;
|
||||
}
|
||||
key = p->raw;
|
||||
if(key.n < Maxrunes)
|
||||
key = e.raw;
|
||||
sputr(&key, c);
|
||||
sclear(&mapped);
|
||||
if(maplookup(p->l->map, &key, &mapped)){
|
||||
p->raw = key;
|
||||
e.raw = key;
|
||||
if(issmalltsu(&mapped)){
|
||||
sappend(&p->pre, &mapped);
|
||||
keeptail(&p->raw);
|
||||
sappend(&e.next, &mapped);
|
||||
keeptail(&e.raw);
|
||||
}
|
||||
return 1;
|
||||
return e;
|
||||
}
|
||||
if(p->raw.n != 0 && mapget(p->l->map, &p->raw, &mapped)){
|
||||
sappend(&p->pre, &mapped);
|
||||
sclear(&p->raw);
|
||||
if(e.raw.n != 0 && mapget(p->l->map, &e.raw, &mapped)){
|
||||
sappend(&e.next, &mapped);
|
||||
sclear(&e.raw);
|
||||
sclear(&key);
|
||||
sputr(&key, c);
|
||||
if(maplookup(p->l->map, &key, &mapped)){
|
||||
p->raw = key;
|
||||
return 1;
|
||||
e.raw = key;
|
||||
return e;
|
||||
}
|
||||
}
|
||||
commitim(p, com);
|
||||
return 0;
|
||||
/* The key starts no syllable: flush the reading and pass it on. */
|
||||
jpreading(p->l->map, &e.next, &e.raw, &e.s);
|
||||
sclear(&e.next);
|
||||
sclear(&e.raw);
|
||||
e.eat = 0;
|
||||
return e;
|
||||
}
|
||||
|
||||
static int
|
||||
dotrans(Rune c, Str *com)
|
||||
{
|
||||
Emit e;
|
||||
Str mapped;
|
||||
|
||||
if(isjp(&im)){
|
||||
e.eat = transjp(&im, c, com);
|
||||
if(e.eat)
|
||||
e = im.l->trans(&im, c);
|
||||
sappend(com, &e.s);
|
||||
im.pre = e.next;
|
||||
im.raw = e.raw;
|
||||
if(e.eat && isjp(&im))
|
||||
dictqjp();
|
||||
else
|
||||
clearkouho();
|
||||
return e.eat;
|
||||
}
|
||||
e = im.l->trans(&im, c);
|
||||
if(im.l->lang == LangVI){
|
||||
if(e.s.n > 0 || !e.eat)
|
||||
sclear(&im.raw);
|
||||
if(e.eat && e.next.n > 0){
|
||||
if(im.raw.n >= Maxrunes){
|
||||
if(!mapget(im.l->map, &e.next, &mapped))
|
||||
mapped = e.next;
|
||||
sappend(com, &mapped);
|
||||
reset();
|
||||
return e.eat;
|
||||
}
|
||||
sputr(&im.raw, c);
|
||||
}
|
||||
}
|
||||
clearkouho();
|
||||
if(e.s.n > 0)
|
||||
sappend(com, &e.s);
|
||||
sclear(&im.pre);
|
||||
sappend(&im.pre, &e.next);
|
||||
return e.eat;
|
||||
}
|
||||
|
||||
Lang*
|
||||
getlang(int lang)
|
||||
@@ -430,42 +416,30 @@ foldascii(Str *s)
|
||||
s->r[i] += 'a' - 'A';
|
||||
}
|
||||
|
||||
/* What typing raw in language l would produce, composition included. */
|
||||
static void
|
||||
transstr(Lang *l, Str *raw, Str *out)
|
||||
{
|
||||
Im q;
|
||||
Emit e;
|
||||
Str mapped;
|
||||
int i;
|
||||
|
||||
sclear(out);
|
||||
if(l == nil || l->trans == nil || (l->trans == transmap && l->map == nil)){
|
||||
if(l->trans == nil){
|
||||
*out = *raw;
|
||||
return;
|
||||
}
|
||||
memset(&q, 0, sizeof q);
|
||||
q.l = l;
|
||||
if(isjp(&q)){
|
||||
for(i = 0; i < raw->n; i++)
|
||||
if(!transjp(&q, raw->r[i], out) && out->n < Maxrunes)
|
||||
sputr(out, raw->r[i]);
|
||||
if(haspre(&q))
|
||||
commitim(&q, out);
|
||||
return;
|
||||
}
|
||||
for(i = 0; i < raw->n; i++){
|
||||
e = l->trans(&q, raw->r[i]);
|
||||
sappend(out, &e.s);
|
||||
q.pre = e.next;
|
||||
if(!e.eat && out->n < Maxrunes)
|
||||
q.raw = e.raw;
|
||||
if(!e.eat)
|
||||
sputr(out, raw->r[i]);
|
||||
}
|
||||
if(q.pre.n == 0)
|
||||
return;
|
||||
if(l->map != nil && mapget(l->map, &q.pre, &mapped))
|
||||
sappend(out, &mapped);
|
||||
else
|
||||
sappend(out, &q.pre);
|
||||
commitim(&q, out);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -15,6 +15,7 @@ typevi(struct ct *t, char *keys, char *want)
|
||||
e = transvi(&state, (uchar)*p);
|
||||
sappend(&out, &e.s);
|
||||
state.pre = e.next;
|
||||
state.raw = e.raw;
|
||||
if(!e.eat)
|
||||
sputr(&out, (uchar)*p);
|
||||
}
|
||||
|
||||
69
vi.c
69
vi.c
@@ -31,12 +31,6 @@ static struct {
|
||||
{L'Y', {L'Ý', L'Ỳ', L'Ỷ', L'Ỹ', L'Ỵ'}},
|
||||
};
|
||||
|
||||
static int
|
||||
istone(Rune c)
|
||||
{
|
||||
return c == 's' || c == 'f' || c == 'r' || c == 'x' || c == 'j';
|
||||
}
|
||||
|
||||
static int
|
||||
toneidx(Rune c)
|
||||
{
|
||||
@@ -114,20 +108,44 @@ onsetglide(Str *m)
|
||||
return -1;
|
||||
}
|
||||
|
||||
Emit
|
||||
transvi(Im *im, Rune c)
|
||||
/*
|
||||
* The pending text is the key sequence itself, read through the map for
|
||||
* display; raw keeps every key of it so Backspace can replay one fewer.
|
||||
*/
|
||||
static Emit
|
||||
history(Im *im, Rune c, Emit e)
|
||||
{
|
||||
Emit e, mappedkey;
|
||||
Str mapped;
|
||||
|
||||
e.raw = im->raw;
|
||||
if(e.s.n > 0 || !e.eat)
|
||||
sclear(&e.raw);
|
||||
if(!e.eat || e.next.n == 0)
|
||||
return e;
|
||||
if(e.raw.n < Maxrunes){
|
||||
sputr(&e.raw, c);
|
||||
return e;
|
||||
}
|
||||
/* The history is full: flush the composed text as it stands. */
|
||||
if(!mapget(im->l->map, &e.next, &mapped))
|
||||
mapped = e.next;
|
||||
sappend(&e.s, &mapped);
|
||||
sclear(&e.next);
|
||||
sclear(&e.raw);
|
||||
return e;
|
||||
}
|
||||
|
||||
static Emit
|
||||
tone(Im *im, Rune c)
|
||||
{
|
||||
Emit e;
|
||||
Str mapped, pre;
|
||||
int i, tidx, vi, last, penult, glide;
|
||||
Rune v, b1, b2;
|
||||
|
||||
if(!istone(c) && c != 'z')
|
||||
return transmap(im, c);
|
||||
mappedkey = transmap(im, c);
|
||||
if(mappedkey.eat)
|
||||
return mappedkey;
|
||||
|
||||
e = transmap(im, c);
|
||||
if(e.eat)
|
||||
return e;
|
||||
memset(&e, 0, sizeof e);
|
||||
if(im->pre.n == 0)
|
||||
return e;
|
||||
@@ -175,17 +193,24 @@ transvi(Im *im, Rune c)
|
||||
return e;
|
||||
}
|
||||
|
||||
if(c == 'z')
|
||||
mapped.r[vi] = removetone(mapped.r[vi]);
|
||||
else{
|
||||
tidx = toneidx(c);
|
||||
if(tidx < 0)
|
||||
mapped.r[vi] = removetone(mapped.r[vi]);
|
||||
else
|
||||
mapped.r[vi] = applytone(mapped.r[vi], tidx);
|
||||
}
|
||||
e.eat = 1;
|
||||
e.next = mapped;
|
||||
return e;
|
||||
}
|
||||
|
||||
Emit
|
||||
transvi(Im *im, Rune c)
|
||||
{
|
||||
if(toneidx(c) < 0 && c != 'z')
|
||||
return history(im, c, transmap(im, c));
|
||||
return history(im, c, tone(im, c));
|
||||
}
|
||||
|
||||
void
|
||||
backvi(Im *im)
|
||||
{
|
||||
@@ -200,10 +225,10 @@ backvi(Im *im)
|
||||
raw = im->raw;
|
||||
spopr(&raw);
|
||||
sclear(&im->pre);
|
||||
sclear(&im->raw);
|
||||
for(i = 0; i < raw.n; i++){
|
||||
e = transvi(im, raw.r[i]);
|
||||
sclear(&im->pre);
|
||||
sappend(&im->pre, &e.next);
|
||||
im->pre = e.next;
|
||||
im->raw = e.raw;
|
||||
}
|
||||
im->raw = raw;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user