/*
* Copyright (c) 2002-2016, 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 jline.console;
import java.util.
HashMap;
import java.util.
Map;
/**
* The KeyMap class contains all bindings from keys to operations.
*
* @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
* @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;
public
KeyMap(
String name) {
this(
name, new
Object[
KEYMAP_LENGTH]);
}
@
Deprecated
public
KeyMap(
String name, boolean
unused) {
this(
name);
}
protected
KeyMap(
String name,
Object[]
mapping) {
this.
mapping =
mapping;
this.
name =
name;
}
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");
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<
String,
KeyMap>
keyMaps() {
Map<
String,
KeyMap>
keyMaps = new
HashMap<
String,
KeyMap>();
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);
keyMaps.
put("vi",
viMov);
KeyMap viIns =
viInsertion();
bindArrowKeys(
viIns);
keyMaps.
put(
VI_INSERT,
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);
}
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);
}
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);
}
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);
}
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_BEGINNING_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);
}
}