commit
0e1d74622b
4 changed files with 169 additions and 63 deletions
|
|
@ -1800,7 +1800,7 @@ var Editor = function(renderer, session) {
|
|||
|
||||
this.$blockScrolling += 1;
|
||||
// todo: find a way to automatically exit multiselect mode
|
||||
this.exitMultiSelectMode && this.exitMultiSelectMode()
|
||||
this.exitMultiSelectMode && this.exitMultiSelectMode();
|
||||
this.moveCursorTo(lineNumber - 1, column || 0);
|
||||
this.$blockScrolling -= 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,8 @@ oop.inherits(IncrementalSearch, Search);
|
|||
if (reset) {
|
||||
e.moveCursorToPosition(this.$startPos);
|
||||
this.$currentPos = this.$startPos;
|
||||
} else {
|
||||
e.pushEmacsMark && e.pushEmacsMark(this.$startPos, false);
|
||||
}
|
||||
this.highlight(null);
|
||||
return Range.fromPoints(this.$currentPos, this.$currentPos);
|
||||
|
|
|
|||
|
|
@ -95,16 +95,36 @@ exports.handler.attach = function(editor) {
|
|||
$formerLineStart = editor.session.$useEmacsStyleLineStart;
|
||||
editor.session.$useEmacsStyleLineStart = true;
|
||||
|
||||
editor.session.$emacsMark = null;
|
||||
editor.session.$emacsMark = null; // the active mark
|
||||
editor.session.$emacsMarkRing = editor.session.$emacsMarkRing || [];
|
||||
|
||||
editor.emacsMarkMode = function() {
|
||||
editor.emacsMark = function() {
|
||||
return this.session.$emacsMark;
|
||||
}
|
||||
|
||||
editor.setEmacsMarkMode = function(p) {
|
||||
editor.setEmacsMark = function(p) {
|
||||
// to deactivate pass in a falsy value
|
||||
this.session.$emacsMark = p;
|
||||
}
|
||||
|
||||
editor.pushEmacsMark = function(p, activate) {
|
||||
var prevMark = this.session.$emacsMark;
|
||||
if (prevMark)
|
||||
this.session.$emacsMarkRing.push(prevMark);
|
||||
if (!p || activate) this.setEmacsMark(p)
|
||||
else this.session.$emacsMarkRing.push(p);
|
||||
}
|
||||
|
||||
editor.popEmacsMark = function() {
|
||||
var mark = this.emacsMark();
|
||||
if (mark) { this.setEmacsMark(null); return mark; }
|
||||
return this.session.$emacsMarkRing.pop();
|
||||
}
|
||||
|
||||
editor.getLastEmacsMark = function(p) {
|
||||
return this.session.$emacsMark || this.session.$emacsMarkRing.slice(-1)[0];
|
||||
}
|
||||
|
||||
editor.on("click", $resetMarkMode);
|
||||
editor.on("changeSession", $kbSessionChange);
|
||||
editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates;
|
||||
|
|
@ -112,6 +132,8 @@ exports.handler.attach = function(editor) {
|
|||
editor.commands.addCommands(commands);
|
||||
exports.handler.platform = editor.commands.platform;
|
||||
editor.$emacsModeHandler = this;
|
||||
editor.addEventListener('copy', this.onCopy);
|
||||
editor.addEventListener('paste', this.onPaste);
|
||||
};
|
||||
|
||||
exports.handler.detach = function(editor) {
|
||||
|
|
@ -122,6 +144,8 @@ exports.handler.detach = function(editor) {
|
|||
editor.removeEventListener("changeSession", $kbSessionChange);
|
||||
editor.unsetStyle("emacs-mode");
|
||||
editor.commands.removeCommands(commands);
|
||||
editor.removeEventListener('copy', this.onCopy);
|
||||
editor.removeEventListener('paste', this.onPaste);
|
||||
};
|
||||
|
||||
var $kbSessionChange = function(e) {
|
||||
|
|
@ -137,6 +161,8 @@ var $kbSessionChange = function(e) {
|
|||
|
||||
if (!e.session.hasOwnProperty('$emacsMark'))
|
||||
e.session.$emacsMark = null;
|
||||
if (!e.session.hasOwnProperty('$emacsMarkRing'))
|
||||
e.session.$emacsMarkRing = [];
|
||||
}
|
||||
|
||||
var $resetMarkMode = function(e) {
|
||||
|
|
@ -157,6 +183,17 @@ combinations.forEach(function(c) {
|
|||
eMods[hashId] = c.toLowerCase() + "-";
|
||||
});
|
||||
|
||||
exports.handler.onCopy = function(e, editor) {
|
||||
if (editor.$handlesEmacsOnCopy) return;
|
||||
editor.$handlesEmacsOnCopy = true;
|
||||
exports.handler.commands.killRingSave.exec(editor);
|
||||
delete editor.$handlesEmacsOnCopy;
|
||||
}
|
||||
|
||||
exports.handler.onPaste = function(e, editor) {
|
||||
editor.pushEmacsMark(editor.getCursorPosition());
|
||||
}
|
||||
|
||||
exports.handler.bindKey = function(key, command) {
|
||||
if (!key)
|
||||
return;
|
||||
|
|
@ -165,18 +202,27 @@ exports.handler.bindKey = function(key, command) {
|
|||
key.split("|").forEach(function(keyPart) {
|
||||
keyPart = keyPart.toLowerCase();
|
||||
ckb[keyPart] = command;
|
||||
keyPart = keyPart.split(" ")[0];
|
||||
if (!ckb[keyPart])
|
||||
ckb[keyPart] = "null";
|
||||
// register all partial key combos as null commands
|
||||
// to be able to activate key combos with arbitrary length
|
||||
// Example: if keyPart is "C-c C-l t" then "C-c C-l t" will
|
||||
// get command assigned and "C-c" and "C-c C-l" will get
|
||||
// a null command assigned in this.commmandKeyBinding. For
|
||||
// the lookup logic see handleKeyboard()
|
||||
var keyParts = keyPart.split(" ").slice(0,-1);
|
||||
keyParts.reduce(function(keyMapKeys, keyPart, i) {
|
||||
var prefix = keyMapKeys[i-1] ? keyMapKeys[i-1] + ' ' : '';
|
||||
return keyMapKeys.concat([prefix + keyPart]);
|
||||
}, []).forEach(function(keyPart) {
|
||||
if (!ckb[keyPart]) ckb[keyPart] = "null";
|
||||
});
|
||||
}, this);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
|
||||
var editor = data.editor;
|
||||
// insertstring data.count times
|
||||
if (hashId == -1) {
|
||||
editor.setEmacsMarkMode(null);
|
||||
editor.pushEmacsMark();
|
||||
if (data.count) {
|
||||
var str = Array(data.count + 1).join(key);
|
||||
data.count = null;
|
||||
|
|
@ -190,10 +236,15 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
|
|||
|
||||
// CTRL + number / universalArgument for setting data.count
|
||||
if (modifier == "c-" || data.universalArgument) {
|
||||
var prevCount = String(data.count || 0);
|
||||
var count = parseInt(key[key.length - 1]);
|
||||
if (count) {
|
||||
data.count = count;
|
||||
if (typeof count === 'number' && !isNaN(count)) {
|
||||
data.count = parseInt(prevCount + count);
|
||||
return {command: "null"};
|
||||
} else if (data.universalArgument) {
|
||||
// if no number pressed use emacs defaults for universalArgument
|
||||
// which is 4
|
||||
data.count = 4;
|
||||
}
|
||||
}
|
||||
data.universalArgument = false;
|
||||
|
|
@ -230,7 +281,7 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
|
|||
args = command.args;
|
||||
if (command.command) command = command.command;
|
||||
if (command === "goorselect") {
|
||||
command = editor.emacsMarkMode() ? args[1] : args[0];
|
||||
command = editor.emacsMark() ? args[1] : args[0];
|
||||
args = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -239,7 +290,7 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
|
|||
if (command === "insertstring" ||
|
||||
command === "splitline" ||
|
||||
command === "togglecomment") {
|
||||
editor.setEmacsMarkMode(null);
|
||||
editor.pushEmacsMark();
|
||||
}
|
||||
command = this.commands[command] || editor.commands.commands[command];
|
||||
if (!command) return undefined;
|
||||
|
|
@ -251,15 +302,20 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
|
|||
if (data.count) {
|
||||
var count = data.count;
|
||||
data.count = 0;
|
||||
return {
|
||||
args: args,
|
||||
command: {
|
||||
exec: function(editor, args) {
|
||||
for (var i = 0; i < count; i++)
|
||||
command.exec(editor, args);
|
||||
if (!command || !command.handlesCount) {
|
||||
return {
|
||||
args: args,
|
||||
command: {
|
||||
exec: function(editor, args) {
|
||||
for (var i = 0; i < count; i++)
|
||||
command.exec(editor, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
} else {
|
||||
if (!args) args = {}
|
||||
if (typeof args === 'object') args.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
return {command: command, args: args};
|
||||
|
|
@ -367,36 +423,70 @@ exports.handler.addCommands({
|
|||
selectRectangularRegion: function(editor) {
|
||||
editor.multiSelect.toggleBlockSelection();
|
||||
},
|
||||
setMark: function(editor) {
|
||||
// Emulate emacs highlighting behaviour in transient-mark-mode.
|
||||
// Sets mark-mode and clears current selection.
|
||||
// When mark is set, keyboard cursor movement commands become
|
||||
// selection modification commands. That is,
|
||||
// "goto" commands become "select" commands.
|
||||
// Any insertion or mouse click resets mark-mode.
|
||||
// setMark twice in a row at the same place resets markmode
|
||||
var markMode = editor.emacsMarkMode();
|
||||
if (markMode) {
|
||||
var cp = editor.getCursorPosition();
|
||||
if (editor.selection.isEmpty() &&
|
||||
markMode.row == cp.row && markMode.column == cp.column) {
|
||||
editor.setEmacsMarkMode(null);
|
||||
// console.log("Mark mode off");
|
||||
setMark: {
|
||||
exec: function(editor, args) {
|
||||
// Sets mark-mode and clears current selection.
|
||||
// When mark is set, keyboard cursor movement commands become
|
||||
// selection modification commands. That is,
|
||||
// "goto" commands become "select" commands.
|
||||
// Any insertion or mouse click resets mark-mode.
|
||||
// setMark twice in a row at the same place resets markmode
|
||||
if (args && args.count) {
|
||||
var mark = editor.popEmacsMark();
|
||||
mark && editor.selection.moveCursorToPosition(mark);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// turn on mark mode
|
||||
markMode = editor.getCursorPosition();
|
||||
editor.setEmacsMarkMode(markMode);
|
||||
editor.selection.setSelectionAnchor(markMode.row, markMode.column);
|
||||
},
|
||||
exchangePointAndMark: {
|
||||
exec: function(editor) {
|
||||
var range = editor.selection.getRange();
|
||||
editor.selection.setSelectionRange(range, !editor.selection.isBackwards());
|
||||
|
||||
var mark = editor.emacsMark(),
|
||||
transientMarkModeActive = true;
|
||||
|
||||
// if transientMarkModeActive then mark behavior is a little
|
||||
// different. Deactivate the mark when setMark is run with active
|
||||
// mark
|
||||
if (transientMarkModeActive && (mark || !editor.selection.isEmpty())) {
|
||||
editor.pushEmacsMark();
|
||||
editor.clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mark) {
|
||||
var cp = editor.getCursorPosition();
|
||||
if (editor.selection.isEmpty() &&
|
||||
mark.row == cp.row && mark.column == cp.column) {
|
||||
editor.pushEmacsMark();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// turn on mark mode
|
||||
mark = editor.getCursorPosition();
|
||||
editor.setEmacsMark(mark);
|
||||
editor.selection.setSelectionAnchor(mark.row, mark.column);
|
||||
},
|
||||
readonly: true,
|
||||
multiselectAction: "forEach"
|
||||
handlesCount: true,
|
||||
multiSelectAction: "forEach"
|
||||
},
|
||||
exchangePointAndMark: {
|
||||
exec: function(editor, args) {
|
||||
var sel = editor.selection;
|
||||
if (args.count) {
|
||||
var pos = editor.getCursorPosition();
|
||||
sel.clearSelection();
|
||||
sel.moveCursorToPosition(editor.popEmacsMark());
|
||||
editor.pushEmacsMark(pos);
|
||||
return;
|
||||
}
|
||||
var lastMark = editor.getLastEmacsMark();
|
||||
var range = sel.getRange();
|
||||
if (range.isEmpty()) {
|
||||
sel.selectToPosition(lastMark);
|
||||
return;
|
||||
}
|
||||
sel.setSelectionRange(range, !sel.isBackwards());
|
||||
},
|
||||
readonly: true,
|
||||
handlesCount: true,
|
||||
multiSelectAction: "forEach"
|
||||
},
|
||||
killWord: {
|
||||
exec: function(editor, dir) {
|
||||
|
|
@ -413,10 +503,10 @@ exports.handler.addCommands({
|
|||
editor.session.remove(range);
|
||||
editor.clearSelection();
|
||||
},
|
||||
multiselectAction: "forEach"
|
||||
multiSelectAction: "forEach"
|
||||
},
|
||||
killLine: function(editor) {
|
||||
editor.setEmacsMarkMode(null);
|
||||
editor.pushEmacsMark(null);
|
||||
var pos = editor.getCursorPosition();
|
||||
if (pos.column == 0 &&
|
||||
editor.session.doc.getLine(pos.row).length == 0) {
|
||||
|
|
@ -438,7 +528,7 @@ exports.handler.addCommands({
|
|||
editor.clearSelection();
|
||||
},
|
||||
yank: function(editor) {
|
||||
editor.onPaste(exports.killRing.get());
|
||||
editor.onPaste(exports.killRing.get() || '');
|
||||
editor.keyBinding.$data.lastCommand = "yank";
|
||||
},
|
||||
yankRotate: function(editor) {
|
||||
|
|
@ -448,16 +538,29 @@ exports.handler.addCommands({
|
|||
editor.onPaste(exports.killRing.rotate());
|
||||
editor.keyBinding.$data.lastCommand = "yank";
|
||||
},
|
||||
killRegion: function(editor) {
|
||||
exports.killRing.add(editor.getCopyText());
|
||||
editor.commands.byName.cut.exec(editor);
|
||||
killRegion: {
|
||||
exec: function(editor) {
|
||||
exports.killRing.add(editor.getCopyText());
|
||||
editor.commands.byName.cut.exec(editor);
|
||||
},
|
||||
readonly: true,
|
||||
multiSelectAction: "forEach"
|
||||
},
|
||||
killRingSave: function(editor) {
|
||||
exports.killRing.add(editor.getCopyText());
|
||||
killRingSave: {
|
||||
exec: function(editor) {
|
||||
exports.killRing.add(editor.getCopyText());
|
||||
setTimeout(function() {
|
||||
var sel = editor.selection,
|
||||
range = sel.getRange();
|
||||
editor.pushEmacsMark(sel.isBackwards() ? range.end : range.start);
|
||||
sel.clearSelection();
|
||||
}, 0);
|
||||
},
|
||||
readonly: true
|
||||
},
|
||||
keyboardQuit: function(editor) {
|
||||
editor.selection.clearSelection();
|
||||
editor.setEmacsMarkMode(null);
|
||||
editor.setEmacsMark(null);
|
||||
},
|
||||
focusCommandLine: function(editor, arg) {
|
||||
if (editor.showCommandLine)
|
||||
|
|
@ -478,8 +581,9 @@ exports.killRing = {
|
|||
if (this.$data.length > 30)
|
||||
this.$data.shift();
|
||||
},
|
||||
get: function() {
|
||||
return this.$data[this.$data.length - 1] || "";
|
||||
get: function(n) {
|
||||
n = n || 1;
|
||||
return this.$data.slice(this.$data.length-n, this.$data.length).reverse().join('\n');
|
||||
},
|
||||
pop: function() {
|
||||
if (this.$data.length > 1)
|
||||
|
|
|
|||
|
|
@ -514,14 +514,14 @@ var Editor = require("./editor").Editor;
|
|||
* @method Editor.exitMultiSelectMode
|
||||
**/
|
||||
this.exitMultiSelectMode = function() {
|
||||
if (this.inVirtualSelectionMode)
|
||||
if (!this.inMultiSelectMode || this.inVirtualSelectionMode)
|
||||
return;
|
||||
this.multiSelect.toSingleRange();
|
||||
};
|
||||
|
||||
this.getCopyText = function() {
|
||||
var text = "";
|
||||
if (this.inMultiSelectMode) {
|
||||
if (this.inMultiSelectMode && !this.inVirtualSelectionMode) {
|
||||
var ranges = this.multiSelect.rangeList.ranges;
|
||||
var buf = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
|
@ -550,10 +550,10 @@ var Editor = require("./editor").Editor;
|
|||
var lines = text.split(/\r\n|\r|\n/);
|
||||
var ranges = this.selection.rangeList.ranges;
|
||||
|
||||
if (lines.length > ranges.length || (lines.length <= 2 && !lines[1]))
|
||||
if (lines.length > ranges.length || lines.length < 2 || !lines[1])
|
||||
return this.commands.exec("insertstring", this, text);
|
||||
|
||||
for (var i = ranges.length; i--; ) {
|
||||
for (var i = ranges.length; i--;) {
|
||||
var range = ranges[i];
|
||||
if (!range.isEmpty())
|
||||
this.session.remove(range);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue