org.crsh.console.Mode Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.crsh.console;
import org.crsh.util.Utils;
import java.io.IOException;
import java.util.logging.Logger;
/**
* The current mode of the editor state machine. It decodes a command line operation according
* to the current status and its possible state and provide an editor action that will modify the
* state of the editor.
*
* @author Julien Viet
*/
public abstract class Mode extends EditorAction {
/** The logger. */
private static final Logger log = Logger.getLogger(Mode.class.getName());
public abstract String getKeyMap();
public abstract String toString();
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
editor.console.setMode(this);
}
/**
* Transform a key stroke into a editor action. If no action must be taken, null should be returned.
*
* @param keyStroke the key stroke
* @return the editor action
*/
public EditorAction on(KeyStroke keyStroke) {
String message = "Operation " + keyStroke.operation + " not mapped in " + getClass().getSimpleName() + " mode " + this;
log.warning(message);
return null;
}
public static final Mode EMACS = new Mode() {
@Override
public final String getKeyMap() {
return "emacs";
}
@Override
public EditorAction on(KeyStroke keyStroke) {
switch (keyStroke.operation) {
case SELF_INSERT:
return new InsertKey(keyStroke.sequence);
case VI_EDITING_MODE:
return VI_INSERT;
case BACKWARD_DELETE_CHAR:
return EditorAction.DELETE_PREV_CHAR;
case BACKWARD_CHAR:
return EditorAction.LEFT;
case FORWARD_CHAR:
return EditorAction.RIGHT;
case DELETE_CHAR:
return EditorAction.DELETE_NEXT_CHAR;
case BACKWARD_WORD:
return EditorAction.MOVE_PREV_WORD_AT_BEGINNING;
case FORWARD_WORD:
return EditorAction.MOVE_NEXT_WORD_AFTER_END;
case BEGINNING_OF_LINE:
return EditorAction.MOVE_BEGINNING;
case EXIT_OR_DELETE_CHAR:
return EditorAction.EOF_MAYBE;
case END_OF_LINE:
return EditorAction.MOVE_END;
case COMPLETE:
return EditorAction.COMPLETE;
case ACCEPT_LINE:
return EditorAction.ENTER;
case KILL_LINE:
return EditorAction.DELETE_END;
case BACKWARD_KILL_LINE:
return EditorAction.DELETE_BEGINNING;
case PREVIOUS_HISTORY:
return EditorAction.HISTORY_PREV;
case NEXT_HISTORY:
return EditorAction.HISTORY_NEXT;
case TRANSPOSE_CHARS:
return EditorAction.TRANSPOSE_CHARS;
case UNIX_LINE_DISCARD:
return EditorAction.UNIX_LINE_DISCARD;
case UNIX_WORD_RUBOUT:
return EditorAction.DELETE_PREV_WORD;
case BACKWARD_KILL_WORD:
return EditorAction.DELETE_PREV_WORD;
case INSERT_COMMENT:
return EditorAction.INSERT_COMMENT;
case BEGINNING_OF_HISTORY:
return EditorAction.HISTORY_FIRST;
case END_OF_HISTORY:
return EditorAction.HISTORY_LAST;
case INTERRUPT:
return EditorAction.INTERRUPT;
case CLEAR_SCREEN:
return EditorAction.CLS;
case YANK:
return EditorAction.PASTE_AFTER;
case KILL_WORD:
return EditorAction.DELETE_NEXT_WORD;
case DO_LOWERCASE_VERSION:
case ABORT:
case EXCHANGE_POINT_AND_MARK:
case QUOTED_INSERT:
case REVERSE_SEARCH_HISTORY:
case FORWARD_SEARCH_HISTORY:
case CHARACTER_SEARCH:
case UNDO:
case RE_READ_INIT_FILE:
case START_KBD_MACRO:
case END_KBD_MACRO:
case CALL_LAST_KBD_MACRO:
case TAB_INSERT:
case REVERT_LINE:
case YANK_NTH_ARG:
case CHARACTER_SEARCH_BACKWARD:
case SET_MARK:
case TILDE_EXPAND:
case INSERT_COMPLETIONS:
case DIGIT_ARGUMENT:
case YANK_LAST_ARG:
case POSSIBLE_COMPLETIONS:
case DELETE_HORIZONTAL_SPACE:
case CAPITALIZE_WORD:
case DOWNCASE_WORD:
case NON_INCREMENTAL_REVERSE_SEARCH_HISTORY:
case TRANSPOSE_WORDS:
case UPCASE_WORD:
case YANK_POP:
// Not yet implemented
default:
return super.on(keyStroke);
}
}
@Override
public String toString() {
return "Mode.EMACS";
}
};
public static final Mode VI_INSERT = new Mode() {
@Override
public final String getKeyMap() {
return "vi-insert";
}
@Override
public EditorAction on(KeyStroke keyStroke) {
switch (keyStroke.operation) {
case VI_MOVEMENT_MODE:
return VI_MOVE.then(EditorAction.LEFT);
case FORWARD_CHAR:
return EditorAction.RIGHT;
case BACKWARD_CHAR:
return EditorAction.LEFT;
case VI_NEXT_WORD:
return EditorAction.MOVE_NEXT_WORD_AT_BEGINNING;
case VI_EOF_MAYBE:
return EditorAction.EOF_MAYBE;
case SELF_INSERT:
return new InsertKey(keyStroke.sequence);
case BACKWARD_DELETE_CHAR:
return EditorAction.DELETE_PREV_CHAR;
case COMPLETE:
return EditorAction.COMPLETE;
case ACCEPT_LINE:
return EditorAction.ENTER;
case TRANSPOSE_CHARS:
return EditorAction.TRANSPOSE_CHARS;
case UNIX_LINE_DISCARD:
return EditorAction.UNIX_LINE_DISCARD;
case UNIX_WORD_RUBOUT:
return EditorAction.DELETE_PREV_WORD;
case INTERRUPT:
return EditorAction.INTERRUPT;
case PREVIOUS_HISTORY:
return EditorAction.HISTORY_PREV;
case NEXT_HISTORY:
return EditorAction.HISTORY_NEXT;
case BEGINNING_OF_HISTORY:
return EditorAction.HISTORY_FIRST;
case END_OF_HISTORY:
return EditorAction.HISTORY_LAST;
case YANK:
case MENU_COMPLETE:
case MENU_COMPLETE_BACKWARD:
case REVERSE_SEARCH_HISTORY:
case FORWARD_SEARCH_HISTORY:
case QUOTED_INSERT:
case UNDO:
// Not yet implemented
default:
return super.on(keyStroke);
}
}
@Override
public String toString() {
return "Mode.VI_INSERT";
}
};
public static final Mode VI_MOVE = new Mode() {
@Override
public final String getKeyMap() {
return "vi-move";
}
@Override
public EditorAction on(KeyStroke keyStroke) {
int[] buffer = keyStroke.sequence;
switch (keyStroke.operation) {
case VI_MOVE_ACCEPT_LINE:
return EditorAction.ENTER;
case VI_INSERTION_MODE:
return VI_INSERT;
case VI_INSERT_BEG:
return EditorAction.MOVE_BEGINNING.then(VI_INSERT);
case VI_INSERT_COMMENT:
return EditorAction.INSERT_COMMENT;
case BACKWARD_DELETE_CHAR:
return EditorAction.DELETE_PREV_CHAR;
case VI_DELETE:
return EditorAction.DELETE_NEXT_CHAR;
case KILL_LINE:
return EditorAction.DELETE_END;
case BACKWARD_KILL_LINE:
return EditorAction.DELETE_BEGINNING;
case VI_DELETE_TO_EOL:
return EditorAction.DELETE_END;
case VI_DELETE_TO:
return DELETE_TO;
case VI_NEXT_WORD:
return EditorAction.MOVE_NEXT_WORD_AT_BEGINNING;
case BACKWARD_WORD:
return EditorAction.MOVE_PREV_WORD_AT_BEGINNING;
case VI_CHANGE_TO_EOL:
return EMACS.then(EditorAction.DELETE_END).then(VI_INSERT);
case VI_CHANGE_TO:
return CHANGE_TO;
case VI_YANK_TO:
return YANK_TO;
case VI_ARG_DIGIT:
Digit digit = new Digit();
digit.count = buffer[0] - '0';
return digit;
case VI_APPEND_MODE:
// That's a trick to let the cursor go to the end of the line
// then we set to VI_INSERT
return EMACS.then(EditorAction.RIGHT).then(VI_INSERT);
case VI_BEGNNING_OF_LINE_OR_ARG_DIGIT:
return EditorAction.MOVE_BEGINNING;
case FORWARD_CHAR:
return EditorAction.RIGHT;
case TRANSPOSE_CHARS:
return EditorAction.TRANSPOSE_CHARS;
case UNIX_LINE_DISCARD:
return EditorAction.UNIX_LINE_DISCARD;
case UNIX_WORD_RUBOUT:
return EditorAction.DELETE_PREV_WORD;
case END_OF_LINE:
return EditorAction.MOVE_END;
case VI_PREV_WORD:
return EditorAction.MOVE_PREV_WORD_AT_BEGINNING;
case BACKWARD_CHAR:
return EditorAction.LEFT;
case VI_END_WORD:
return EditorAction.MOVE_NEXT_WORD_BEFORE_END;
case VI_CHANGE_CASE:
return EditorAction.CHANGE_CASE;
case VI_KILL_WHOLE_LINE:
return EditorAction.DELETE_LINE.then(VI_INSERT);
case VI_PUT:
return EditorAction.PASTE_AFTER;
case VI_CHANGE_CHAR:
return new ChangeChar(1);
case INTERRUPT:
return EditorAction.INTERRUPT;
case VI_SEARCH:
// Unmapped
return null;
case PREVIOUS_HISTORY:
return EditorAction.HISTORY_PREV;
case NEXT_HISTORY:
return EditorAction.HISTORY_NEXT;
case BEGINNING_OF_HISTORY:
return EditorAction.HISTORY_FIRST;
case END_OF_HISTORY:
return EditorAction.HISTORY_LAST;
case CLEAR_SCREEN:
return EditorAction.CLS;
default:
return super.on(keyStroke);
}
}
@Override
public String toString() {
return "Mode.VI_MOVE";
}
};
public static final Mode DELETE_TO = new Mode() {
@Override
public String getKeyMap() {
return "vi-move";
}
@Override
public EditorAction on(KeyStroke keyStroke) {
switch (keyStroke.operation) {
case BACKWARD_CHAR:
return EditorAction.DELETE_PREV_CHAR.then(VI_MOVE);
case FORWARD_CHAR:
return EditorAction.DELETE_NEXT_CHAR.then(VI_MOVE);
case END_OF_LINE:
return EditorAction.DELETE_END.then(VI_MOVE);
case VI_NEXT_WORD:
return EditorAction.DELETE_UNTIL_NEXT_WORD.then(VI_MOVE);
case VI_DELETE_TO:
return EditorAction.DELETE_LINE.then(VI_MOVE);
case INTERRUPT:
return EditorAction.INTERRUPT.then(VI_MOVE);
default:
return VI_MOVE;
}
}
@Override
public String toString() {
return "Mode.DELETE_TO";
}
};
public static final Mode CHANGE_TO = new Mode() {
@Override
public String getKeyMap() {
return "vi-move";
}
@Override
public EditorAction on(KeyStroke keyStroke) {
switch (keyStroke.operation) {
case BACKWARD_CHAR:
return EditorAction.DELETE_PREV_CHAR.then(VI_INSERT);
case END_OF_LINE:
return EMACS.then(EditorAction.DELETE_END).then(VI_INSERT);
case VI_NEXT_WORD:
return EditorAction.DELETE_NEXT_WORD.then(VI_INSERT);
case VI_CHANGE_TO:
return EditorAction.DELETE_LINE.then(VI_INSERT);
case INTERRUPT:
return EditorAction.INTERRUPT.then(VI_MOVE);
default:
return VI_MOVE;
}
}
@Override
public String toString() {
return "Mode.CHANGE_TO";
}
};
public static final Mode YANK_TO = new Mode() {
@Override
public String getKeyMap() {
return "vi-move";
}
@Override
public EditorAction on(KeyStroke keyStroke) {
switch (keyStroke.operation) {
case VI_YANK_TO:
return EditorAction.COPY.then(VI_MOVE);
case END_OF_LINE:
return COPY_END_OF_LINE.then(VI_MOVE);
case VI_BEGNNING_OF_LINE_OR_ARG_DIGIT:
return COPY_BEGINNING_OF_LINE.then(VI_MOVE);
case VI_NEXT_WORD:
return EditorAction.COPY_NEXT_WORD.then(VI_MOVE);
case VI_FIRST_PRINT:
return EditorAction.COPY_PREV_WORD.then(VI_MOVE);
case INTERRUPT:
return EditorAction.INTERRUPT.then(VI_MOVE);
default:
return super.on(keyStroke);
}
}
@Override
public String toString() {
return "Mode.YANK_TO";
}
};
public static class ChangeChar extends Mode {
@Override
public String getKeyMap() {
return "vi-insert"; // We use insert for ESC
}
/** / */
final int count;
public ChangeChar(int count) {
this.count = count;
}
@Override
public EditorAction on(KeyStroke keyStroke) {
switch (keyStroke.operation) {
case VI_MOVEMENT_MODE: // ESC
return VI_MOVE;
case INTERRUPT:
return EditorAction.INTERRUPT.then(VI_MOVE);
default:
return new EditorAction.ChangeChars(count, keyStroke.sequence[0]).then(VI_MOVE);
}
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof ChangeChar) {
ChangeChar that = (ChangeChar)obj;
return count == that.count;
} else {
return false;
}
}
@Override
public String toString() {
return "Mode.ChangeChat[count=" + count + "]";
}
}
public static class Digit extends Mode {
/** . */
int count = 0;
/** . */
Character to = null; // null | d:delete-to
public Digit(int count) {
this.count = count;
}
public Digit() {
this(0);
}
public int getCount() {
return count;
}
public Character getTo() {
return to;
}
@Override
public String getKeyMap() {
return "vi-move";
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof Digit) {
Digit that = (Digit)obj;
return count == that.count && Utils.equals(to, that.to);
} else {
return false;
}
}
@Override
public String toString() {
return "Mode.Digit[count=" + count + ",to=" + to + "]";
}
@Override
public EditorAction on(KeyStroke keyStroke) {
switch (keyStroke.operation) {
case VI_ARG_DIGIT:
count = count * 10 + keyStroke.sequence[0] - '0';
return null;
case BACKWARD_CHAR:
if (to == null) {
return EditorAction.LEFT.repeat(count).then(VI_MOVE);
} else if (to == 'd') {
return EditorAction.DELETE_PREV_CHAR.repeat(count).then(VI_MOVE);
} else if (to == 'c') {
return EditorAction.DELETE_PREV_CHAR.repeat(count).then(VI_INSERT);
} else if (to == 'y') {
// Not implemented
return VI_MOVE;
} else {
throw new AssertionError();
}
case FORWARD_CHAR:
if (to == null) {
return EditorAction.RIGHT.repeat(count).then(VI_MOVE);
} else if (to == 'd') {
return EditorAction.DELETE_NEXT_CHAR.repeat(count).then(VI_MOVE);
} else if (to == 'c') {
return EditorAction.DELETE_NEXT_CHAR.repeat(count).then(VI_INSERT);
} else if (to == 'y') {
throw new UnsupportedOperationException("Not yet handled");
} else {
return super.on(keyStroke);
}
case VI_NEXT_WORD:
if (to == null) {
return EditorAction.MOVE_NEXT_WORD_AT_BEGINNING.repeat(count).then(VI_MOVE);
} else if (to == 'd') {
return EditorAction.DELETE_UNTIL_NEXT_WORD.repeat(count).then(VI_MOVE);
} else if (to == 'c') {
return EditorAction.DELETE_NEXT_WORD.repeat(count).then(VI_INSERT);
} else {
return super.on(keyStroke);
}
case VI_PREV_WORD:
if (to == null) {
return EditorAction.MOVE_PREV_WORD_AT_END.repeat(count).then(VI_MOVE);
} else {
super.on(keyStroke);
}
case VI_END_WORD:
if (to == null) {
return EditorAction.MOVE_NEXT_WORD_BEFORE_END.repeat(count).then(VI_MOVE);
} else {
super.on(keyStroke);
}
case BACKWARD_DELETE_CHAR:
if (to == null) {
return EditorAction.DELETE_PREV_CHAR.repeat(count).then(VI_MOVE);
} else {
return super.on(keyStroke);
}
case VI_CHANGE_CASE:
if (to == null) {
return EditorAction.CHANGE_CASE.repeat(count).then(VI_MOVE);
} else {
return super.on(keyStroke);
}
case VI_DELETE:
if (to == null) {
return new EditorAction.DeleteNextChars(count).then(VI_MOVE);
} else {
return super.on(keyStroke);
}
case VI_DELETE_TO:
if (to != null) {
throw new UnsupportedOperationException("Not yet handled");
}
to = 'd';
return null;
case VI_CHANGE_TO:
if (to != null) {
throw new UnsupportedOperationException("Not yet handled");
}
to = 'c';
return null;
case VI_YANK_TO:
return YANK_TO;
case VI_CHANGE_CHAR:
if (to != null) {
throw new UnsupportedOperationException("Not yet handled");
}
return new ChangeChar(count);
case INTERRUPT:
return EditorAction.INTERRUPT.then(VI_MOVE);
default:
return VI_MOVE;
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy