--- /dev/null 2015-04-26 06:51:08.003313989 -0700 +++ new/jdk/src/jdk.jline/share/classes/jdk/internal/jline/console/KeyMap.java 2015-06-18 03:04:37.669613267 -0700 @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2002-2012, the original author or authors. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * http://www.opensource.org/licenses/bsd-license.php + */ +package jdk.internal.jline.console; + +import java.util.HashMap; +import java.util.Map; + +/** + * The KeyMap class contains all bindings from keys to operations. + * + * @author Guillaume Nodet + * @since 2.6 + */ +public class KeyMap { + + public static final String VI_MOVE = "vi-move"; + public static final String VI_INSERT = "vi-insert"; + public static final String EMACS = "emacs"; + public static final String EMACS_STANDARD = "emacs-standard"; + public static final String EMACS_CTLX = "emacs-ctlx"; + public static final String EMACS_META = "emacs-meta"; + + private static final int KEYMAP_LENGTH = 256; + + private static final Object NULL_FUNCTION = new Object(); + + private Object[] mapping = new Object[KEYMAP_LENGTH]; + private Object anotherKey = null; + private String name; + private boolean isViKeyMap; + + public KeyMap(String name, boolean isViKeyMap) { + this(name, new Object[KEYMAP_LENGTH], isViKeyMap); + } + + protected KeyMap(String name, Object[] mapping, boolean isViKeyMap) { + this.mapping = mapping; + this.name = name; + this.isViKeyMap = isViKeyMap; + } + + public boolean isViKeyMap() { + return isViKeyMap; + } + + public String getName() { + return name; + } + + public Object getAnotherKey() { + return anotherKey; + } + + public void from(KeyMap other) { + this.mapping = other.mapping; + this.anotherKey = other.anotherKey; + } + + public Object getBound( CharSequence keySeq ) { + if (keySeq != null && keySeq.length() > 0) { + KeyMap map = this; + for (int i = 0; i < keySeq.length(); i++) { + char c = keySeq.charAt(i); + if (c > 255) { + return Operation.SELF_INSERT; + } + if (map.mapping[c] instanceof KeyMap) { + if (i == keySeq.length() - 1) { + return map.mapping[c]; + } else { + map = (KeyMap) map.mapping[c]; + } + } else { + return map.mapping[c]; + } + } + } + return null; + } + + public void bindIfNotBound( CharSequence keySeq, Object function ) { + + bind (this, keySeq, function, true); + } + + public void bind( CharSequence keySeq, Object function ) { + + bind (this, keySeq, function, false); + } + + private static void bind( KeyMap map, CharSequence keySeq, Object function ) { + + bind (map, keySeq, function, false); + } + + private static void bind( KeyMap map, CharSequence keySeq, Object function, + boolean onlyIfNotBound ) { + + if (keySeq != null && keySeq.length() > 0) { + for (int i = 0; i < keySeq.length(); i++) { + char c = keySeq.charAt(i); + if (c >= map.mapping.length) { + return; + } + if (i < keySeq.length() - 1) { + if (!(map.mapping[c] instanceof KeyMap)) { + KeyMap m = new KeyMap("anonymous", false); + if (map.mapping[c] != Operation.DO_LOWERCASE_VERSION) { + m.anotherKey = map.mapping[c]; + } + map.mapping[c] = m; + } + map = (KeyMap) map.mapping[c]; + } else { + if (function == null) { + function = NULL_FUNCTION; + } + if (map.mapping[c] instanceof KeyMap) { + map.anotherKey = function; + } else { + Object op = map.mapping[c]; + if (onlyIfNotBound == false + || op == null + || op == Operation.DO_LOWERCASE_VERSION + || op == Operation.VI_MOVEMENT_MODE ) { + + } + + map.mapping[c] = function; + } + } + } + } + } + + public void setBlinkMatchingParen(boolean on) { + if (on) { + bind( "}", Operation.INSERT_CLOSE_CURLY ); + bind( ")", Operation.INSERT_CLOSE_PAREN ); + bind( "]", Operation.INSERT_CLOSE_SQUARE ); + } + } + + private static void bindArrowKeys(KeyMap map) { + + // MS-DOS + bind( map, "\033[0A", Operation.PREVIOUS_HISTORY ); + bind( map, "\033[0B", Operation.BACKWARD_CHAR ); + bind( map, "\033[0C", Operation.FORWARD_CHAR ); + bind( map, "\033[0D", Operation.NEXT_HISTORY ); + + // Windows + bind( map, "\340\000", Operation.KILL_WHOLE_LINE ); + bind( map, "\340\107", Operation.BEGINNING_OF_LINE ); + bind( map, "\340\110", Operation.PREVIOUS_HISTORY ); + bind( map, "\340\111", Operation.BEGINNING_OF_HISTORY ); + bind( map, "\340\113", Operation.BACKWARD_CHAR ); + bind( map, "\340\115", Operation.FORWARD_CHAR ); + bind( map, "\340\117", Operation.END_OF_LINE ); + bind( map, "\340\120", Operation.NEXT_HISTORY ); + bind( map, "\340\121", Operation.END_OF_HISTORY ); + bind( map, "\340\122", Operation.OVERWRITE_MODE ); + bind( map, "\340\123", Operation.DELETE_CHAR ); + + bind( map, "\000\107", Operation.BEGINNING_OF_LINE ); + bind( map, "\000\110", Operation.PREVIOUS_HISTORY ); + bind( map, "\000\111", Operation.BEGINNING_OF_HISTORY ); + bind( map, "\000\110", Operation.PREVIOUS_HISTORY ); + bind( map, "\000\113", Operation.BACKWARD_CHAR ); + bind( map, "\000\115", Operation.FORWARD_CHAR ); + bind( map, "\000\117", Operation.END_OF_LINE ); + bind( map, "\000\120", Operation.NEXT_HISTORY ); + bind( map, "\000\121", Operation.END_OF_HISTORY ); + bind( map, "\000\122", Operation.OVERWRITE_MODE ); + bind( map, "\000\123", Operation.DELETE_CHAR ); + + bind( map, "\033[A", Operation.PREVIOUS_HISTORY ); + bind( map, "\033[B", Operation.NEXT_HISTORY ); + bind( map, "\033[C", Operation.FORWARD_CHAR ); + bind( map, "\033[D", Operation.BACKWARD_CHAR ); + bind( map, "\033[H", Operation.BEGINNING_OF_LINE ); + bind( map, "\033[F", Operation.END_OF_LINE ); + + bind( map, "\033OA", Operation.PREVIOUS_HISTORY ); + bind( map, "\033OB", Operation.NEXT_HISTORY ); + bind( map, "\033OC", Operation.FORWARD_CHAR ); + bind( map, "\033OD", Operation.BACKWARD_CHAR ); + bind( map, "\033OH", Operation.BEGINNING_OF_LINE ); + bind( map, "\033OF", Operation.END_OF_LINE ); + + bind( map, "\033[1~", Operation.BEGINNING_OF_LINE); + bind( map, "\033[4~", Operation.END_OF_LINE); + bind( map, "\033[3~", Operation.DELETE_CHAR); + + // MINGW32 + bind( map, "\0340H", Operation.PREVIOUS_HISTORY ); + bind( map, "\0340P", Operation.NEXT_HISTORY ); + bind( map, "\0340M", Operation.FORWARD_CHAR ); + bind( map, "\0340K", Operation.BACKWARD_CHAR ); + } + +// public boolean isConvertMetaCharsToAscii() { +// return convertMetaCharsToAscii; +// } + +// public void setConvertMetaCharsToAscii(boolean convertMetaCharsToAscii) { +// this.convertMetaCharsToAscii = convertMetaCharsToAscii; +// } + + public static boolean isMeta( char c ) { + return c > 0x7f && c <= 0xff; + } + + public static char unMeta( char c ) { + return (char) (c & 0x7F); + } + + public static char meta( char c ) { + return (char) (c | 0x80); + } + + public static Map keyMaps() { + Map keyMaps = new HashMap(); + + KeyMap emacs = emacs(); + bindArrowKeys(emacs); + keyMaps.put(EMACS, emacs); + keyMaps.put(EMACS_STANDARD, emacs); + keyMaps.put(EMACS_CTLX, (KeyMap) emacs.getBound("\u0018")); + keyMaps.put(EMACS_META, (KeyMap) emacs.getBound("\u001b")); + + KeyMap viMov = viMovement(); + bindArrowKeys(viMov); + keyMaps.put(VI_MOVE, viMov); + keyMaps.put("vi-command", viMov); + + KeyMap viIns = viInsertion(); + bindArrowKeys(viIns); + keyMaps.put(VI_INSERT, viIns); + keyMaps.put("vi", viIns); + + return keyMaps; + } + + public static KeyMap emacs() { + Object[] map = new Object[KEYMAP_LENGTH]; + Object[] ctrl = new Object[] { + // Control keys. + Operation.SET_MARK, /* Control-@ */ + Operation.BEGINNING_OF_LINE, /* Control-A */ + Operation.BACKWARD_CHAR, /* Control-B */ + Operation.INTERRUPT, /* Control-C */ + Operation.EXIT_OR_DELETE_CHAR, /* Control-D */ + Operation.END_OF_LINE, /* Control-E */ + Operation.FORWARD_CHAR, /* Control-F */ + Operation.ABORT, /* Control-G */ + Operation.BACKWARD_DELETE_CHAR, /* Control-H */ + Operation.COMPLETE, /* Control-I */ + Operation.ACCEPT_LINE, /* Control-J */ + Operation.KILL_LINE, /* Control-K */ + Operation.CLEAR_SCREEN, /* Control-L */ + Operation.ACCEPT_LINE, /* Control-M */ + Operation.NEXT_HISTORY, /* Control-N */ + null, /* Control-O */ + Operation.PREVIOUS_HISTORY, /* Control-P */ + Operation.QUOTED_INSERT, /* Control-Q */ + Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ + Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ + Operation.TRANSPOSE_CHARS, /* Control-T */ + Operation.UNIX_LINE_DISCARD, /* Control-U */ + Operation.QUOTED_INSERT, /* Control-V */ + Operation.UNIX_WORD_RUBOUT, /* Control-W */ + emacsCtrlX(), /* Control-X */ + Operation.YANK, /* Control-Y */ + null, /* Control-Z */ + emacsMeta(), /* Control-[ */ + null, /* Control-\ */ + Operation.CHARACTER_SEARCH, /* Control-] */ + null, /* Control-^ */ + Operation.UNDO, /* Control-_ */ + }; + System.arraycopy( ctrl, 0, map, 0, ctrl.length ); + for (int i = 32; i < 256; i++) { + map[i] = Operation.SELF_INSERT; + } + map[DELETE] = Operation.BACKWARD_DELETE_CHAR; + return new KeyMap(EMACS, map, false); + } + + public static final char CTRL_D = (char) 4; + public static final char CTRL_G = (char) 7; + public static final char CTRL_H = (char) 8; + public static final char CTRL_I = (char) 9; + public static final char CTRL_J = (char) 10; + public static final char CTRL_M = (char) 13; + public static final char CTRL_R = (char) 18; + public static final char CTRL_S = (char) 19; + public static final char CTRL_U = (char) 21; + public static final char CTRL_X = (char) 24; + public static final char CTRL_Y = (char) 25; + public static final char ESCAPE = (char) 27; /* Ctrl-[ */ + public static final char CTRL_OB = (char) 27; /* Ctrl-[ */ + public static final char CTRL_CB = (char) 29; /* Ctrl-] */ + + public static final int DELETE = (char) 127; + + public static KeyMap emacsCtrlX() { + Object[] map = new Object[KEYMAP_LENGTH]; + map[CTRL_G] = Operation.ABORT; + map[CTRL_R] = Operation.RE_READ_INIT_FILE; + map[CTRL_U] = Operation.UNDO; + map[CTRL_X] = Operation.EXCHANGE_POINT_AND_MARK; + map['('] = Operation.START_KBD_MACRO; + map[')'] = Operation.END_KBD_MACRO; + for (int i = 'A'; i <= 'Z'; i++) { + map[i] = Operation.DO_LOWERCASE_VERSION; + } + map['e'] = Operation.CALL_LAST_KBD_MACRO; + map[DELETE] = Operation.KILL_LINE; + return new KeyMap(EMACS_CTLX, map, false); + } + + public static KeyMap emacsMeta() { + Object[] map = new Object[KEYMAP_LENGTH]; + map[CTRL_G] = Operation.ABORT; + map[CTRL_H] = Operation.BACKWARD_KILL_WORD; + map[CTRL_I] = Operation.TAB_INSERT; + map[CTRL_J] = Operation.VI_EDITING_MODE; + map[CTRL_M] = Operation.VI_EDITING_MODE; + map[CTRL_R] = Operation.REVERT_LINE; + map[CTRL_Y] = Operation.YANK_NTH_ARG; + map[CTRL_OB] = Operation.COMPLETE; + map[CTRL_CB] = Operation.CHARACTER_SEARCH_BACKWARD; + map[' '] = Operation.SET_MARK; + map['#'] = Operation.INSERT_COMMENT; + map['&'] = Operation.TILDE_EXPAND; + map['*'] = Operation.INSERT_COMPLETIONS; + map['-'] = Operation.DIGIT_ARGUMENT; + map['.'] = Operation.YANK_LAST_ARG; + map['<'] = Operation.BEGINNING_OF_HISTORY; + map['='] = Operation.POSSIBLE_COMPLETIONS; + map['>'] = Operation.END_OF_HISTORY; + map['?'] = Operation.POSSIBLE_COMPLETIONS; + for (int i = 'A'; i <= 'Z'; i++) { + map[i] = Operation.DO_LOWERCASE_VERSION; + } + map['\\'] = Operation.DELETE_HORIZONTAL_SPACE; + map['_'] = Operation.YANK_LAST_ARG; + map['b'] = Operation.BACKWARD_WORD; + map['c'] = Operation.CAPITALIZE_WORD; + map['d'] = Operation.KILL_WORD; + map['f'] = Operation.FORWARD_WORD; + map['l'] = Operation.DOWNCASE_WORD; + map['p'] = Operation.NON_INCREMENTAL_REVERSE_SEARCH_HISTORY; + map['r'] = Operation.REVERT_LINE; + map['t'] = Operation.TRANSPOSE_WORDS; + map['u'] = Operation.UPCASE_WORD; + map['y'] = Operation.YANK_POP; + map['~'] = Operation.TILDE_EXPAND; + map[DELETE] = Operation.BACKWARD_KILL_WORD; + return new KeyMap(EMACS_META, map, false); + } + + public static KeyMap viInsertion() { + Object[] map = new Object[KEYMAP_LENGTH]; + Object[] ctrl = new Object[] { + // Control keys. + null, /* Control-@ */ + Operation.SELF_INSERT, /* Control-A */ + Operation.SELF_INSERT, /* Control-B */ + Operation.SELF_INSERT, /* Control-C */ + Operation.VI_EOF_MAYBE, /* Control-D */ + Operation.SELF_INSERT, /* Control-E */ + Operation.SELF_INSERT, /* Control-F */ + Operation.SELF_INSERT, /* Control-G */ + Operation.BACKWARD_DELETE_CHAR, /* Control-H */ + Operation.COMPLETE, /* Control-I */ + Operation.ACCEPT_LINE, /* Control-J */ + Operation.SELF_INSERT, /* Control-K */ + Operation.SELF_INSERT, /* Control-L */ + Operation.ACCEPT_LINE, /* Control-M */ + Operation.MENU_COMPLETE, /* Control-N */ + Operation.SELF_INSERT, /* Control-O */ + Operation.MENU_COMPLETE_BACKWARD, /* Control-P */ + Operation.SELF_INSERT, /* Control-Q */ + Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ + Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ + Operation.TRANSPOSE_CHARS, /* Control-T */ + Operation.UNIX_LINE_DISCARD, /* Control-U */ + Operation.QUOTED_INSERT, /* Control-V */ + Operation.UNIX_WORD_RUBOUT, /* Control-W */ + Operation.SELF_INSERT, /* Control-X */ + Operation.YANK, /* Control-Y */ + Operation.SELF_INSERT, /* Control-Z */ + Operation.VI_MOVEMENT_MODE, /* Control-[ */ + Operation.SELF_INSERT, /* Control-\ */ + Operation.SELF_INSERT, /* Control-] */ + Operation.SELF_INSERT, /* Control-^ */ + Operation.UNDO, /* Control-_ */ + }; + System.arraycopy( ctrl, 0, map, 0, ctrl.length ); + for (int i = 32; i < 256; i++) { + map[i] = Operation.SELF_INSERT; + } + map[DELETE] = Operation.BACKWARD_DELETE_CHAR; + return new KeyMap(VI_INSERT, map, false); + } + + public static KeyMap viMovement() { + Object[] map = new Object[KEYMAP_LENGTH]; + Object[] low = new Object[] { + // Control keys. + null, /* Control-@ */ + null, /* Control-A */ + null, /* Control-B */ + Operation.INTERRUPT, /* Control-C */ + /* + * ^D is supposed to move down half a screen. In bash + * appears to be ignored. + */ + Operation.VI_EOF_MAYBE, /* Control-D */ + Operation.EMACS_EDITING_MODE, /* Control-E */ + null, /* Control-F */ + Operation.ABORT, /* Control-G */ + Operation.BACKWARD_CHAR, /* Control-H */ + null, /* Control-I */ + Operation.VI_MOVE_ACCEPT_LINE, /* Control-J */ + Operation.KILL_LINE, /* Control-K */ + Operation.CLEAR_SCREEN, /* Control-L */ + Operation.VI_MOVE_ACCEPT_LINE, /* Control-M */ + Operation.VI_NEXT_HISTORY, /* Control-N */ + null, /* Control-O */ + Operation.VI_PREVIOUS_HISTORY, /* Control-P */ + /* + * My testing with readline is the ^Q is ignored. + * Maybe this should be null? + */ + Operation.QUOTED_INSERT, /* Control-Q */ + + /* + * TODO - Very broken. While in forward/reverse + * history search the VI keyset should go out the + * window and we need to enter a very simple keymap. + */ + Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ + /* TODO */ + Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ + Operation.TRANSPOSE_CHARS, /* Control-T */ + Operation.UNIX_LINE_DISCARD, /* Control-U */ + /* TODO */ + Operation.QUOTED_INSERT, /* Control-V */ + Operation.UNIX_WORD_RUBOUT, /* Control-W */ + null, /* Control-X */ + /* TODO */ + Operation.YANK, /* Control-Y */ + null, /* Control-Z */ + emacsMeta(), /* Control-[ */ + null, /* Control-\ */ + /* TODO */ + Operation.CHARACTER_SEARCH, /* Control-] */ + null, /* Control-^ */ + /* TODO */ + Operation.UNDO, /* Control-_ */ + Operation.FORWARD_CHAR, /* SPACE */ + null, /* ! */ + null, /* " */ + Operation.VI_INSERT_COMMENT, /* # */ + Operation.END_OF_LINE, /* $ */ + Operation.VI_MATCH, /* % */ + Operation.VI_TILDE_EXPAND, /* & */ + null, /* ' */ + null, /* ( */ + null, /* ) */ + /* TODO */ + Operation.VI_COMPLETE, /* * */ + Operation.VI_NEXT_HISTORY, /* + */ + Operation.VI_CHAR_SEARCH, /* , */ + Operation.VI_PREVIOUS_HISTORY, /* - */ + /* TODO */ + Operation.VI_REDO, /* . */ + Operation.VI_SEARCH, /* / */ + Operation.VI_BEGNNING_OF_LINE_OR_ARG_DIGIT, /* 0 */ + Operation.VI_ARG_DIGIT, /* 1 */ + Operation.VI_ARG_DIGIT, /* 2 */ + Operation.VI_ARG_DIGIT, /* 3 */ + Operation.VI_ARG_DIGIT, /* 4 */ + Operation.VI_ARG_DIGIT, /* 5 */ + Operation.VI_ARG_DIGIT, /* 6 */ + Operation.VI_ARG_DIGIT, /* 7 */ + Operation.VI_ARG_DIGIT, /* 8 */ + Operation.VI_ARG_DIGIT, /* 9 */ + null, /* : */ + Operation.VI_CHAR_SEARCH, /* ; */ + null, /* < */ + Operation.VI_COMPLETE, /* = */ + null, /* > */ + Operation.VI_SEARCH, /* ? */ + null, /* @ */ + Operation.VI_APPEND_EOL, /* A */ + Operation.VI_PREV_WORD, /* B */ + Operation.VI_CHANGE_TO_EOL, /* C */ + Operation.VI_DELETE_TO_EOL, /* D */ + Operation.VI_END_WORD, /* E */ + Operation.VI_CHAR_SEARCH, /* F */ + /* I need to read up on what this does */ + Operation.VI_FETCH_HISTORY, /* G */ + null, /* H */ + Operation.VI_INSERT_BEG, /* I */ + null, /* J */ + null, /* K */ + null, /* L */ + null, /* M */ + Operation.VI_SEARCH_AGAIN, /* N */ + null, /* O */ + Operation.VI_PUT, /* P */ + null, /* Q */ + /* TODO */ + Operation.VI_REPLACE, /* R */ + Operation.VI_KILL_WHOLE_LINE, /* S */ + Operation.VI_CHAR_SEARCH, /* T */ + /* TODO */ + Operation.REVERT_LINE, /* U */ + null, /* V */ + Operation.VI_NEXT_WORD, /* W */ + Operation.VI_RUBOUT, /* X */ + Operation.VI_YANK_TO, /* Y */ + null, /* Z */ + null, /* [ */ + Operation.VI_COMPLETE, /* \ */ + null, /* ] */ + Operation.VI_FIRST_PRINT, /* ^ */ + Operation.VI_YANK_ARG, /* _ */ + Operation.VI_GOTO_MARK, /* ` */ + Operation.VI_APPEND_MODE, /* a */ + Operation.VI_PREV_WORD, /* b */ + Operation.VI_CHANGE_TO, /* c */ + Operation.VI_DELETE_TO, /* d */ + Operation.VI_END_WORD, /* e */ + Operation.VI_CHAR_SEARCH, /* f */ + null, /* g */ + Operation.BACKWARD_CHAR, /* h */ + Operation.VI_INSERTION_MODE, /* i */ + Operation.NEXT_HISTORY, /* j */ + Operation.PREVIOUS_HISTORY, /* k */ + Operation.FORWARD_CHAR, /* l */ + Operation.VI_SET_MARK, /* m */ + Operation.VI_SEARCH_AGAIN, /* n */ + null, /* o */ + Operation.VI_PUT, /* p */ + null, /* q */ + Operation.VI_CHANGE_CHAR, /* r */ + Operation.VI_SUBST, /* s */ + Operation.VI_CHAR_SEARCH, /* t */ + Operation.UNDO, /* u */ + null, /* v */ + Operation.VI_NEXT_WORD, /* w */ + Operation.VI_DELETE, /* x */ + Operation.VI_YANK_TO, /* y */ + null, /* z */ + null, /* { */ + Operation.VI_COLUMN, /* | */ + null, /* } */ + Operation.VI_CHANGE_CASE, /* ~ */ + Operation.VI_DELETE /* DEL */ + }; + System.arraycopy( low, 0, map, 0, low.length ); + for (int i = 128; i < 256; i++) { + map[i] = null; + } + return new KeyMap(VI_MOVE, map, false); + } +}