2 using System.Collections;
3 using System.Collections.Generic;
11 public class MInputMethod
13 private static MSymbol Minput_method = MSymbol.Of ("input-method");
14 private static MSymbol Mdescription = MSymbol.Of ("description");
15 private static MSymbol Mvariable = MSymbol.Of ("variable");
16 private static MSymbol Mcommand = MSymbol.Of ("command");
17 private static MSymbol Mtitle = MSymbol.Of ("title");
18 private static MSymbol Minclude = MSymbol.Of ("include");
19 private static MSymbol Mmacro = MSymbol.Of ("macro");
20 private static MSymbol Mmap = MSymbol.Of ("map");
21 private static MSymbol Mstate = MSymbol.Of ("state");
22 internal static MSymbol Mcandidates = MSymbol.Of ("candidates");
23 internal static MSymbol Mcandidates_group_size = MSymbol.Of ("candidates-group-size");
24 private static MSymbol Mat_less_than = MSymbol.Of ("@<");
25 private static MSymbol Mat_greater_than = MSymbol.Of ("@>");
26 private static MSymbol Mat_minus = MSymbol.Of ("@-");
27 private static MSymbol Mat_plus = MSymbol.Of ("@+");
28 private static MSymbol Mat_open_square_bracket = MSymbol.Of ("@[");
29 private static MSymbol Mat_close_square_bracket = MSymbol.Of ("@]");
30 private static MSymbol Minsert = MSymbol.Of ("insert");
32 internal class Variable
35 public MText description;
38 public MPlist candidates;
41 internal class Command
44 public MText description;
45 public MSymbol[][] keys;
50 public MSymbol[] keys;
52 private static MSymbol char_to_symbol (int c)
54 return MSymbol.Of (String.Format ("#{0:X}", c));
57 public KeySeq (MPlist plist)
59 keys = new MSymbol[plist.Count];
61 foreach (MPlist p in plist)
66 keys[i++] = char_to_symbol (p.Integer);
72 public KeySeq (MText mt)
74 keys = new MSymbol[mt.Length];
75 for (int i = 0; i < mt.Length; i++)
76 keys[i] = char_to_symbol (mt[i]);
79 public MSymbol this[int i] { get { return keys[i]; } }
81 public int Length { get { return keys.Length; } }
87 public Dictionary<MSymbol, Map> submaps;
88 public MExpression actions;
90 public void Add (KeySeq keys, int index, MExpression actions)
95 submaps = new Dictionary<MSymbol, Map> ();
97 submaps.TryGetValue (keys[index], out sub);
100 MSymbol sym = keys[index];
101 submaps[sym] = sub = new Map ();
103 if (index + 1 < keys.Length)
104 sub.Add (keys, index + 1, actions);
106 this.actions = actions;
109 public MExpression Lookup (KeySeq keys, int index)
113 if (index + 1 == keys.Length)
115 if (submaps.TryGetValue (keys[index], out sub))
116 return sub.Lookup (keys, index + 1);
125 public Dictionary<MSymbol, MExpression> branches
126 = new Dictionary<MSymbol, MExpression> ();
129 private static Dictionary<MDatabase.Tag, MInputMethod> im_table
130 = new Dictionary<MDatabase.Tag, MInputMethod> ();
132 private static MExpression.FunctionTable global_table
133 = new MExpression.FunctionTable ();
135 public readonly MSymbol language;
136 public readonly MSymbol name;
137 public readonly MSymbol subname;
139 internal MExpression.FunctionTable local_table
140 = new MExpression.FunctionTable (global_table);
141 internal MDatabase mdb;
142 internal MText description;
143 internal MText title;
144 internal Command[] commands;
145 internal Variable[] variables;
146 internal MPlist bindings;
147 internal Dictionary<MSymbol, Map> maps
148 = new Dictionary<MSymbol, Map> ();
149 internal State init_state;
150 internal Dictionary<MSymbol, State> states
151 = new Dictionary<MSymbol, State> ();
152 internal MPlist externals;
154 static MInputMethod ()
156 MExpression.Defun (global_table, "insert",
157 new MExpression.Evaluator (insert),
158 1, 1, typeof (MExpression));
159 MExpression.Defun (global_table, "candidates",
160 new MExpression.Evaluator (insert_candidates),
161 1, 1, typeof (object));
162 MExpression.Defun (global_table, "delete",
163 new MExpression.Evaluator (delete),
164 1, 1, typeof (object));
165 MExpression.Defun (global_table, "select",
166 new MExpression.Evaluator (select),
167 1, 1, typeof (object));
168 MExpression.Defun (global_table, "show",
169 new MExpression.Evaluator (show),
171 MExpression.Defun (global_table, "hide",
172 new MExpression.Evaluator (hide),
174 MExpression.Defun (global_table, "move",
175 new MExpression.Evaluator (move),
176 1, 1, typeof (object));
177 MExpression.Defun (global_table, "mark",
178 new MExpression.Evaluator (mark),
179 1, 1, typeof (MSymbol));
180 MExpression.Defun (global_table, "pushback",
181 new MExpression.Evaluator (pushback),
182 1, 1, typeof (object));
183 MExpression.Defun (global_table, "pop",
184 new MExpression.Evaluator (pop),
186 MExpression.Defun (global_table, "undo",
187 new MExpression.Evaluator (undo),
188 0, 1, typeof (object));
189 MExpression.Defun (global_table, "commit",
190 new MExpression.Evaluator (commit),
192 MExpression.Defun (global_table, "unhandle",
193 new MExpression.Evaluator (unhandle),
195 MExpression.Defun (global_table, "shift",
196 new MExpression.Evaluator (shift),
197 1, 1, typeof (MSymbol));
198 MExpression.Defun (global_table, "call",
199 new MExpression.Evaluator (call),
200 2, -1, typeof (MSymbol), typeof (MSymbol),
204 private MInputMethod (MDatabase.Tag tag)
206 mdb = MDatabase.Find (tag);
208 throw new Exception (String.Format ("Input method {0} not available",
213 MPlist plist = (MPlist) mdb.Load ();
216 for (; ! plist.IsEmpty; plist = plist.next)
219 MPlist pl = plist.Plist;
222 MSymbol sym = pl.Symbol;
224 if (sym == Mdescription)
226 description = parse_description (pl);
227 if (description == null)
228 description = new MText ("No description");
230 else if (sym == Mtitle)
235 else if (sym == Mvariable)
236 parse_variables (pl);
237 else if (sym == Mcommand)
239 else if (sym == Minclude)
241 else if (sym == Mmacro)
243 else if (sym == Mmap)
245 else if (sym == Mstate)
251 private static MText parse_description (MPlist plist)
253 return (plist.IsMText ? plist.Text
254 : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
258 private void parse_variables (MPlist plist)
260 variables = new Variable[plist.Count];
262 for (int i = 0; ! plist.IsEmpty; plist = plist.next)
263 if (plist.IsPlist && plist.Plist.IsSymbol)
265 Variable var = new Variable ();
266 MPlist p = plist.Plist;
270 var.description = parse_description (p);
271 if (var.description == null)
272 var.description = new MText ("No description");
275 var.type = (p.IsMText ? typeof (MText)
276 : p.IsInteger ? typeof (int)
277 : p.IsSymbol ? typeof (MSymbol)
280 var.candidates = p.next;
281 variables[i++] = var;
285 private void parse_commands (MPlist plist)
287 commands = new Command[plist.Count];
289 for (int i = 0; ! plist.IsEmpty; plist = plist.next)
290 if (plist.IsPlist && plist.Plist.IsSymbol)
292 Command cmd = new Command ();
293 MPlist p = plist.Plist;
297 cmd.description = parse_description (p);
298 if (cmd.description == null)
299 cmd.description = new MText ("No description");
302 KeySeq[] keys = new KeySeq[p.Count];
303 for (int j = 0; ! p.IsEmpty; p = p.next)
306 keys[j++] = new KeySeq (p.Text);
308 keys[j++] = new KeySeq (p.Plist);
314 private void parse_include (MPlist plist)
318 MPlist p = plist.Plist;
319 MSymbol language, name, extra;
323 name = extra = MSymbol.nil;
334 MInputMethod im = MInputMethod.Get (language, name, extra);
338 if (! plist.IsSymbol)
340 MSymbol target_type = plist.Symbol;
342 MSymbol target_name = MSymbol.nil;
344 target_name = plist.Symbol;
345 if (target_type == Mmacro)
347 if (target_name == MSymbol.nil)
348 im.local_table.Copy (local_table);
350 im.local_table.Copy (target_name, local_table);
352 else if (target_type == Mmap)
354 if (target_name == MSymbol.nil)
356 foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
357 maps[kv.Key] = kv.Value;
362 if (im.maps.TryGetValue (target_name, out map))
363 maps[target_name] = map;
366 else if (target_type == Mstate)
368 if (target_name == MSymbol.nil)
370 foreach (KeyValuePair<MSymbol, State> kv in im.states)
371 states[kv.Key] = kv.Value;
376 if (im.states.TryGetValue (target_name, out state))
377 states[target_name] = state;
382 private void parse_macros (MPlist plist)
384 for (; ! plist.IsEmpty; plist = plist.next)
387 MPlist pl = plist.Plist;
391 MSymbol name = pl.Symbol;
392 MExpression expr = new MExpression (pl.next, local_table);
393 MExpression.Defmacro (local_table, name, expr);
397 private void parse_maps (MPlist plist)
399 for (; ! plist.IsEmpty; plist = plist.next)
402 MPlist pl = plist.Plist;
406 Map map = new Map ();
407 map.name = pl.Symbol;
408 maps[map.name] = map;
409 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
416 keys = new KeySeq (p.Text);
418 keys = new KeySeq (p.Plist);
421 if (keys.keys.Length == 0
422 && keys.keys[0] == null)
427 MExpression expr = new MExpression (p, local_table);
428 map.Add (keys, 0, expr);
433 private void parse_states (MPlist plist)
435 for (; ! plist.IsEmpty; plist = plist.next)
438 MPlist pl = plist.Plist;
449 State state = new State ();
450 state.name = pl.Symbol;
452 states[state.name] = state;
453 if (init_state == null)
455 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
462 MSymbol map_name = p.Symbol;
464 state.branches[map_name] = new MExpression (p, local_table);
469 public static MInputMethod Get (MSymbol language, MSymbol name,
473 = new MDatabase.Tag (Minput_method, language, name, extra);
475 if (im_table.TryGetValue (tag, out im))
478 im = new MInputMethod (tag);
479 } catch (Exception e) {
480 Console.WriteLine (e);
486 private static void adjust_markers (MInputContext ic,
487 int from, int to, object inserted)
489 int ins = (inserted == null ? 0
490 : inserted is int ? 1
491 : ((MText) inserted).Length);
492 int diff = ins - (to - from);
494 for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
496 int pos = plist.Integer;
500 plist.val = pos + diff;
505 if (ic.cursor_pos >= to)
506 ic.cursor_pos += diff;
507 else if (ic.cursor_pos > from)
508 ic.cursor_pos = from;
511 private static void preedit_replace (MInputContext ic,
512 int from, int to, int c)
514 ic.preedit.Del (from, to);
515 ic.preedit.Ins (from, c);
516 adjust_markers (ic, from, to, c);
519 private static void preedit_replace (MInputContext ic,
520 int from, int to, MText mt)
522 ic.preedit[from, to] = mt;
523 adjust_markers (ic, from, to, mt);
526 private static object insert (object[] args, MPlist bindings,
529 MInputContext ic = (MInputContext) context;
530 object arg = ((MExpression) args[0]).Eval (bindings, ic);
533 preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
535 preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
536 ic.preedit_changed = true;
537 ic.cursor_pos_changed = true;
541 internal class Candidates
548 public Block (int index, MPlist plist)
559 get { return (Data is MText
560 ? ((MText) Data).Length
561 : ((MPlist) Data).Count); }
564 public object this[int i]
567 if (Data is MText) return ((MText) Data)[i];
568 return ((MPlist) Data)[i];
573 private Block[] blocks;
575 private int index = 0;
576 public object[] group;
578 private bool IsFixed { get { return group != null; } }
581 Block last = blocks[blocks.Length - 1];
582 return last.Index + last.Count; }
586 get { return (IsFixed ? index % group.Length
587 : index - blocks[row].Index); }
590 public object Group {
591 get { return (IsFixed ? group : blocks[row].Data); }
594 public int GroupLength
599 int nitems = group.Length;
600 int start = index - (index % nitems);
602 return (start + nitems <= total ? nitems : total - start);
604 return blocks[row].Count;
608 public object Current {
610 return (IsFixed ? group[index % group.Length]
611 : blocks[row][index - blocks[row].Index]);
615 public Candidates (MPlist list, int column)
617 int nblocks = list.Count;
619 blocks = new Block[nblocks];
620 for (int i = 0, start = 0; i < nblocks; i++, list = list.next)
621 start += (blocks[i] = new Block (index, list)).Count;
623 group = new object[column];
626 public static void Detach (MInputContext ic)
628 ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
629 ic.candidates = null;
630 ic.preedit_changed = true;
631 ic.cursor_pos_changed = true;
632 ic.candidate_changed = true;
635 // Fill the array "group" by candidates stating from INDEX.
636 // INDEX must be a multiple of "column". Set NTIMES to the
637 // number of valid candidates in "group". Update "block" if
638 // necessary. Return "group".
640 private int fill_group (int start)
642 int nitems = group.Length;
647 while (start < b.Index)
650 while (start >= b.Index + b.Count)
656 for (int i = 0; i < nitems; i++, start++)
661 if (r == blocks.Length)
672 // Update "row" to what contains the first candidate of
673 // the previous candidate-group, update "current_index", and
674 // update "group" if necessary. Return the previous
675 // candidate-group. Set NITEMS to the number of valid
676 // candidates contained in that group.
678 public int PrevGroup ()
685 nitems = group.Length;
686 if ((index -= col + nitems) < 0)
687 index = (Total / nitems) * nitems;
688 nitems = fill_group (index);
692 row = row > 0 ? row-- : blocks.Length - 1;
693 nitems = blocks[row].Count;
694 index = blocks[row].Index;
696 index += col < nitems ? col : nitems - 1;
700 public int NextGroup ()
707 nitems = group.Length;
708 if ((index += nitems - col) >= Total)
710 nitems = fill_group (index);
714 row = row < blocks.Length - 1 ? row + 1 : 0;
715 nitems = blocks[row].Count;
716 index = blocks[row].Count;
718 index += col < nitems ? col : nitems - 1;
728 int nitems = PrevGroup ();
729 index += col < nitems - 1 ? col : nitems - 1;
738 int nitems = GroupLength;
740 if (col == nitems - 1)
742 nitems = NextGroup ();
756 index += GroupLength - (Column + 1);
759 public void Select (int col)
761 int maxcol = GroupLength - 1;
764 index = index - Column + col;
767 public void Update (MInputContext ic)
771 if (ic.candidates == null)
773 from = ic.cursor_pos;
775 ic.candidates = this;
779 from = ic.candidate_from;
780 to = ic.candidate_to;
783 object candidate = ic.candidates.Current;
785 if (candidate is MText)
786 preedit_replace (ic, from, to, (MText) candidate);
788 preedit_replace (ic, from, to, (int) candidate);
789 ic.preedit.PushProp (from, to, Mcandidates, this);
791 ic.candidate_from = from;
792 ic.candidate_to = to;
793 ic.preedit_changed = true;
794 ic.cursor_pos_changed = true;
795 ic.candidate_changed = true;
799 private static object insert_candidates (object[] args, MPlist bindings,
802 MInputContext ic = (MInputContext) context;
803 MPlist list = (MPlist) args[0];
806 MPlist slot = (MPlist) bindings.Find (Mcandidates_group_size);
808 column = slot.Integer;
809 Candidates candidates = new Candidates (list, column);
810 candidates.Update (ic);
814 private static object select (object[] args, MPlist bindings,
817 MInputContext ic = (MInputContext) context;
818 object arg = args[0];
820 if (ic.candidates == null)
824 MSymbol sym = (MSymbol) arg;
826 if (sym == Mat_less_than)
827 ic.candidates.First ();
828 else if (sym == Mat_greater_than)
829 ic.candidates.Last ();
830 else if (sym == Mat_minus)
831 ic.candidates.Prev ();
832 else if (sym == Mat_plus)
833 ic.candidates.Next ();
834 else if (sym == Mat_open_square_bracket)
835 ic.candidates.PrevGroup ();
836 else if (sym == Mat_close_square_bracket)
837 ic.candidates.NextGroup ();
842 ic.candidates.Select ((int) arg);
843 ic.candidates.Update (ic);
847 private static object delete (object[] args, MPlist bindings,
848 object context) { return 1; }
849 private static object show (object[] args, MPlist bindings,
850 object context) { return 1; }
851 private static object hide (object[] args, MPlist bindings,
852 object context) { return 1; }
853 private static object move (object[] args, MPlist bindings,
854 object context) { return 1; }
855 private static object mark (object[] args, MPlist bindings,
856 object context) { return 1; }
857 private static object pushback (object[] args, MPlist bindings,
858 object context) { return 1; }
859 private static object pop (object[] args, MPlist bindings,
860 object context) { return 1; }
861 private static object undo (object[] args, MPlist bindings,
862 object context) { return 1; }
863 private static object commit (object[] args, MPlist bindings,
864 object context) { return 1; }
865 private static object unhandle (object[] args, MPlist bindings,
866 object context) { return 1; }
867 private static object shift (object[] args, MPlist bindings,
868 object context) { return 1; }
869 private static object call (object[] args, MPlist bindings,
870 object context) { return 1; }
873 public class MInputContext
875 public MInputMethod im;
876 public MText produced;
879 public bool status_changed;
880 public MText preedit;
881 public bool preedit_changed;
882 public int cursor_pos;
883 public bool cursor_pos_changed;
884 internal MInputMethod.Candidates candidates;
885 public MPlist candidate_group;
886 public int candidate_index;
887 public int candidate_from, candidate_to;
888 public bool candidate_show;
889 public bool candidate_changed;
890 public MPlist callback_args;
892 private MInputMethod.State state;
893 private MInputMethod.State prev_state;
894 private Stack<MSymbol> keys;
895 private int state_key_head;
896 private int key_head;
897 private int commit_key_head;
898 private MText preedit_saved;
899 private int state_pos;
900 internal MPlist markers = new MPlist ();
902 private MPlist vars_saved;
903 private MText preceding_text;
904 private MText following_text;
905 private bool key_unhandled;
907 internal object GetCandidates (out int column)
912 MInputMethod.Candidates candidates
913 = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
914 MInputMethod.Mcandidates);
915 if (candidates == null)
917 column = candidates.Column;
918 return candidates.Current;