int modifiers = ModifierKeys.computeModifiers(signal);
modifiers = modifiers & ~ModifierKeys.SHIFT;
int strippedKeyDigest = CharCodeWithModifiers.computeKeyDigest(modifiers, letter);
JsoIntMap<MoveAction> movementKeysMapping = getMovementKeysMapping();
if (movementKeysMapping.containsKey(strippedKeyDigest)) {
MoveAction action = movementKeysMapping.get(strippedKeyDigest);
getScheme().getInputController().getSelection().move(action, inVisualMode);
return true;
}
if (tryAddNumericPrefix((char) letter)) {
return true;
}
numericPrefixText.setLength(0);
return false;
}
/**
* Paste events. These can come in from native Command+V or forced via
* holding a local clipboard/buffer of any text copied within the editor.
*/
@Override
public boolean onDefaultPaste(SignalEvent signal, String text) {
handlePaste(text, true);
return true;
}
};
addMode(Modes.COMMAND, commandMode);
/*
* Command+Alt+V - Switch from Vim to Default Scheme
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.ACTION | ModifierKeys.ALT, 'v') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
getInputController().setActiveInputScheme(getInputController().nativeScheme);
return true;
}
});
/*
* ESC, ACTION+[ - reset state in command mode
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, KeyCodeMap.ESC) {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
switchMode(Modes.COMMAND);
return true;
}
});
commandMode.addShortcut(new EventShortcut(ModifierKeys.ACTION, '[') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
switchMode(Modes.COMMAND);
return true;
}
});
/*
* TODO: extract common visual mode switching code.
*/
/*
* v - Switch to visual mode (character)
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'v') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
setStatus("-- VISUAL (char) --");
inVisualMode = true;
visualMoveUnit = MoveUnit.CHARACTER;
// select the character the cursor is over now
SelectionModel selectionModel = getInputController().getEditor().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
int cursorColumn = selectionModel.getCursorColumn();
selectionModel.setSelection(cursorLineInfo, cursorColumn, cursorLineInfo, cursorColumn + 1);
return true;
}
});
/*
* V - Switch to visual mode (line)
*/
/*
* TODO: Doesn't exactly match vim's visual-line mode, force
* selections of entire lines.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'V') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
setStatus("-- VISUAL (line) --");
inVisualMode = true;
visualMoveUnit = MoveUnit.LINE;
// move cursor to beginning of current line, select to column 0 of next
// line
SelectionModel selectionModel = getInputController().getEditor().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
LineInfo nextLineInfo =
new LineInfo(cursorLineInfo.line().getNextLine(), cursorLineInfo.number() + 1);
selectionModel.setSelection(cursorLineInfo, 0, nextLineInfo, 0);
return true;
}
});
/*
* i - Switch to insert mode
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'i') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
switchMode(Modes.INSERT);
return true;
}
});
/*
* A - Jump to end of line, enter insert mode.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'A') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getEditor().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
int lastColumn = LineUtils.getLastCursorColumn(cursorLineInfo.line());
selectionModel.setCursorPosition(cursorLineInfo, lastColumn);
switchMode(Modes.INSERT);
return true;
}
});
/*
* O - Insert line above, enter insert mode.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'O') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getEditor().getSelection();
Document document = getInputController().getEditor().getDocument();
Line cursorLine = selectionModel.getCursorLine();
int cursorLineNumber = selectionModel.getCursorLineNumber();
document.insertText(cursorLine, 0, "\n");
selectionModel.setCursorPosition(new LineInfo(cursorLine, cursorLineNumber), 0);
switchMode(Modes.INSERT);
return true;
}
});
/*
* o - Insert line below, enter insert mode.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'o') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getEditor().getSelection();
Document document = getInputController().getEditor().getDocument();
Line cursorLine = selectionModel.getCursorLine();
int cursorLineNumber = selectionModel.getCursorLineNumber();
document.insertText(cursorLine, LineUtils.getLastCursorColumn(cursorLine), "\n");
selectionModel.setCursorPosition(new LineInfo(cursorLine.getNextLine(),
cursorLineNumber + 1), 0);
switchMode(Modes.INSERT);
return true;
}
});
/*
* : - Switch to colon capture mode for commands.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, ':') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
switchMode(Modes.COMMAND_CAPTURE);
return true;
}
});
/*
* "/" - Switch to search mode.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, '/') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
switchMode(Modes.SEARCH_CAPTURE);
return true;
}
});
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, '*') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getEditor().getSelection();
String word =
TextUtils.getWordAtColumn(selectionModel.getCursorLine().getText(),
selectionModel.getCursorColumn());
if (word == null) {
return true;
}
switchMode(Modes.SEARCH_CAPTURE);
searchTerm.append(word);
doPartialSearch();
drawSearchTerm();
return true;
}
});
/*
* Movement
*/
/*
* ^,0 - Move to first character in line.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, '^') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getEditor().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
selectionModel.setCursorPosition(cursorLineInfo, 0);
return true;
}
});
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, '0') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
if (tryAddNumericPrefix('0')) {
return true;
}
SelectionModel selectionModel = getInputController().getEditor().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
selectionModel.setCursorPosition(cursorLineInfo, 0);
return true;
}
});
/*
* $ - Move to end of line.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, '$') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getEditor().getSelection();
Line cursorLine = selectionModel.getCursorLine();
LineInfo cursorLineInfo = new LineInfo(cursorLine, selectionModel.getCursorLineNumber());
selectionModel.setCursorPosition(cursorLineInfo, LineUtils.getLastCursorColumn(cursorLine));
return true;
}
});
/*
* w - move the cursor to the first character of the next word.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'w') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
String text = selectionModel.getCursorLine().getText();
int column = selectionModel.getCursorColumn();
column = TextUtils.moveByWord(text, column, true, false);
if (column == -1) {
Line cursorLine = cursorLineInfo.line().getNextLine();
if (cursorLine != null) {
cursorLineInfo = new LineInfo(cursorLine, cursorLineInfo.number() + 1);
column = 0;
} else {
column = LineUtils.getLastCursorColumn(cursorLine); // at last character
// in document
}
}
selectionModel.setCursorPosition(cursorLineInfo, column);
return true;
}
});
/*
* b - move the cursor to the first character of the previous word.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'b') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
String text = selectionModel.getCursorLine().getText();
int column = selectionModel.getCursorColumn();
column = TextUtils.moveByWord(text, column, false, false);
if (column == -1) {
Line cursorLine = cursorLineInfo.line().getPreviousLine();
if (cursorLine != null) {
cursorLineInfo = new LineInfo(cursorLine, cursorLineInfo.number() - 1);
column = LineUtils.getLastCursorColumn(cursorLine);
} else {
column = 0; // at first character in document
}
}
selectionModel.setCursorPosition(cursorLineInfo, column);
return true;
}
});
/*
* e - move the cursor to the last character of the next word.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'e') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
String text = selectionModel.getCursorLine().getText();
int column = selectionModel.getCursorColumn();
column = TextUtils.moveByWord(text, column, true, true);
if (column == -1) {
Line cursorLine = cursorLineInfo.line().getNextLine();
if (cursorLine != null) {
cursorLineInfo = new LineInfo(cursorLine, cursorLineInfo.number() + 1);
column = 0;
} else {
// at the last character in the document
column = LineUtils.getLastCursorColumn(cursorLine);
}
}
selectionModel.setCursorPosition(cursorLineInfo, column);
return true;
}
});
/*
* % - jump to the next matching {}, [] or () character if the cursor is
* over one of the two.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, '%') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
final SelectionModel selectionModel = getInputController().getSelection();
Document document = getInputController().getEditor().getDocument();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
String text = selectionModel.getCursorLine().getText();
final char cursorChar = text.charAt(selectionModel.getCursorColumn());
final char searchChar;
final boolean searchingForward = OPENING_GROUPS.indexOf(cursorChar) >= 0;
final Position searchingTo;
if (searchingForward) {
searchChar = CLOSING_GROUPS.charAt(OPENING_GROUPS.indexOf(cursorChar));
searchingTo =
new Position(new LineInfo(document.getLastLine(), document.getLastLineNumber()),
document.getLastLine().length());
} else if (CLOSING_GROUPS.indexOf(cursorChar) >= 0) {
searchChar = OPENING_GROUPS.charAt(CLOSING_GROUPS.indexOf(cursorChar));
searchingTo = new Position(new LineInfo(document.getFirstLine(), 0), 0);
} else {
return true; // not on a valid starting character
}
Position startingPosition = new Position(cursorLineInfo, selectionModel.getCursorColumn()
+ (searchingForward ? 0 : 1));
PositionUtils.visit(new LineUtils.LineVisitor() {
// keep a stack to match the correct corresponding bracket
ScopeMatcher scopeMatcher = new ScopeMatcher(searchingForward, cursorChar, searchChar);
@Override
public boolean accept(Line line, int lineNumber, int beginColumn, int endColumn) {
int column;
String text = line.getText().substring(beginColumn, endColumn);
column = scopeMatcher.searchNextLine(text);
if (column >= 0) {
selectionModel
.setCursorPosition(new LineInfo(line, lineNumber), column + beginColumn);
return false;
}
return true;
}
}, startingPosition, searchingTo);
return true;
}
});
/*
* } - next paragraph.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, '}') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
int lineNumber = cursorLineInfo.number();
boolean skippingEmptyLines = true;
Line line;
for (line = cursorLineInfo.line(); line.getNextLine() != null; line = line.getNextLine(),
lineNumber++) {
String text = line.getText();
text = text.substring(0, text.length() - (text.endsWith("\n") ? 1 : 0));
boolean isEmptyLine = text.trim().length() > 0;
if (skippingEmptyLines) {
// check if this line is empty
if (isEmptyLine) {
skippingEmptyLines = false; // non-empty line
}
} else {
// check if this line is not empty
if (!isEmptyLine) {
break;
}
}
}
selectionModel.setCursorPosition(new LineInfo(line, lineNumber), 0);
return true;
}
});
/*
* TODO: merge both paragraph searching blocks together.
*/
/*
* { - previous paragraph.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, '{') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getSelection();
LineInfo cursorLineInfo =
new LineInfo(selectionModel.getCursorLine(), selectionModel.getCursorLineNumber());
int lineNumber = cursorLineInfo.number();
boolean skippingEmptyLines = true;
Line line;
for (line = cursorLineInfo.line(); line.getPreviousLine() != null; line =
line.getPreviousLine(), lineNumber--) {
String text = line.getText();
text = text.substring(0, text.length() - (text.endsWith("\n") ? 1 : 0));
if (skippingEmptyLines) {
// check if this line is empty
if (text.trim().length() > 0) {
skippingEmptyLines = false; // non-empty line
}
} else {
// check if this line is not empty
if (text.trim().length() > 0) {
// not empty, continue
} else {
break;
}
}
}
selectionModel.setCursorPosition(new LineInfo(line, lineNumber), 0);
return true;
}
});
/*
* Cmd+u - page up.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.ACTION, 'u') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
getInputController().getSelection().move(MoveAction.PAGE_UP, inVisualMode);
return true;
}
});
/*
* Cmd+d - page down.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.ACTION, 'd') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
getInputController().getSelection().move(MoveAction.PAGE_DOWN, inVisualMode);
return true;
}
});
/*
* Ngg - move cursor to line N, or first line by default.
*/
commandMode.addShortcut(new StreamShortcut("gg") {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
moveCursorToLine(getPrefixValue(), true);
return true;
}
});
/*
* NG - move cursor to line N, or last line by default.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'G') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
moveCursorToLine(getPrefixValue(), false);
return true;
}
});
/*
* Text manipulation
*/
/*
* x - Delete one character to right of cursor, or the current selection.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'x') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
scheme.getInputController().deleteCharacter(true);
return true;
}
});
/*
* X - Delete one character to left of cursor.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'X') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
scheme.getInputController().deleteCharacter(false);
return true;
}
});
/*
* p - Paste after cursor.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'p') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
if (clipboard != null && clipboard.length() > 0) {
handlePaste(clipboard, true);
}
return true;
}
});
/*
* P - Paste before cursor.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'P') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
if (clipboard != null && clipboard.length() > 0) {
handlePaste(clipboard, false);
}
return true;
}
});
/*
* Nyy - Copy N lines. If there is already a selection, copy that instead.
*/
commandMode.addShortcut(new StreamShortcut("yy") {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
SelectionModel selectionModel = getInputController().getEditor().getSelection();
if (selectionModel.hasSelection()) {
isLineCopy = (visualMoveUnit == MoveUnit.LINE);
} else {
int numLines = getPrefixValue();
if (numLines <= 0) {
numLines = 1;
}
selectNextNLines(numLines);
isLineCopy = false;
}
Preconditions.checkState(selectionModel.hasSelection());
getInputController().prepareForCopy();
Position[] selectionRange = selectionModel.getSelectionRange(true);
clipboard =
LineUtils.getText(selectionRange[0].getLine(), selectionRange[0].getColumn(),
selectionRange[1].getLine(), selectionRange[1].getColumn());
selectionModel.deselect();
switchMode(Modes.COMMAND);
return false;
}
});
/*
* Ndd - Cut N lines.
*/
commandMode.addShortcut(new StreamShortcut("dd") {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
int numLines = getPrefixValue();
if (numLines <= 0) {
numLines = 1;
}
SelectionModel selectionModel = getInputController().getEditor().getSelection();
selectNextNLines(numLines);
Preconditions.checkState(selectionModel.hasSelection());
getInputController().prepareForCopy();
Position[] selectionRange = selectionModel.getSelectionRange(true);
clipboard =
LineUtils.getText(selectionRange[0].getLine(), selectionRange[0].getColumn(),
selectionRange[1].getLine(), selectionRange[1].getColumn());
selectionModel.deleteSelection(getInputController().getEditorDocumentMutator());
return false;
}
});
/*
* >> - indent line.
*/
commandMode.addShortcut(new StreamShortcut(">>") {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
scheme.getInputController().indentSelection();
return true;
}
});
/*
* << - dedent line.
*/
commandMode.addShortcut(new StreamShortcut("<<") {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
scheme.getInputController().dedentSelection();
return true;
}
});
/*
* u - Undo.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'u') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
scheme.getInputController().getEditor().undo();
return true;
}
});
/*
* ACTION+r - Redo.
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.ACTION, 'r') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
scheme.getInputController().getEditor().redo();
return true;
}
});
/**
* n - next search match
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'n') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
doSearch(true);
return true;
}
});
/**
* N - previous search match
*/
commandMode.addShortcut(new EventShortcut(ModifierKeys.NONE, 'N') {
@Override
public boolean event(InputScheme scheme, SignalEvent event) {
doSearch(false);
return true;
}
});
insertMode = new InputMode() {
@Override
public void setup() {
setStatus("-- INSERT --");
}
@Override
public void teardown() {
}
@Override
public boolean onDefaultInput(SignalEvent signal, char character) {
int letter = KeyCodeMap.getKeyFromEvent(signal);
int modifiers = ModifierKeys.computeModifiers(signal);
modifiers = modifiers & ~ModifierKeys.SHIFT;
int strippedKeyDigest = CharCodeWithModifiers.computeKeyDigest(modifiers, letter);
JsoIntMap<MoveAction> movementKeysMapping = DefaultScheme.MOVEMENT_KEYS_MAPPING;
if (movementKeysMapping.containsKey(strippedKeyDigest)) {
MoveAction action = movementKeysMapping.get(strippedKeyDigest);
getScheme().getInputController().getSelection().move(action, false);
return true;
}
InputController input = getInputController();