in lib/ace/keyboard/emacs.js [236:341]
exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
// if keyCode == -1 a non-printable key was pressed, such as just
// control. Handling those is currently not supported in this handler
if (keyCode === -1) return undefined;
var editor = data.editor;
editor._signal("changeStatus");
// insertstring data.count times
if (hashId == -1) {
editor.pushEmacsMark();
if (data.count) {
var str = new Array(data.count + 1).join(key);
data.count = null;
return {command: "insertstring", args: str};
}
}
var modifier = eMods[hashId];
// CTRL + number / universalArgument for setting data.count
if (modifier == "c-" || data.count) {
var count = parseInt(key[key.length - 1]);
if (typeof count === 'number' && !isNaN(count)) {
data.count = Math.max(data.count, 0) || 0;
data.count = 10 * data.count + count;
return {command: "null"};
}
}
// this.commandKeyBinding maps key specs like "c-p" (for CTRL + P) to
// command objects, for lookup key needs to include the modifier
if (modifier) key = modifier + key;
// Key combos like CTRL+X H build up the data.keyChain
if (data.keyChain) key = data.keyChain += " " + key;
// Key combo prefixes get stored as "null" (String!) in this
// this.commandKeyBinding. When encountered no command is invoked but we
// buld up data.keyChain
var command = this.commandKeyBinding[key];
data.keyChain = command == "null" ? key : "";
// there really is no command
if (!command) return undefined;
// we pass b/c of key combo or universalArgument
if (command === "null") return {command: "null"};
if (command === "universalArgument") {
// if no number pressed emacs repeats action 4 times.
// minus sign is needed to allow next keypress to replace it
data.count = -4;
return {command: "null"};
}
// lookup command
// TODO extract special handling of markmode
// TODO special case command.command is really unnecessary, remove
var args;
if (typeof command !== "string") {
args = command.args;
if (command.command) command = command.command;
if (command === "goorselect") {
command = editor.emacsMark() ? args[1] : args[0];
args = null;
}
}
if (typeof command === "string") {
if (command === "insertstring" ||
command === "splitline" ||
command === "togglecomment") {
editor.pushEmacsMark();
}
command = this.commands[command] || editor.commands.commands[command];
if (!command) return undefined;
}
if (!command.readOnly && !command.isYank)
data.lastCommand = null;
if (!command.readOnly && editor.emacsMark())
editor.setEmacsMark(null);
if (data.count) {
var count = data.count;
data.count = 0;
if (!command || !command.handlesCount) {
return {
args: args,
command: {
exec: function(editor, args) {
for (var i = 0; i < count; i++)
command.exec(editor, args);
},
multiSelectAction: command.multiSelectAction
}
};
} else {
if (!args) args = {};
if (typeof args === 'object') args.count = count;
}
}
return {command: command, args: args};
};