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