templates/static/js/codemirror/keymap/vim.js in swagr-0.0.8 vs templates/static/js/codemirror/keymap/vim.js in swagr-0.0.10
- old
+ new
@@ -113,10 +113,14 @@
motionArgs: { forward: false, wordEnd: true, inclusive: true }},
{ keys: ['g', 'E'], type: 'motion',
motion: 'moveByWords',
motionArgs: { forward: false, wordEnd: true, bigWord: true,
inclusive: true }},
+ { keys: ['{'], type: 'motion', motion: 'moveByParagraph',
+ motionArgs: { forward: false }},
+ { keys: ['}'], type: 'motion', motion: 'moveByParagraph',
+ motionArgs: { forward: true }},
{ keys: ['Ctrl-f'], type: 'motion',
motion: 'moveByPage', motionArgs: { forward: true }},
{ keys: ['Ctrl-b'], type: 'motion',
motion: 'moveByPage', motionArgs: { forward: false }},
{ keys: ['g', 'g'], type: 'motion',
@@ -245,11 +249,11 @@
var SPECIAL_SYMBOLS = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\'';
var specialSymbols = SPECIAL_SYMBOLS.split('');
var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
'Esc', 'Home', 'End', 'PageUp', 'PageDown'];
var validMarks = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
- numbers);
+ numbers).concat(['<', '>']);
var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
numbers).concat('-\"'.split(''));
function isAlphabet(k) {
return alphabetRegex.test(k);
@@ -718,11 +722,15 @@
}
if (command.type == 'keyToEx') {
// Handle user defined Ex to Ex mappings
exCommandDispatcher.processCommand(cm, command.exArgs.input);
} else {
- showPrompt(cm, onPromptClose, ':');
+ if (vim.visualMode) {
+ showPrompt(cm, onPromptClose, ':', undefined, '\'<,\'>');
+ } else {
+ showPrompt(cm, onPromptClose, ':');
+ }
}
},
evalInput: function(cm, vim) {
// If the motion comand is set, execute both the operator and motion.
// Otherwise return.
@@ -804,10 +812,16 @@
}
// Need to set the cursor to clear the selection. Otherwise,
// CodeMirror can't figure out that we changed directions...
cm.setCursor(selectionStart);
cm.setSelection(selectionStart, selectionEnd);
+ updateMark(cm, vim, '<',
+ cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
+ : selectionEnd);
+ updateMark(cm, vim, '>',
+ cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
+ : selectionStart);
} else if (!operator) {
curEnd = clipCursorToContent(cm, curEnd);
cm.setCursor(curEnd.line, curEnd.ch);
}
}
@@ -921,10 +935,26 @@
cm.moveV((motionArgs.forward ? repeat : -repeat), 'page');
var curEnd = cm.getCursor();
cm.setCursor(curStart);
return curEnd;
},
+ moveByParagraph: function(cm, motionArgs) {
+ var line = cm.getCursor().line;
+ var repeat = motionArgs.repeat;
+ var inc = motionArgs.forward ? 1 : -1;
+ for (var i = 0; i < repeat; i++) {
+ if ((!motionArgs.forward && line === 0) ||
+ (motionArgs.forward && line == cm.lineCount() - 1)) {
+ break;
+ }
+ line += inc;
+ while (line !== 0 && line != cm.lineCount - 1 && cm.getLine(line)) {
+ line += inc;
+ }
+ }
+ return { line: line, ch: 0 };
+ },
moveByWords: function(cm, motionArgs) {
return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward,
!!motionArgs.wordEnd, !!motionArgs.bigWord);
},
moveTillCharacter: function(cm, motionArgs) {
@@ -1117,25 +1147,33 @@
cm.setSelection(curEnd, curStart);
} else {
cm.setSelection(curStart, curEnd);
}
} else {
+ curStart = cm.getCursor('anchor');
+ curEnd = cm.getCursor('head');
if (!vim.visualLine && actionArgs.linewise) {
// Shift-V pressed in characterwise visual mode. Switch to linewise
// visual mode instead of exiting visual mode.
vim.visualLine = true;
- curStart = cm.getCursor('anchor');
- curEnd = cm.getCursor('head');
curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
lineLength(cm, curStart.line);
curEnd.ch = cursorIsBefore(curStart, curEnd) ?
lineLength(cm, curEnd.line) : 0;
cm.setSelection(curStart, curEnd);
+ } else if (vim.visualLine && !actionArgs.linewise) {
+ // v pressed in linewise visual mode. Switch to characterwise visual
+ // mode instead of exiting visual mode.
+ vim.visualLine = false;
} else {
exitVisualMode(cm, vim);
}
}
+ updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
+ : curEnd);
+ updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
+ : curStart);
},
joinLines: function(cm, actionArgs, vim) {
var curStart, curEnd;
if (vim.visualMode) {
curStart = cm.getCursor('anchor');
@@ -1230,17 +1268,11 @@
setRegister: function(cm, actionArgs, vim) {
vim.inputState.registerName = actionArgs.selectedCharacter;
},
setMark: function(cm, actionArgs, vim) {
var markName = actionArgs.selectedCharacter;
- if (!inArray(markName, validMarks)) {
- return;
- }
- if (vim.marks[markName]) {
- vim.marks[markName].clear();
- }
- vim.marks[markName] = cm.setBookmark(cm.getCursor());
+ updateMark(cm, vim, markName, cm.getCursor());
},
replace: function(cm, actionArgs) {
var replaceWith = actionArgs.selectedCharacter;
var curStart = cm.getCursor();
var line = cm.getLine(curStart.line);
@@ -1634,10 +1666,20 @@
// to the column we want to go to.
var line = cm.getCursor().line;
return clipCursorToContent(cm, { line: line, ch: repeat - 1 });
}
+ function updateMark(cm, vim, markName, pos) {
+ if (!inArray(markName, validMarks)) {
+ return;
+ }
+ if (vim.marks[markName]) {
+ vim.marks[markName].clear();
+ }
+ vim.marks[markName] = cm.setBookmark(pos);
+ }
+
function charIdxInLine(start, line, character, forward, includeChar) {
// Search for char in line.
// motion_options: {forward, includeChar}
// If includeChar = true, include it too.
// If forward = true, search forward, else search backwards.
@@ -1802,10 +1844,16 @@
return this.marked;
},
setMarked: function(marked) {
this.marked = marked;
},
+ getOverlay: function() {
+ return this.searchOverlay;
+ },
+ setOverlay: function(overlay) {
+ this.searchOverlay = overlay;
+ },
isReversed: function() {
return getVimGlobalState().isReversed;
},
setReversed: function(reversed) {
getVimGlobalState().isReversed = reversed;
@@ -1813,13 +1861,13 @@
};
function getSearchState(cm) {
var vim = getVimState(cm);
return vim.searchState_ || (vim.searchState_ = new SearchState());
}
- function dialog(cm, text, shortText, callback) {
+ function dialog(cm, text, shortText, callback, initialValue) {
if (cm.openDialog) {
- cm.openDialog(text, callback, {bottom: true});
+ cm.openDialog(text, callback, { bottom: true, value: initialValue });
}
else {
callback(prompt(shortText, ""));
}
}
@@ -1897,13 +1945,14 @@
raw += '</span>';
}
return raw;
}
var searchPromptDesc = '(Javascript regexp)';
- function showPrompt(cm, onPromptClose, prefix, desc) {
+ function showPrompt(cm, onPromptClose, prefix, desc, initialValue) {
var shortText = (prefix || '') + ' ' + (desc || '');
- dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose);
+ dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose,
+ initialValue);
}
function regexEqual(r1, r2) {
if (r1 instanceof RegExp && r2 instanceof RegExp) {
var props = ["global", "multiline", "ignoreCase", "source"];
for (var i = 0; i < props.length; i++) {
@@ -1932,21 +1981,57 @@
clearSearchHighlight(cm);
highlightSearchMatches(cm, query);
state.setQuery(query);
});
}
+ function searchOverlay(query) {
+ return {
+ token: function(stream) {
+ var match = stream.match(query, false);
+ if (match) {
+ if (!stream.sol()) {
+ // Backtrack 1 to match \b
+ stream.backUp(1);
+ if (!query.exec(stream.next() + match[0])) {
+ stream.next();
+ return null;
+ }
+ }
+ stream.match(query);
+ return "searching";
+ }
+ while (!stream.eol()) {
+ stream.next();
+ if (stream.match(query, false)) break;
+ }
+ },
+ query: query
+ };
+ }
function highlightSearchMatches(cm, query) {
- // TODO: Highlight only text inside the viewport. Highlighting everything
- // is inefficient and expensive.
- if (cm.lineCount() < 2000) { // This is too expensive on big documents.
- var marked = [];
- for (var cursor = cm.getSearchCursor(query);
- cursor.findNext();) {
- marked.push(cm.markText(cursor.from(), cursor.to(),
- { className: 'CodeMirror-searching' }));
+ if (cm.addOverlay) {
+ var overlay = getSearchState(cm).getOverlay();
+ if (!overlay || query != overlay.query) {
+ if (overlay) {
+ cm.removeOverlay(overlay);
+ }
+ overlay = searchOverlay(query);
+ cm.addOverlay(overlay);
+ getSearchState(cm).setOverlay(overlay);
}
- getSearchState(cm).setMarked(marked);
+ } else {
+ // TODO: Highlight only text inside the viewport. Highlighting everything
+ // is inefficient and expensive.
+ if (cm.lineCount() < 2000) { // This is too expensive on big documents.
+ var marked = [];
+ for (var cursor = cm.getSearchCursor(query);
+ cursor.findNext();) {
+ marked.push(cm.markText(cursor.from(), cursor.to(),
+ { className: 'cm-searching' }));
+ }
+ getSearchState(cm).setMarked(marked);
+ }
}
}
function findNext(cm, prev, repeat) {
return cm.operation(function() {
var state = getSearchState(cm);
@@ -1976,51 +2061,93 @@
}
}
return cursor.from();
});}
function clearSearchHighlight(cm) {
- cm.operation(function() {
- var state = getSearchState(cm);
- if (!state.getQuery()) {
- return;
+ if (cm.addOverlay) {
+ cm.removeOverlay(getSearchState(cm).getOverlay());
+ getSearchState(cm).setOverlay(null);
+ } else {
+ cm.operation(function() {
+ var state = getSearchState(cm);
+ if (!state.getQuery()) {
+ return;
+ }
+ var marked = state.getMarked();
+ if (!marked) {
+ return;
+ }
+ for (var i = 0; i < marked.length; ++i) {
+ marked[i].clear();
+ }
+ state.setMarked(null);
+ });
+ }
+ }
+ /**
+ * Check if pos is in the specified range, INCLUSIVE.
+ * Range can be specified with 1 or 2 arguments.
+ * If the first range argument is an array, treat it as an array of line
+ * numbers. Match pos against any of the lines.
+ * If the first range argument is a number,
+ * if there is only 1 range argument, check if pos has the same line
+ * number
+ * if there are 2 range arguments, then check if pos is in between the two
+ * range arguments.
+ */
+ function isInRange(pos, start, end) {
+ if (typeof pos != 'number') {
+ // Assume it is a cursor position. Get the line number.
+ pos = pos.line;
+ }
+ if (start instanceof Array) {
+ return inArray(pos, start);
+ } else {
+ if (end) {
+ return (pos >= start && pos <= end);
+ } else {
+ return pos == start;
}
- var marked = state.getMarked();
- if (!marked) {
- return;
- }
- for (var i = 0; i < marked.length; ++i) {
- marked[i].clear();
- }
- state.setMarked(null);
- });}
+ }
+ }
// Ex command handling
// Care must be taken when adding to the default Ex command map. For any
// pair of commands that have a shared prefix, at least one of their
// shortNames must not match the prefix of the other command.
var defaultExCommandMap = [
{ name: 'map', type: 'builtIn' },
{ name: 'write', shortName: 'w', type: 'builtIn' },
{ name: 'undo', shortName: 'u', type: 'builtIn' },
- { name: 'redo', shortName: 'red', type: 'builtIn' }
+ { name: 'redo', shortName: 'red', type: 'builtIn' },
+ { name: 'substitute', shortName: 's', type: 'builtIn'}
];
- var ExCommandDispatcher = function() {
+ Vim.ExCommandDispatcher = function() {
this.buildCommandMap_();
};
- ExCommandDispatcher.prototype = {
+ Vim.ExCommandDispatcher.prototype = {
processCommand: function(cm, input) {
- var params = this.parseInput_(input);
+ var inputStream = new CodeMirror.StringStream(input);
+ var params = {};
+ params.input = input;
+ try {
+ this.parseInput_(cm, inputStream, params);
+ } catch(e) {
+ showConfirm(cm, e);
+ return;
+ }
var commandName;
if (!params.commandName) {
// If only a line range is defined, move to the line.
if (params.line !== undefined) {
commandName = 'move';
}
} else {
var command = this.matchCommand_(params.commandName);
if (command) {
commandName = command.name;
+ this.parseCommandArgs_(inputStream, params, command);
if (command.type == 'exToKey') {
// Handle Ex to Key mapping.
for (var i = 0; i < command.toKeys.length; i++) {
vim.handleKey(cm, command.toKeys[i]);
}
@@ -2036,41 +2163,67 @@
showConfirm(cm, 'Not an editor command ":' + input + '"');
return;
}
exCommands[commandName](cm, params);
},
- parseInput_: function(input) {
- var result = {};
- result.input = input;
- var idx = 0;
- // Trim preceding ':'.
- var colons = (/^:+/).exec(input);
- if (colons) {
- idx += colons[0].length;
- }
-
+ parseInput_: function(cm, inputStream, result) {
+ inputStream.eatWhile(':');
// Parse range.
- var numberMatch = (/^(\d+)/).exec(input.substring(idx));
- if (numberMatch) {
- result.line = parseInt(numberMatch[1], 10);
- idx += numberMatch[0].length;
+ if (inputStream.eat('%')) {
+ result.line = 0;
+ result.lineEnd = cm.lineCount() - 1;
+ } else {
+ result.line = this.parseLineSpec_(cm, inputStream);
+ if (result.line !== undefined && inputStream.eat(',')) {
+ result.lineEnd = this.parseLineSpec_(cm, inputStream);
+ }
}
// Parse command name.
- var commandMatch = (/^(\w+)/).exec(input.substring(idx));
+ var commandMatch = inputStream.match(/^(\w+)/);
if (commandMatch) {
result.commandName = commandMatch[1];
- idx += commandMatch[1].length;
+ } else {
+ result.commandName = inputStream.match(/.*/)[0];
}
+ return result;
+ },
+ parseLineSpec_: function(cm, inputStream) {
+ var numberMatch = inputStream.match(/^(\d+)/);
+ if (numberMatch) {
+ return parseInt(numberMatch[1], 10) - 1;
+ }
+ switch (inputStream.next()) {
+ case '.':
+ return cm.getCursor().line;
+ case '$':
+ return cm.lineCount() - 1;
+ case '\'':
+ var mark = getVimState(cm).marks[inputStream.next()];
+ if (mark && mark.find()) {
+ return mark.find().line;
+ } else {
+ throw "Mark not set";
+ }
+ break;
+ default:
+ inputStream.backUp(1);
+ return cm.getCursor().line;
+ }
+ },
+ parseCommandArgs_: function(inputStream, params, command) {
+ if (inputStream.eol()) {
+ return;
+ }
+ params.argString = inputStream.match(/.*/)[0];
// Parse command-line arguments
- var args = trim(input.substring(idx)).split(/\s+/);
+ var delim = command.argDelimiter || /\s+/;
+ var args = trim(params.argString).split(delim);
if (args.length && args[0]) {
- result.commandArgs = args;
+ params.args = args;
}
-
- return result;
},
matchCommand_: function(commandName) {
// Return the command in the command map that matches the shortest
// prefix of the passed in command name. The match is guaranteed to be
// unambiguous if the defaultExCommandMap's shortNames are set up
@@ -2093,13 +2246,13 @@
var key = command.shortName || command.name;
this.commandMap_[key] = command;
}
},
map: function(lhs, rhs) {
- if (lhs.charAt(0) == ':') {
+ if (lhs != ':' && lhs.charAt(0) == ':') {
var commandName = lhs.substring(1);
- if (rhs.charAt(0) == ':') {
+ if (rhs != ':' && rhs.charAt(0) == ':') {
// Ex to Ex mapping
this.commandMap_[commandName] = {
name: commandName,
type: 'exToEx',
toInput: rhs.substring(1)
@@ -2111,11 +2264,11 @@
type: 'exToKey',
toKeys: parseKeyString(rhs)
};
}
} else {
- if (rhs.charAt(0) == ':') {
+ if (rhs != ':' && rhs.charAt(0) == ':') {
// Key to Ex mapping.
defaultKeymap.unshift({
keys: parseKeyString(lhs),
type: 'keyToEx',
exArgs: { input: rhs.substring(1) }});
@@ -2170,11 +2323,11 @@
return keys;
}
var exCommands = {
map: function(cm, params) {
- var mapArgs = params.commandArgs;
+ var mapArgs = params.args;
if (!mapArgs || mapArgs.length < 2) {
if (cm) {
showConfirm(cm, 'Invalid mapping: ' + params.input);
}
return;
@@ -2185,10 +2338,70 @@
commandDispatcher.processMotion(cm, getVimState(cm), {
motion: 'moveToLineOrEdgeOfDocument',
motionArgs: { forward: false, explicitRepeat: true,
linewise: true, repeat: params.line }});
},
+ substitute: function(cm, params) {
+ var argString = params.argString;
+ var slashes = findUnescapedSlashes(argString);
+ if (slashes[0] !== 0) {
+ showConfirm(cm, 'Substitutions should be of the form ' +
+ ':s/pattern/replace/');
+ return;
+ }
+ var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
+ var replacePart = '';
+ var flagsPart;
+ var count;
+ if (slashes[1]) {
+ replacePart = argString.substring(slashes[1] + 1, slashes[2]);
+ }
+ if (slashes[2]) {
+ // After the 3rd slash, we can have flags followed by a space followed
+ // by count.
+ var trailing = argString.substring(slashes[2] + 1).split(' ');
+ flagsPart = trailing[0];
+ count = parseInt(trailing[1]);
+ }
+ if (flagsPart) {
+ regexPart = regexPart + '/' + flagsPart;
+ }
+ if (regexPart) {
+ // If regex part is empty, then use the previous query. Otherwise use
+ // the regex part as the new query.
+ updateSearchQuery(cm, regexPart, true /** ignoreCase */,
+ true /** smartCase */);
+ }
+ var state = getSearchState(cm);
+ var query = state.getQuery();
+ var lineStart = params.line || 0;
+ var lineEnd = params.lineEnd || lineStart;
+ if (count) {
+ lineStart = lineEnd;
+ lineEnd = lineStart + count - 1;
+ }
+ var startPos = clipCursorToContent(cm, { line: lineStart, ch: 0 });
+ function doReplace() {
+ for (var cursor = cm.getSearchCursor(query, startPos);
+ cursor.findNext() &&
+ isInRange(cursor.from(), lineStart, lineEnd);) {
+ var text = cm.getRange(cursor.from(), cursor.to());
+ var newText = text.replace(query, replacePart);
+ cursor.replace(newText);
+ }
+ var vim = getVimState(cm);
+ if (vim.visualMode) {
+ exitVisualMode(cm, vim);
+ }
+ }
+ if (cm.compoundChange) {
+ // Only exists in v2
+ cm.compoundChange(doReplace);
+ } else {
+ cm.operation(doReplace);
+ }
+ },
redo: CodeMirror.commands.redo,
undo: CodeMirror.commands.undo,
write: function(cm) {
if (CodeMirror.commands.save) {
// If a save command is defined, call it.
@@ -2198,10 +2411,10 @@
cm.save();
}
}
};
- var exCommandDispatcher = new ExCommandDispatcher();
+ var exCommandDispatcher = new Vim.ExCommandDispatcher();
// Register Vim with CodeMirror
function buildVimKeyMap() {
/**
* Handle the raw key event from CodeMirror. Translate the