2 using System.Collections;
3 using System.Collections.Generic;
11 public class MInputMethod
13 internal static MExpression.Domain domain = new MExpression.Domain (null);
14 internal MExpression.Domain local_domain;
16 private static MSymbol Minput_method = MSymbol.Of ("input-method");
17 private static MSymbol Mdescription = MSymbol.Of ("description");
18 private static MSymbol Mvariable = MSymbol.Of ("variable");
19 private static MSymbol Mcommand = MSymbol.Of ("command");
20 private static MSymbol Mtitle = MSymbol.Of ("title");
21 private static MSymbol Minclude = MSymbol.Of ("include");
22 private static MSymbol Mmacro = MSymbol.Of ("macro");
23 private static MSymbol Mmap = MSymbol.Of ("map");
24 private static MSymbol Mstate = MSymbol.Of ("state");
25 internal static MSymbol Mcandidates = MSymbol.Of ("candidates");
26 internal static MSymbol Mcandidates_group_size = MSymbol.Of ("candidates-group-size");
27 private static MSymbol Mat_less_than = MSymbol.Of ("@<");
28 private static MSymbol Mat_greater_than = MSymbol.Of ("@>");
29 private static MSymbol Mat_minus = MSymbol.Of ("@-");
30 private static MSymbol Mat_plus = MSymbol.Of ("@+");
31 private static MSymbol Mat_open_square_bracket = MSymbol.Of ("@[");
32 private static MSymbol Mat_close_square_bracket = MSymbol.Of ("@]");
33 private static MSymbol Minsert = MSymbol.Of ("insert");
34 private static MSymbol Mdelete = MSymbol.Of ("delete");
35 private static MSymbol Mmove = MSymbol.Of ("move");
36 private static MSymbol Mmark = MSymbol.Of ("mark");
37 private static MSymbol Mmarker = MSymbol.Of ("marker");
38 private static MSymbol Madd = MSymbol.Of ("add");
39 private static MSymbol Msub = MSymbol.Of ("sub");
40 private static MSymbol Mmul = MSymbol.Of ("mul");
41 private static MSymbol Mdiv = MSymbol.Of ("div");
42 private static MSymbol Mif = MSymbol.Of ("if");
43 private static MSymbol Mcond = MSymbol.Of ("cond");
44 private static MSymbol Mchar_at = MSymbol.Of ("char-at");
46 internal class Variable
49 public MText description;
52 public MPlist candidates;
55 internal class Command
58 public MText description;
59 public MSymbol[][] keys;
64 public MSymbol[] keys;
66 private static MSymbol char_to_symbol (int c)
68 return MSymbol.Of (String.Format ("#{0:X}", c));
71 public KeySeq (MPlist plist)
73 keys = new MSymbol[plist.Count];
75 foreach (MPlist p in plist)
80 keys[i++] = char_to_symbol (p.Integer);
86 public KeySeq (MText mt)
88 keys = new MSymbol[mt.Length];
89 for (int i = 0; i < mt.Length; i++)
90 keys[i] = char_to_symbol (mt[i]);
93 public MSymbol this[int i] { get { return keys[i]; } }
95 public int Length { get { return keys.Length; } }
101 public Dictionary<MSymbol, Map> submaps;
102 public MExpression actions;
104 public void Add (KeySeq keys, int index, MExpression actions)
109 submaps = new Dictionary<MSymbol, Map> ();
111 submaps.TryGetValue (keys[index], out sub);
114 MSymbol sym = keys[index];
115 submaps[sym] = sub = new Map ();
117 if (index + 1 < keys.Length)
118 sub.Add (keys, index + 1, actions);
120 this.actions = actions;
123 public MExpression Lookup (KeySeq keys, int index)
127 if (index + 1 == keys.Length)
129 if (submaps.TryGetValue (keys[index], out sub))
130 return sub.Lookup (keys, index + 1);
139 public Dictionary<MSymbol, MExpression> branches
140 = new Dictionary<MSymbol, MExpression> ();
143 private static Dictionary<MDatabase.Tag, MInputMethod> im_table
144 = new Dictionary<MDatabase.Tag, MInputMethod> ();
146 public readonly MSymbol language;
147 public readonly MSymbol name;
148 public readonly MSymbol subname;
150 internal MDatabase mdb;
151 internal MText description;
152 internal MText title;
153 internal Command[] commands;
154 internal Variable[] variables;
155 internal MPlist bindings;
156 internal Dictionary<MSymbol, Map> maps
157 = new Dictionary<MSymbol, Map> ();
158 internal State init_state;
159 internal Dictionary<MSymbol, State> states
160 = new Dictionary<MSymbol, State> ();
161 internal MPlist externals;
163 static MInputMethod ()
165 domain.Defun ("insert", insert, 1, 1);
166 domain.Defun ("candidates", insert_candidates, 1, 1);
167 domain.Defun ("delete", delete, 1, 1);
168 domain.Defun ("select", select, 1, 1);
169 domain.Defun ("show", show, 0, 0);
170 domain.Defun ("hide", hide, 0, 0);
171 domain.Defun ("move", move, 1, 1);
172 domain.Defun ("mark", mark, 1, 1);
173 domain.Defun ("pushback", pushback, 1, 1);
174 domain.Defun ("pop", pop, 0, 0);
175 domain.Defun ("undo", undo, 0, 1);
176 domain.Defun ("commit", commit, 0, 0);
177 domain.Defun ("unhandle", unhandle, 0, 0);
178 domain.Defun ("shift", shift, 1, 1);
179 domain.Defun ("call", call, 2, -1);
180 domain.Defun ("marker", marker, 1, 1, true);
181 domain.Defun ("char-at", char_at, 1, 1, true);
184 private MInputMethod (MDatabase.Tag tag)
186 mdb = MDatabase.Find (tag);
188 throw new Exception (String.Format ("Input method {0} not available",
193 local_domain = new MExpression.Domain (domain, null);
195 MPlist plist = (MPlist) mdb.Load ();
198 for (; ! plist.IsEmpty; plist = plist.next)
201 MPlist pl = plist.Plist;
204 MSymbol sym = pl.Symbol;
206 if (sym == Mdescription)
208 description = parse_description (pl);
209 if (description == null)
210 description = new MText ("No description");
212 else if (sym == Mtitle)
217 else if (sym == Mvariable)
218 parse_variables (pl);
219 else if (sym == Mcommand)
221 else if (sym == Minclude)
223 else if (sym == Mmacro)
225 else if (sym == Mmap)
227 else if (sym == Mstate)
233 private static void transform (MPlist plist)
235 for (; ! plist.IsEmpty; plist = plist.next)
239 MPlist p = new MPlist ();
240 p.Add (MSymbol.symbol, Minsert);
241 p.Add (MSymbol.mtext, plist.Text);
242 plist.Set (MSymbol.plist, p);
244 else if (plist.IsInteger)
246 MPlist p = new MPlist ();
247 p.Add (MSymbol.symbol, Minsert);
248 p.Add (MSymbol.integer, plist.Integer);
249 plist.Set (MSymbol.plist, p);
251 else if (plist.IsPlist)
253 MPlist pl = plist.Plist;
257 if (pl.Symbol == Madd)
258 pl.Set (MSymbol.symbol, MSymbol.Of ("+="));
259 else if (pl.Symbol == Msub)
260 pl.Set (MSymbol.symbol, MSymbol.Of ("-="));
261 else if (pl.Symbol == Mmul)
262 pl.Set (MSymbol.symbol, MSymbol.Of ("*="));
263 else if (pl.Symbol == Mdiv)
264 pl.Set (MSymbol.symbol, MSymbol.Of ("/="));
265 else if (pl.Symbol == Minsert)
267 // (insert (CANDIDATES ...))
268 // => (candidates CANDIDATES ...)
271 pl.Set (MSymbol.symbol, Mcandidates);
274 pl.Set (p.key, p.val);
275 for (p = p.next; ! p.IsEmpty; p = p.next);
276 pl.Add (p.key, p.val);
279 else if (pl.Symbol == Mif)
285 else if (pl.Symbol == Mcond)
287 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
298 else if (pl.Symbol == Mdelete
299 || pl.Symbol == Mmove
300 || pl.Symbol == Mmark)
305 MSymbol sym = pl.Symbol;
306 MPlist p = new MPlist ();
307 p.Add (MSymbol.symbol, Mmarker);
308 p.Add (MSymbol.symbol, sym);
309 pl.Set (MSymbol.plist, p);
312 else if (pl.Symbol == Mstate)
317 MSymbol sym = pl.Symbol;
318 MPlist p = new MPlist ();
319 p.Add (MSymbol.symbol, Mstate);
320 p.Add (MSymbol.symbol, sym);
321 pl.Set (MSymbol.plist, p);
327 // (CANDIDATES ...) => (candidates CANDIDATES ...)
328 pl.Push (MSymbol.symbol, Mcandidates);
331 else if (plist.IsSymbol)
333 MSymbol sym = plist.Symbol;
335 if (sym.Name[0] == '@')
337 MPlist p = new MPlist ();
338 p.Add (MSymbol.symbol, Mchar_at);
339 p.Add (MSymbol.symbol, sym);
340 plist.Set (MSymbol.plist, p);
346 private static MText parse_description (MPlist plist)
348 return (plist.IsMText ? plist.Text
349 : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
353 private void parse_variables (MPlist plist)
355 variables = new Variable[plist.Count];
357 for (int i = 0; ! plist.IsEmpty; plist = plist.next)
358 if (plist.IsPlist && plist.Plist.IsSymbol)
360 Variable var = new Variable ();
361 MPlist p = plist.Plist;
365 var.description = parse_description (p);
366 if (var.description == null)
367 var.description = new MText ("No description");
370 var.type = (p.IsMText ? typeof (MText)
371 : p.IsInteger ? typeof (int)
372 : p.IsSymbol ? typeof (MSymbol)
375 var.candidates = p.next;
376 variables[i++] = var;
380 private void parse_commands (MPlist plist)
382 commands = new Command[plist.Count];
384 for (int i = 0; ! plist.IsEmpty; plist = plist.next)
385 if (plist.IsPlist && plist.Plist.IsSymbol)
387 Command cmd = new Command ();
388 MPlist p = plist.Plist;
392 cmd.description = parse_description (p);
393 if (cmd.description == null)
394 cmd.description = new MText ("No description");
397 KeySeq[] keys = new KeySeq[p.Count];
398 for (int j = 0; ! p.IsEmpty; p = p.next)
401 keys[j++] = new KeySeq (p.Text);
403 keys[j++] = new KeySeq (p.Plist);
409 private void parse_include (MPlist plist)
413 MPlist p = plist.Plist;
414 MSymbol language, name, extra;
418 name = extra = MSymbol.nil;
429 MInputMethod im = MInputMethod.Get (language, name, extra);
433 if (! plist.IsSymbol)
435 MSymbol target_type = plist.Symbol;
437 MSymbol target_name = MSymbol.nil;
439 target_name = plist.Symbol;
440 if (target_type == Mmacro)
442 if (target_name == MSymbol.nil)
443 im.local_domain.CopyFunc (local_domain);
445 im.local_domain.CopyFunc (target_name, local_domain);
447 else if (target_type == Mmap)
449 if (target_name == MSymbol.nil)
451 foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
452 maps[kv.Key] = kv.Value;
457 if (im.maps.TryGetValue (target_name, out map))
458 maps[target_name] = map;
461 else if (target_type == Mstate)
463 if (target_name == MSymbol.nil)
465 foreach (KeyValuePair<MSymbol, State> kv in im.states)
466 states[kv.Key] = kv.Value;
471 if (im.states.TryGetValue (target_name, out state))
472 states[target_name] = state;
477 private void parse_macros (MPlist plist)
479 for (; ! plist.IsEmpty; plist = plist.next)
482 MPlist pl = plist.Plist;
487 local_domain.Defun (pl.Symbol, new MPlist (), pl.next);
491 private void parse_maps (MPlist plist)
493 for (; ! plist.IsEmpty; plist = plist.next)
496 MPlist pl = plist.Plist;
500 Map map = new Map ();
501 map.name = pl.Symbol;
502 maps[map.name] = map;
503 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
510 keys = new KeySeq (p.Text);
512 keys = new KeySeq (p.Plist);
515 if (keys.keys.Length == 0
516 && keys.keys[0] == null)
522 MExpression expr = new MExpression (p, local_domain);
523 map.Add (keys, 0, expr);
528 private void parse_states (MPlist plist)
530 for (; ! plist.IsEmpty; plist = plist.next)
533 MPlist pl = plist.Plist;
544 State state = new State ();
545 state.name = pl.Symbol;
547 states[state.name] = state;
548 if (init_state == null)
550 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
557 MSymbol map_name = p.Symbol;
560 state.branches[map_name] = new MExpression (p, local_domain);
565 public static MInputMethod Get (MSymbol language, MSymbol name,
569 = new MDatabase.Tag (Minput_method, language, name, extra);
571 if (im_table.TryGetValue (tag, out im))
574 im = new MInputMethod (tag);
575 } catch (Exception e) {
576 Console.WriteLine (e);
582 private static void adjust_markers (MInputContext ic,
583 int from, int to, object inserted)
585 int ins = (inserted == null ? 0
586 : inserted is int ? 1
587 : ((MText) inserted).Length);
588 int diff = ins - (to - from);
590 for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
592 int pos = plist.Integer;
596 plist.val = pos + diff;
601 if (ic.cursor_pos >= to)
602 ic.cursor_pos += diff;
603 else if (ic.cursor_pos > from)
604 ic.cursor_pos = from;
607 private static void preedit_replace (MInputContext ic,
608 int from, int to, int c)
610 ic.preedit.Del (from, to);
611 ic.preedit.Ins (from, c);
612 adjust_markers (ic, from, to, c);
615 private static void preedit_replace (MInputContext ic,
616 int from, int to, MText mt)
618 ic.preedit[from, to] = mt;
619 adjust_markers (ic, from, to, mt);
622 private static object insert (MExpression[] args,
623 MExpression.Domain domain)
625 MInputContext ic = (MInputContext) domain.context;
626 object arg = args[0].Val;
629 preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
631 preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
632 ic.preedit_changed = true;
633 ic.cursor_pos_changed = true;
637 internal class Candidates
644 public Block (int index, MPlist plist)
655 get { return (Data is MText
656 ? ((MText) Data).Length
657 : ((MPlist) Data).Count); }
660 public object this[int i]
663 if (Data is MText) return ((MText) Data)[i];
664 return ((MPlist) Data)[i];
669 private Block[] blocks;
671 private int index = 0;
672 public object[] group;
674 private bool IsFixed { get { return group != null; } }
677 Block last = blocks[blocks.Length - 1];
678 return last.Index + last.Count; }
682 get { return (IsFixed ? index % group.Length
683 : index - blocks[row].Index); }
686 public object Group {
687 get { return (IsFixed ? group : blocks[row].Data); }
690 public int GroupLength
695 int nitems = group.Length;
696 int start = index - (index % nitems);
698 return (start + nitems <= total ? nitems : total - start);
700 return blocks[row].Count;
704 public object Current {
706 return (IsFixed ? group[index % group.Length]
707 : blocks[row][index - blocks[row].Index]);
711 public Candidates (MPlist list, int column)
713 int nblocks = list.Count;
715 blocks = new Block[nblocks];
716 for (int i = 0, start = 0; i < nblocks; i++, list = list.next)
717 start += (blocks[i] = new Block (index, list)).Count;
719 group = new object[column];
722 public static void Detach (MInputContext ic)
724 ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
725 ic.candidates = null;
726 ic.preedit_changed = true;
727 ic.cursor_pos_changed = true;
728 ic.candidate_changed = true;
731 // Fill the array "group" by candidates stating from INDEX.
732 // INDEX must be a multiple of "column". Set NTIMES to the
733 // number of valid candidates in "group". Update "block" if
734 // necessary. Return "group".
736 private int fill_group (int start)
738 int nitems = group.Length;
743 while (start < b.Index)
746 while (start >= b.Index + b.Count)
752 for (int i = 0; i < nitems; i++, start++)
757 if (r == blocks.Length)
768 // Update "row" to what contains the first candidate of
769 // the previous candidate-group, update "current_index", and
770 // update "group" if necessary. Return the previous
771 // candidate-group. Set NITEMS to the number of valid
772 // candidates contained in that group.
774 public int PrevGroup ()
781 nitems = group.Length;
782 if ((index -= col + nitems) < 0)
783 index = (Total / nitems) * nitems;
784 nitems = fill_group (index);
788 row = row > 0 ? row-- : blocks.Length - 1;
789 nitems = blocks[row].Count;
790 index = blocks[row].Index;
792 index += col < nitems ? col : nitems - 1;
796 public int NextGroup ()
803 nitems = group.Length;
804 if ((index += nitems - col) >= Total)
806 nitems = fill_group (index);
810 row = row < blocks.Length - 1 ? row + 1 : 0;
811 nitems = blocks[row].Count;
812 index = blocks[row].Count;
814 index += col < nitems ? col : nitems - 1;
824 int nitems = PrevGroup ();
825 index += col < nitems - 1 ? col : nitems - 1;
834 int nitems = GroupLength;
836 if (col == nitems - 1)
838 nitems = NextGroup ();
852 index += GroupLength - (Column + 1);
855 public void Select (int col)
857 int maxcol = GroupLength - 1;
860 index = index - Column + col;
863 public void Update (MInputContext ic)
867 if (ic.candidates == null)
869 from = ic.cursor_pos;
871 ic.candidates = this;
875 from = ic.candidate_from;
876 to = ic.candidate_to;
879 object candidate = ic.candidates.Current;
881 if (candidate is MText)
882 preedit_replace (ic, from, to, (MText) candidate);
884 preedit_replace (ic, from, to, (int) candidate);
885 ic.preedit.PushProp (from, to, Mcandidates, this);
887 ic.candidate_from = from;
888 ic.candidate_to = to;
889 ic.preedit_changed = true;
890 ic.cursor_pos_changed = true;
891 ic.candidate_changed = true;
895 private static object insert_candidates (MExpression[] args,
896 MExpression.Domain domain)
898 MInputContext ic = (MInputContext) domain.context;
899 MPlist list = (MPlist) args[0].Val;
902 if (domain.IsBound (Mcandidates_group_size))
904 object val = domain.GetValue (Mcandidates_group_size);
908 Candidates candidates = new Candidates (list, column);
909 candidates.Update (ic);
913 private static object state (MExpression[] args,
914 MExpression.Domain domain)
916 MInputContext ic = (MInputContext) domain.context;
917 MSymbol sym = (MSymbol) args[0].Args[0].Val;
919 if (sym == MSymbol.t)
921 return ic.im.states[sym];
924 private static object marker (MExpression[] args,
925 MExpression.Domain domain)
927 MInputContext ic = (MInputContext) domain.context;
928 MSymbol sym = (MSymbol) args[0].Args[0].Val;
929 int pos = ic.cursor_pos;
931 if (sym.Name.Length == 2 && sym.Name[0] == '@')
935 case '<': pos = 0; break;
936 case '>': pos = ic.preedit.Length; break;
937 case '-': pos = ic.cursor_pos - 1; break;
938 case '+': pos = ic.cursor_pos + 1; break;
943 ic.preedit.FindProp (Mcandidates, pos - 1, out pos, out to);
949 if (ic.cursor_pos < ic.preedit.Length - 1)
952 ic.preedit.FindProp (Mcandidates, pos, out from, out pos);
955 pos = ic.preedit.Length;
958 if (sym.Name[0] >= '0' && sym.Name[0] <= '9')
963 else if (sym.Name.Length >= 3 && sym.Name[0] == '@')
970 object val = ic.markers.Get (sym);
978 private static object char_at (MExpression[] args,
979 MExpression.Domain domain)
981 MInputContext ic = (MInputContext) domain.context;
982 MSymbol sym = (MSymbol) args[0].Args[0].Val;
990 private static object delete (MExpression[] args,
991 MExpression.Domain domain)
993 MInputContext ic = (MInputContext) domain.context;
994 int pos = (int) args[0].Val;
996 if (pos < ic.cursor_pos)
997 preedit_replace (ic, pos, ic.cursor_pos, null);
999 preedit_replace (ic, ic.cursor_pos, pos, null);
1000 ic.preedit_changed = true;
1001 ic.cursor_pos_changed = true;
1005 private static object select (MExpression[] args,
1006 MExpression.Domain domain)
1008 MInputContext ic = (MInputContext) domain.context;
1009 object arg = args[0].Val;
1011 if (ic.candidates == null)
1015 MSymbol sym = (MSymbol) arg;
1017 if (sym == Mat_less_than)
1018 ic.candidates.First ();
1019 else if (sym == Mat_greater_than)
1020 ic.candidates.Last ();
1021 else if (sym == Mat_minus)
1022 ic.candidates.Prev ();
1023 else if (sym == Mat_plus)
1024 ic.candidates.Next ();
1025 else if (sym == Mat_open_square_bracket)
1026 ic.candidates.PrevGroup ();
1027 else if (sym == Mat_close_square_bracket)
1028 ic.candidates.NextGroup ();
1032 else if (arg is int)
1033 ic.candidates.Select ((int) arg);
1034 ic.candidates.Update (ic);
1038 private static object show (MExpression[] args, MExpression.Domain domain)
1040 private static object hide (MExpression[] args, MExpression.Domain domain)
1042 private static object move (MExpression[] args, MExpression.Domain domain)
1044 private static object mark (MExpression[] args, MExpression.Domain domain)
1046 private static object pushback (MExpression[] args, MExpression.Domain domain)
1048 private static object pop (MExpression[] args, MExpression.Domain domain)
1050 private static object undo (MExpression[] args, MExpression.Domain domain)
1052 private static object commit (MExpression[] args, MExpression.Domain domain)
1054 private static object unhandle (MExpression[] args, MExpression.Domain domain)
1056 private static object shift (MExpression[] args, MExpression.Domain domain)
1058 private static object call (MExpression[] args, MExpression.Domain domain)
1062 public class MInputContext
1064 public MInputMethod im;
1065 public MText produced;
1067 public MText status;
1068 public bool status_changed;
1069 public MText preedit;
1070 public bool preedit_changed;
1071 public int cursor_pos;
1072 public bool cursor_pos_changed;
1073 internal MInputMethod.Candidates candidates;
1074 public MPlist candidate_group;
1075 public int candidate_index;
1076 public int candidate_from, candidate_to;
1077 public bool candidate_show;
1078 public bool candidate_changed;
1079 public MPlist callback_args;
1081 private MInputMethod.State state;
1082 private MInputMethod.State prev_state;
1083 private Stack<MSymbol> keys;
1084 private int state_key_head;
1085 private int key_head;
1086 private int commit_key_head;
1087 private MText preedit_saved;
1088 private int state_pos;
1089 internal MPlist markers = new MPlist ();
1090 private MPlist vars;
1091 private MPlist vars_saved;
1092 private MText preceding_text;
1093 private MText following_text;
1094 private bool key_unhandled;
1096 internal MExpression.Domain domain;
1098 public MInputContext (MInputMethod im)
1101 domain = new MExpression.Domain (im.local_domain, this);
1104 internal object GetCandidates (out int column)
1107 if (cursor_pos == 0)
1109 MInputMethod.Candidates candidates
1110 = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
1111 MInputMethod.Mcandidates);
1112 if (candidates == null)
1114 column = candidates.Column;
1115 return candidates.Current;