1
2
3
4
5
6
7
8
9 package jline.console;
10
11 import java.util.HashMap;
12 import java.util.Map;
13
14
15
16
17
18
19
20 public class KeyMap {
21
22 public static final String VI_MOVE = "vi-move";
23 public static final String VI_INSERT = "vi-insert";
24 public static final String EMACS = "emacs";
25 public static final String EMACS_STANDARD = "emacs-standard";
26 public static final String EMACS_CTLX = "emacs-ctlx";
27 public static final String EMACS_META = "emacs-meta";
28
29 private static final int KEYMAP_LENGTH = 256;
30
31 private static final Object NULL_FUNCTION = new Object();
32
33 private Object[] mapping = new Object[KEYMAP_LENGTH];
34 private Object anotherKey = null;
35 private String name;
36 private boolean isViKeyMap;
37
38 public KeyMap(String name, boolean isViKeyMap) {
39 this(name, new Object[KEYMAP_LENGTH], isViKeyMap);
40 }
41
42 protected KeyMap(String name, Object[] mapping, boolean isViKeyMap) {
43 this.mapping = mapping;
44 this.name = name;
45 this.isViKeyMap = isViKeyMap;
46 }
47
48 public boolean isViKeyMap() {
49 return isViKeyMap;
50 }
51
52 public String getName() {
53 return name;
54 }
55
56 public Object getAnotherKey() {
57 return anotherKey;
58 }
59
60 public void from(KeyMap other) {
61 this.mapping = other.mapping;
62 this.anotherKey = other.anotherKey;
63 }
64
65 public Object getBound( CharSequence keySeq ) {
66 if (keySeq != null && keySeq.length() > 0) {
67 KeyMap map = this;
68 for (int i = 0; i < keySeq.length(); i++) {
69 char c = keySeq.charAt(i);
70 if (c > 255) {
71 return Operation.SELF_INSERT;
72 }
73 if (map.mapping[c] instanceof KeyMap) {
74 if (i == keySeq.length() - 1) {
75 return map.mapping[c];
76 } else {
77 map = (KeyMap) map.mapping[c];
78 }
79 } else {
80 return map.mapping[c];
81 }
82 }
83 }
84 return null;
85 }
86
87 public void bindIfNotBound( CharSequence keySeq, Object function ) {
88
89 bind (this, keySeq, function, true);
90 }
91
92 public void bind( CharSequence keySeq, Object function ) {
93
94 bind (this, keySeq, function, false);
95 }
96
97 private static void bind( KeyMap map, CharSequence keySeq, Object function ) {
98
99 bind (map, keySeq, function, false);
100 }
101
102 private static void bind( KeyMap map, CharSequence keySeq, Object function,
103 boolean onlyIfNotBound ) {
104
105 if (keySeq != null && keySeq.length() > 0) {
106 for (int i = 0; i < keySeq.length(); i++) {
107 char c = keySeq.charAt(i);
108 if (c >= map.mapping.length) {
109 return;
110 }
111 if (i < keySeq.length() - 1) {
112 if (!(map.mapping[c] instanceof KeyMap)) {
113 KeyMap m = new KeyMap("anonymous", false);
114 if (map.mapping[c] != Operation.DO_LOWERCASE_VERSION) {
115 m.anotherKey = map.mapping[c];
116 }
117 map.mapping[c] = m;
118 }
119 map = (KeyMap) map.mapping[c];
120 } else {
121 if (function == null) {
122 function = NULL_FUNCTION;
123 }
124 if (map.mapping[c] instanceof KeyMap) {
125 map.anotherKey = function;
126 } else {
127 Object op = map.mapping[c];
128 if (onlyIfNotBound == false
129 || op == null
130 || op == Operation.DO_LOWERCASE_VERSION
131 || op == Operation.VI_MOVEMENT_MODE ) {
132
133 }
134
135 map.mapping[c] = function;
136 }
137 }
138 }
139 }
140 }
141
142 public void setBlinkMatchingParen(boolean on) {
143 if (on) {
144 bind( "}", Operation.INSERT_CLOSE_CURLY );
145 bind( ")", Operation.INSERT_CLOSE_PAREN );
146 bind( "]", Operation.INSERT_CLOSE_SQUARE );
147 }
148 }
149
150 private static void bindArrowKeys(KeyMap map) {
151
152
153 bind( map, "\033[0A", Operation.PREVIOUS_HISTORY );
154 bind( map, "\033[0B", Operation.BACKWARD_CHAR );
155 bind( map, "\033[0C", Operation.FORWARD_CHAR );
156 bind( map, "\033[0D", Operation.NEXT_HISTORY );
157
158
159 bind( map, "\340\000", Operation.KILL_WHOLE_LINE );
160 bind( map, "\340\107", Operation.BEGINNING_OF_LINE );
161 bind( map, "\340\110", Operation.PREVIOUS_HISTORY );
162 bind( map, "\340\111", Operation.BEGINNING_OF_HISTORY );
163 bind( map, "\340\113", Operation.BACKWARD_CHAR );
164 bind( map, "\340\115", Operation.FORWARD_CHAR );
165 bind( map, "\340\117", Operation.END_OF_LINE );
166 bind( map, "\340\120", Operation.NEXT_HISTORY );
167 bind( map, "\340\121", Operation.END_OF_HISTORY );
168 bind( map, "\340\122", Operation.OVERWRITE_MODE );
169 bind( map, "\340\123", Operation.DELETE_CHAR );
170
171 bind( map, "\000\107", Operation.BEGINNING_OF_LINE );
172 bind( map, "\000\110", Operation.PREVIOUS_HISTORY );
173 bind( map, "\000\111", Operation.BEGINNING_OF_HISTORY );
174 bind( map, "\000\110", Operation.PREVIOUS_HISTORY );
175 bind( map, "\000\113", Operation.BACKWARD_CHAR );
176 bind( map, "\000\115", Operation.FORWARD_CHAR );
177 bind( map, "\000\117", Operation.END_OF_LINE );
178 bind( map, "\000\120", Operation.NEXT_HISTORY );
179 bind( map, "\000\121", Operation.END_OF_HISTORY );
180 bind( map, "\000\122", Operation.OVERWRITE_MODE );
181 bind( map, "\000\123", Operation.DELETE_CHAR );
182
183 bind( map, "\033[A", Operation.PREVIOUS_HISTORY );
184 bind( map, "\033[B", Operation.NEXT_HISTORY );
185 bind( map, "\033[C", Operation.FORWARD_CHAR );
186 bind( map, "\033[D", Operation.BACKWARD_CHAR );
187 bind( map, "\033[H", Operation.BEGINNING_OF_LINE );
188 bind( map, "\033[F", Operation.END_OF_LINE );
189
190 bind( map, "\033OA", Operation.PREVIOUS_HISTORY );
191 bind( map, "\033OB", Operation.NEXT_HISTORY );
192 bind( map, "\033OC", Operation.FORWARD_CHAR );
193 bind( map, "\033OD", Operation.BACKWARD_CHAR );
194 bind( map, "\033OH", Operation.BEGINNING_OF_LINE );
195 bind( map, "\033OF", Operation.END_OF_LINE );
196
197 bind( map, "\033[1~", Operation.BEGINNING_OF_LINE);
198 bind( map, "\033[4~", Operation.END_OF_LINE);
199 bind( map, "\033[3~", Operation.DELETE_CHAR);
200
201
202 bind( map, "\0340H", Operation.PREVIOUS_HISTORY );
203 bind( map, "\0340P", Operation.NEXT_HISTORY );
204 bind( map, "\0340M", Operation.FORWARD_CHAR );
205 bind( map, "\0340K", Operation.BACKWARD_CHAR );
206 }
207
208
209
210
211
212
213
214
215
216 public static boolean isMeta( char c ) {
217 return c > 0x7f && c <= 0xff;
218 }
219
220 public static char unMeta( char c ) {
221 return (char) (c & 0x7F);
222 }
223
224 public static char meta( char c ) {
225 return (char) (c | 0x80);
226 }
227
228 public static Map<String, KeyMap> keyMaps() {
229 Map<String, KeyMap> keyMaps = new HashMap<String, KeyMap>();
230
231 KeyMap emacs = emacs();
232 bindArrowKeys(emacs);
233 keyMaps.put(EMACS, emacs);
234 keyMaps.put(EMACS_STANDARD, emacs);
235 keyMaps.put(EMACS_CTLX, (KeyMap) emacs.getBound("\u0018"));
236 keyMaps.put(EMACS_META, (KeyMap) emacs.getBound("\u001b"));
237
238 KeyMap viMov = viMovement();
239 bindArrowKeys(viMov);
240 keyMaps.put(VI_MOVE, viMov);
241 keyMaps.put("vi-command", viMov);
242
243 KeyMap viIns = viInsertion();
244 bindArrowKeys(viIns);
245 keyMaps.put(VI_INSERT, viIns);
246 keyMaps.put("vi", viIns);
247
248 return keyMaps;
249 }
250
251 public static KeyMap emacs() {
252 Object[] map = new Object[KEYMAP_LENGTH];
253 Object[] ctrl = new Object[] {
254
255 Operation.SET_MARK,
256 Operation.BEGINNING_OF_LINE,
257 Operation.BACKWARD_CHAR,
258 Operation.INTERRUPT,
259 Operation.EXIT_OR_DELETE_CHAR,
260 Operation.END_OF_LINE,
261 Operation.FORWARD_CHAR,
262 Operation.ABORT,
263 Operation.BACKWARD_DELETE_CHAR,
264 Operation.COMPLETE,
265 Operation.ACCEPT_LINE,
266 Operation.KILL_LINE,
267 Operation.CLEAR_SCREEN,
268 Operation.ACCEPT_LINE,
269 Operation.NEXT_HISTORY,
270 null,
271 Operation.PREVIOUS_HISTORY,
272 Operation.QUOTED_INSERT,
273 Operation.REVERSE_SEARCH_HISTORY,
274 Operation.FORWARD_SEARCH_HISTORY,
275 Operation.TRANSPOSE_CHARS,
276 Operation.UNIX_LINE_DISCARD,
277 Operation.QUOTED_INSERT,
278 Operation.UNIX_WORD_RUBOUT,
279 emacsCtrlX(),
280 Operation.YANK,
281 null,
282 emacsMeta(),
283 null,
284 Operation.CHARACTER_SEARCH,
285 null,
286 Operation.UNDO,
287 };
288 System.arraycopy( ctrl, 0, map, 0, ctrl.length );
289 for (int i = 32; i < 256; i++) {
290 map[i] = Operation.SELF_INSERT;
291 }
292 map[DELETE] = Operation.BACKWARD_DELETE_CHAR;
293 return new KeyMap(EMACS, map, false);
294 }
295
296 public static final char CTRL_D = (char) 4;
297 public static final char CTRL_G = (char) 7;
298 public static final char CTRL_H = (char) 8;
299 public static final char CTRL_I = (char) 9;
300 public static final char CTRL_J = (char) 10;
301 public static final char CTRL_M = (char) 13;
302 public static final char CTRL_R = (char) 18;
303 public static final char CTRL_S = (char) 19;
304 public static final char CTRL_U = (char) 21;
305 public static final char CTRL_X = (char) 24;
306 public static final char CTRL_Y = (char) 25;
307 public static final char ESCAPE = (char) 27;
308 public static final char CTRL_OB = (char) 27;
309 public static final char CTRL_CB = (char) 29;
310
311 public static final int DELETE = (char) 127;
312
313 public static KeyMap emacsCtrlX() {
314 Object[] map = new Object[KEYMAP_LENGTH];
315 map[CTRL_G] = Operation.ABORT;
316 map[CTRL_R] = Operation.RE_READ_INIT_FILE;
317 map[CTRL_U] = Operation.UNDO;
318 map[CTRL_X] = Operation.EXCHANGE_POINT_AND_MARK;
319 map['('] = Operation.START_KBD_MACRO;
320 map[')'] = Operation.END_KBD_MACRO;
321 for (int i = 'A'; i <= 'Z'; i++) {
322 map[i] = Operation.DO_LOWERCASE_VERSION;
323 }
324 map['e'] = Operation.CALL_LAST_KBD_MACRO;
325 map[DELETE] = Operation.KILL_LINE;
326 return new KeyMap(EMACS_CTLX, map, false);
327 }
328
329 public static KeyMap emacsMeta() {
330 Object[] map = new Object[KEYMAP_LENGTH];
331 map[CTRL_G] = Operation.ABORT;
332 map[CTRL_H] = Operation.BACKWARD_KILL_WORD;
333 map[CTRL_I] = Operation.TAB_INSERT;
334 map[CTRL_J] = Operation.VI_EDITING_MODE;
335 map[CTRL_M] = Operation.VI_EDITING_MODE;
336 map[CTRL_R] = Operation.REVERT_LINE;
337 map[CTRL_Y] = Operation.YANK_NTH_ARG;
338 map[CTRL_OB] = Operation.COMPLETE;
339 map[CTRL_CB] = Operation.CHARACTER_SEARCH_BACKWARD;
340 map[' '] = Operation.SET_MARK;
341 map['#'] = Operation.INSERT_COMMENT;
342 map['&'] = Operation.TILDE_EXPAND;
343 map['*'] = Operation.INSERT_COMPLETIONS;
344 map['-'] = Operation.DIGIT_ARGUMENT;
345 map['.'] = Operation.YANK_LAST_ARG;
346 map['<'] = Operation.BEGINNING_OF_HISTORY;
347 map['='] = Operation.POSSIBLE_COMPLETIONS;
348 map['>'] = Operation.END_OF_HISTORY;
349 map['?'] = Operation.POSSIBLE_COMPLETIONS;
350 for (int i = 'A'; i <= 'Z'; i++) {
351 map[i] = Operation.DO_LOWERCASE_VERSION;
352 }
353 map['\\'] = Operation.DELETE_HORIZONTAL_SPACE;
354 map['_'] = Operation.YANK_LAST_ARG;
355 map['b'] = Operation.BACKWARD_WORD;
356 map['c'] = Operation.CAPITALIZE_WORD;
357 map['d'] = Operation.KILL_WORD;
358 map['f'] = Operation.FORWARD_WORD;
359 map['l'] = Operation.DOWNCASE_WORD;
360 map['p'] = Operation.NON_INCREMENTAL_REVERSE_SEARCH_HISTORY;
361 map['r'] = Operation.REVERT_LINE;
362 map['t'] = Operation.TRANSPOSE_WORDS;
363 map['u'] = Operation.UPCASE_WORD;
364 map['y'] = Operation.YANK_POP;
365 map['~'] = Operation.TILDE_EXPAND;
366 map[DELETE] = Operation.BACKWARD_KILL_WORD;
367 return new KeyMap(EMACS_META, map, false);
368 }
369
370 public static KeyMap viInsertion() {
371 Object[] map = new Object[KEYMAP_LENGTH];
372 Object[] ctrl = new Object[] {
373
374 null,
375 Operation.SELF_INSERT,
376 Operation.SELF_INSERT,
377 Operation.SELF_INSERT,
378 Operation.VI_EOF_MAYBE,
379 Operation.SELF_INSERT,
380 Operation.SELF_INSERT,
381 Operation.SELF_INSERT,
382 Operation.BACKWARD_DELETE_CHAR,
383 Operation.COMPLETE,
384 Operation.ACCEPT_LINE,
385 Operation.SELF_INSERT,
386 Operation.SELF_INSERT,
387 Operation.ACCEPT_LINE,
388 Operation.MENU_COMPLETE,
389 Operation.SELF_INSERT,
390 Operation.MENU_COMPLETE_BACKWARD,
391 Operation.SELF_INSERT,
392 Operation.REVERSE_SEARCH_HISTORY,
393 Operation.FORWARD_SEARCH_HISTORY,
394 Operation.TRANSPOSE_CHARS,
395 Operation.UNIX_LINE_DISCARD,
396 Operation.QUOTED_INSERT,
397 Operation.UNIX_WORD_RUBOUT,
398 Operation.SELF_INSERT,
399 Operation.YANK,
400 Operation.SELF_INSERT,
401 Operation.VI_MOVEMENT_MODE,
402 Operation.SELF_INSERT,
403 Operation.SELF_INSERT,
404 Operation.SELF_INSERT,
405 Operation.UNDO,
406 };
407 System.arraycopy( ctrl, 0, map, 0, ctrl.length );
408 for (int i = 32; i < 256; i++) {
409 map[i] = Operation.SELF_INSERT;
410 }
411 map[DELETE] = Operation.BACKWARD_DELETE_CHAR;
412 return new KeyMap(VI_INSERT, map, false);
413 }
414
415 public static KeyMap viMovement() {
416 Object[] map = new Object[KEYMAP_LENGTH];
417 Object[] low = new Object[] {
418
419 null,
420 null,
421 null,
422 Operation.INTERRUPT,
423
424
425
426
427 Operation.VI_EOF_MAYBE,
428 Operation.EMACS_EDITING_MODE,
429 null,
430 Operation.ABORT,
431 Operation.BACKWARD_CHAR,
432 null,
433 Operation.VI_MOVE_ACCEPT_LINE,
434 Operation.KILL_LINE,
435 Operation.CLEAR_SCREEN,
436 Operation.VI_MOVE_ACCEPT_LINE,
437 Operation.VI_NEXT_HISTORY,
438 null,
439 Operation.VI_PREVIOUS_HISTORY,
440
441
442
443
444 Operation.QUOTED_INSERT,
445
446
447
448
449
450
451 Operation.REVERSE_SEARCH_HISTORY,
452
453 Operation.FORWARD_SEARCH_HISTORY,
454 Operation.TRANSPOSE_CHARS,
455 Operation.UNIX_LINE_DISCARD,
456
457 Operation.QUOTED_INSERT,
458 Operation.UNIX_WORD_RUBOUT,
459 null,
460
461 Operation.YANK,
462 null,
463 emacsMeta(),
464 null,
465
466 Operation.CHARACTER_SEARCH,
467 null,
468
469 Operation.UNDO,
470 Operation.FORWARD_CHAR,
471 null,
472 null,
473 Operation.VI_INSERT_COMMENT,
474 Operation.END_OF_LINE,
475 Operation.VI_MATCH,
476 Operation.VI_TILDE_EXPAND,
477 null,
478 null,
479 null,
480
481 Operation.VI_COMPLETE,
482 Operation.VI_NEXT_HISTORY,
483 Operation.VI_CHAR_SEARCH,
484 Operation.VI_PREVIOUS_HISTORY,
485
486 Operation.VI_REDO,
487 Operation.VI_SEARCH,
488 Operation.VI_BEGNNING_OF_LINE_OR_ARG_DIGIT,
489 Operation.VI_ARG_DIGIT,
490 Operation.VI_ARG_DIGIT,
491 Operation.VI_ARG_DIGIT,
492 Operation.VI_ARG_DIGIT,
493 Operation.VI_ARG_DIGIT,
494 Operation.VI_ARG_DIGIT,
495 Operation.VI_ARG_DIGIT,
496 Operation.VI_ARG_DIGIT,
497 Operation.VI_ARG_DIGIT,
498 null,
499 Operation.VI_CHAR_SEARCH,
500 null,
501 Operation.VI_COMPLETE,
502 null,
503 Operation.VI_SEARCH,
504 null,
505 Operation.VI_APPEND_EOL,
506 Operation.VI_PREV_WORD,
507 Operation.VI_CHANGE_TO_EOL,
508 Operation.VI_DELETE_TO_EOL,
509 Operation.VI_END_WORD,
510 Operation.VI_CHAR_SEARCH,
511
512 Operation.VI_FETCH_HISTORY,
513 null,
514 Operation.VI_INSERT_BEG,
515 null,
516 null,
517 null,
518 null,
519 Operation.VI_SEARCH_AGAIN,
520 null,
521 Operation.VI_PUT,
522 null,
523
524 Operation.VI_REPLACE,
525 Operation.VI_KILL_WHOLE_LINE,
526 Operation.VI_CHAR_SEARCH,
527
528 Operation.REVERT_LINE,
529 null,
530 Operation.VI_NEXT_WORD,
531 Operation.VI_RUBOUT,
532 Operation.VI_YANK_TO,
533 null,
534 null,
535 Operation.VI_COMPLETE,
536 null,
537 Operation.VI_FIRST_PRINT,
538 Operation.VI_YANK_ARG,
539 Operation.VI_GOTO_MARK,
540 Operation.VI_APPEND_MODE,
541 Operation.VI_PREV_WORD,
542 Operation.VI_CHANGE_TO,
543 Operation.VI_DELETE_TO,
544 Operation.VI_END_WORD,
545 Operation.VI_CHAR_SEARCH,
546 null,
547 Operation.BACKWARD_CHAR,
548 Operation.VI_INSERTION_MODE,
549 Operation.NEXT_HISTORY,
550 Operation.PREVIOUS_HISTORY,
551 Operation.FORWARD_CHAR,
552 Operation.VI_SET_MARK,
553 Operation.VI_SEARCH_AGAIN,
554 null,
555 Operation.VI_PUT,
556 null,
557 Operation.VI_CHANGE_CHAR,
558 Operation.VI_SUBST,
559 Operation.VI_CHAR_SEARCH,
560 Operation.UNDO,
561 null,
562 Operation.VI_NEXT_WORD,
563 Operation.VI_DELETE,
564 Operation.VI_YANK_TO,
565 null,
566 null,
567 Operation.VI_COLUMN,
568 null,
569 Operation.VI_CHANGE_CASE,
570 Operation.VI_DELETE
571 };
572 System.arraycopy( low, 0, map, 0, low.length );
573 for (int i = 128; i < 256; i++) {
574 map[i] = null;
575 }
576 return new KeyMap(VI_MOVE, map, false);
577 }
578 }