1 /*
   2  * Copyright (c) 2002-2012, the original author or authors.
   3  *
   4  * This software is distributable under the BSD license. See the terms of the
   5  * BSD license in the documentation provided with this software.
   6  *
   7  * http://www.opensource.org/licenses/bsd-license.php
   8  */
   9 package jdk.internal.jline.console;
  10 
  11 import java.util.HashMap;
  12 import java.util.Map;
  13 
  14 /**
  15  * The KeyMap class contains all bindings from keys to operations.
  16  *
  17  * @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
  18  * @since 2.6
  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         // MS-DOS
 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         // Windows
 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         // MINGW32
 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 //    public boolean isConvertMetaCharsToAscii() {
 209 //        return convertMetaCharsToAscii;
 210 //    }
 211 
 212 //    public void setConvertMetaCharsToAscii(boolean convertMetaCharsToAscii) {
 213 //        this.convertMetaCharsToAscii = convertMetaCharsToAscii;
 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                         // Control keys.
 255                         Operation.SET_MARK,                 /* Control-@ */
 256                         Operation.BEGINNING_OF_LINE,        /* Control-A */
 257                         Operation.BACKWARD_CHAR,            /* Control-B */
 258                         Operation.INTERRUPT,                /* Control-C */
 259                         Operation.EXIT_OR_DELETE_CHAR,      /* Control-D */
 260                         Operation.END_OF_LINE,              /* Control-E */
 261                         Operation.FORWARD_CHAR,             /* Control-F */
 262                         Operation.ABORT,                    /* Control-G */
 263                         Operation.BACKWARD_DELETE_CHAR,     /* Control-H */
 264                         Operation.COMPLETE,                 /* Control-I */
 265                         Operation.ACCEPT_LINE,              /* Control-J */
 266                         Operation.KILL_LINE,                /* Control-K */
 267                         Operation.CLEAR_SCREEN,             /* Control-L */
 268                         Operation.ACCEPT_LINE,              /* Control-M */
 269                         Operation.NEXT_HISTORY,             /* Control-N */
 270                         null,                               /* Control-O */
 271                         Operation.PREVIOUS_HISTORY,         /* Control-P */
 272                         Operation.QUOTED_INSERT,            /* Control-Q */
 273                         Operation.REVERSE_SEARCH_HISTORY,   /* Control-R */
 274                         Operation.FORWARD_SEARCH_HISTORY,   /* Control-S */
 275                         Operation.TRANSPOSE_CHARS,          /* Control-T */
 276                         Operation.UNIX_LINE_DISCARD,        /* Control-U */
 277                         Operation.QUOTED_INSERT,            /* Control-V */
 278                         Operation.UNIX_WORD_RUBOUT,         /* Control-W */
 279                         emacsCtrlX(),                       /* Control-X */
 280                         Operation.YANK,                     /* Control-Y */
 281                         null,                               /* Control-Z */
 282                         emacsMeta(),                        /* Control-[ */
 283                         null,                               /* Control-\ */
 284                         Operation.CHARACTER_SEARCH,         /* Control-] */
 285                         null,                               /* Control-^ */
 286                         Operation.UNDO,                     /* Control-_ */
 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; /* Ctrl-[ */
 308     public static final char CTRL_OB = (char) 27; /* Ctrl-[ */
 309     public static final char CTRL_CB = (char) 29; /* Ctrl-] */
 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                         // Control keys.
 374                         null,                               /* Control-@ */
 375                         Operation.SELF_INSERT,              /* Control-A */
 376                         Operation.SELF_INSERT,              /* Control-B */
 377                         Operation.SELF_INSERT,              /* Control-C */
 378                         Operation.VI_EOF_MAYBE,             /* Control-D */
 379                         Operation.SELF_INSERT,              /* Control-E */
 380                         Operation.SELF_INSERT,              /* Control-F */
 381                         Operation.SELF_INSERT,              /* Control-G */
 382                         Operation.BACKWARD_DELETE_CHAR,     /* Control-H */
 383                         Operation.COMPLETE,                 /* Control-I */
 384                         Operation.ACCEPT_LINE,              /* Control-J */
 385                         Operation.SELF_INSERT,              /* Control-K */
 386                         Operation.SELF_INSERT,              /* Control-L */
 387                         Operation.ACCEPT_LINE,              /* Control-M */
 388                         Operation.MENU_COMPLETE,            /* Control-N */
 389                         Operation.SELF_INSERT,              /* Control-O */
 390                         Operation.MENU_COMPLETE_BACKWARD,   /* Control-P */
 391                         Operation.SELF_INSERT,              /* Control-Q */
 392                         Operation.REVERSE_SEARCH_HISTORY,   /* Control-R */
 393                         Operation.FORWARD_SEARCH_HISTORY,   /* Control-S */
 394                         Operation.TRANSPOSE_CHARS,          /* Control-T */
 395                         Operation.UNIX_LINE_DISCARD,        /* Control-U */
 396                         Operation.QUOTED_INSERT,            /* Control-V */
 397                         Operation.UNIX_WORD_RUBOUT,         /* Control-W */
 398                         Operation.SELF_INSERT,              /* Control-X */
 399                         Operation.YANK,                     /* Control-Y */
 400                         Operation.SELF_INSERT,              /* Control-Z */
 401                         Operation.VI_MOVEMENT_MODE,         /* Control-[ */
 402                         Operation.SELF_INSERT,              /* Control-\ */
 403                         Operation.SELF_INSERT,              /* Control-] */
 404                         Operation.SELF_INSERT,              /* Control-^ */
 405                         Operation.UNDO,                     /* Control-_ */
 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                         // Control keys.
 419                         null,                               /* Control-@ */
 420                         null,                               /* Control-A */
 421                         null,                               /* Control-B */
 422                         Operation.INTERRUPT,                /* Control-C */
 423                         /*
 424                          * ^D is supposed to move down half a screen. In bash
 425                          * appears to be ignored.
 426                          */
 427                         Operation.VI_EOF_MAYBE,             /* Control-D */
 428                         Operation.EMACS_EDITING_MODE,       /* Control-E */
 429                         null,                               /* Control-F */
 430                         Operation.ABORT,                    /* Control-G */
 431                         Operation.BACKWARD_CHAR,            /* Control-H */
 432                         null,                               /* Control-I */
 433                         Operation.VI_MOVE_ACCEPT_LINE,      /* Control-J */
 434                         Operation.KILL_LINE,                /* Control-K */
 435                         Operation.CLEAR_SCREEN,             /* Control-L */
 436                         Operation.VI_MOVE_ACCEPT_LINE,      /* Control-M */
 437                         Operation.VI_NEXT_HISTORY,          /* Control-N */
 438                         null,                               /* Control-O */
 439                         Operation.VI_PREVIOUS_HISTORY,      /* Control-P */
 440                         /*
 441                          * My testing with readline is the ^Q is ignored.
 442                          * Maybe this should be null?
 443                          */
 444                         Operation.QUOTED_INSERT,            /* Control-Q */
 445 
 446                         /*
 447                          * TODO - Very broken.  While in forward/reverse
 448                          * history search the VI keyset should go out the
 449                          * window and we need to enter a very simple keymap.
 450                          */
 451                         Operation.REVERSE_SEARCH_HISTORY,   /* Control-R */
 452                         /* TODO */
 453                         Operation.FORWARD_SEARCH_HISTORY,   /* Control-S */
 454                         Operation.TRANSPOSE_CHARS,          /* Control-T */
 455                         Operation.UNIX_LINE_DISCARD,        /* Control-U */
 456                         /* TODO */
 457                         Operation.QUOTED_INSERT,            /* Control-V */
 458                         Operation.UNIX_WORD_RUBOUT,         /* Control-W */
 459                         null,                               /* Control-X */
 460                         /* TODO */
 461                         Operation.YANK,                     /* Control-Y */
 462                         null,                               /* Control-Z */
 463                         emacsMeta(),                        /* Control-[ */
 464                         null,                               /* Control-\ */
 465                         /* TODO */
 466                         Operation.CHARACTER_SEARCH,         /* Control-] */
 467                         null,                               /* Control-^ */
 468                         /* TODO */
 469                         Operation.UNDO,                     /* Control-_ */
 470                         Operation.FORWARD_CHAR,             /* SPACE */
 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                         /* TODO */
 481                         Operation.VI_COMPLETE,              /* * */
 482                         Operation.VI_NEXT_HISTORY,          /* + */
 483                         Operation.VI_CHAR_SEARCH,           /* , */
 484                         Operation.VI_PREVIOUS_HISTORY,      /* - */
 485                         /* TODO */
 486                         Operation.VI_REDO,                  /* . */
 487                         Operation.VI_SEARCH,                /* / */
 488                         Operation.VI_BEGNNING_OF_LINE_OR_ARG_DIGIT, /* 0 */
 489                         Operation.VI_ARG_DIGIT,             /* 1 */
 490                         Operation.VI_ARG_DIGIT,             /* 2 */
 491                         Operation.VI_ARG_DIGIT,             /* 3 */
 492                         Operation.VI_ARG_DIGIT,             /* 4 */
 493                         Operation.VI_ARG_DIGIT,             /* 5 */
 494                         Operation.VI_ARG_DIGIT,             /* 6 */
 495                         Operation.VI_ARG_DIGIT,             /* 7 */
 496                         Operation.VI_ARG_DIGIT,             /* 8 */
 497                         Operation.VI_ARG_DIGIT,             /* 9 */
 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,            /* A */
 506                         Operation.VI_PREV_WORD,             /* B */
 507                         Operation.VI_CHANGE_TO_EOL,         /* C */
 508                         Operation.VI_DELETE_TO_EOL,         /* D */
 509                         Operation.VI_END_WORD,              /* E */
 510                         Operation.VI_CHAR_SEARCH,           /* F */
 511                         /* I need to read up on what this does */
 512                         Operation.VI_FETCH_HISTORY,         /* G */
 513                         null,                               /* H */
 514                         Operation.VI_INSERT_BEG,            /* I */
 515                         null,                               /* J */
 516                         null,                               /* K */
 517                         null,                               /* L */
 518                         null,                               /* M */
 519                         Operation.VI_SEARCH_AGAIN,          /* N */
 520                         null,                               /* O */
 521                         Operation.VI_PUT,                   /* P */
 522                         null,                               /* Q */
 523                         /* TODO */
 524                         Operation.VI_REPLACE,               /* R */
 525                         Operation.VI_KILL_WHOLE_LINE,       /* S */
 526                         Operation.VI_CHAR_SEARCH,           /* T */
 527                         /* TODO */
 528                         Operation.REVERT_LINE,              /* U */
 529                         null,                               /* V */
 530                         Operation.VI_NEXT_WORD,             /* W */
 531                         Operation.VI_RUBOUT,                /* X */
 532                         Operation.VI_YANK_TO,               /* Y */
 533                         null,                               /* Z */
 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,           /* a */
 541                         Operation.VI_PREV_WORD,             /* b */
 542                         Operation.VI_CHANGE_TO,             /* c */
 543                         Operation.VI_DELETE_TO,             /* d */
 544                         Operation.VI_END_WORD,              /* e */
 545                         Operation.VI_CHAR_SEARCH,           /* f */
 546                         null,                               /* g */
 547                         Operation.BACKWARD_CHAR,            /* h */
 548                         Operation.VI_INSERTION_MODE,        /* i */
 549                         Operation.NEXT_HISTORY,             /* j */
 550                         Operation.PREVIOUS_HISTORY,         /* k */
 551                         Operation.FORWARD_CHAR,             /* l */
 552                         Operation.VI_SET_MARK,              /* m */
 553                         Operation.VI_SEARCH_AGAIN,          /* n */
 554                         null,                               /* o */
 555                         Operation.VI_PUT,                   /* p */
 556                         null,                               /* q */
 557                         Operation.VI_CHANGE_CHAR,           /* r */
 558                         Operation.VI_SUBST,                 /* s */
 559                         Operation.VI_CHAR_SEARCH,           /* t */
 560                         Operation.UNDO,                     /* u */
 561                         null,                               /* v */
 562                         Operation.VI_NEXT_WORD,             /* w */
 563                         Operation.VI_DELETE,                /* x */
 564                         Operation.VI_YANK_TO,               /* y */
 565                         null,                               /* z */
 566                         null,                               /* { */
 567                         Operation.VI_COLUMN,                /* | */
 568                         null,                               /* } */
 569                         Operation.VI_CHANGE_CASE,           /* ~ */
 570                         Operation.VI_DELETE                 /* DEL */
 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 }