Add basic emacs keybinding.

This commit is contained in:
Julian Viereck 2010-12-23 00:06:17 +01:00
commit b16bc6abc4
5 changed files with 217 additions and 23 deletions

View file

@ -38,6 +38,7 @@
define(function(require, exports, module) { define(function(require, exports, module) {
var lang = require("pilot/lang");
var canon = require("pilot/canon"); var canon = require("pilot/canon");
canon.addCommand({ canon.addCommand({
@ -249,5 +250,12 @@ canon.addCommand({
name: "indent", name: "indent",
exec: function(env, args, request) { env.editor.indent(); } exec: function(env, args, request) { env.editor.indent(); }
}); });
canon.addCommand({
name: "inserttext",
exec: function(env, args, request) {
env.editor.onTextInput(lang.stringRepeat(args.text || "",
args.times || 1));
}
});
}); });

View file

@ -42,10 +42,12 @@ var useragent = require("pilot/useragent");
var event = require("pilot/event"); var event = require("pilot/event");
var default_mac = require("ace/conf/keybindings/default_mac").bindings; var default_mac = require("ace/conf/keybindings/default_mac").bindings;
var default_win = require("ace/conf/keybindings/default_win").bindings; var default_win = require("ace/conf/keybindings/default_win").bindings;
var vim_mode = require("ace/mode/vim");
var canon = require("pilot/canon"); var canon = require("pilot/canon");
require("ace/commands/default_commands"); require("ace/commands/default_commands");
var vim_mode = require("ace/mode/vim");
var emacs_mode = require("ace/mode/emacs");
var KeyBinding = function(element, editor, config) { var KeyBinding = function(element, editor, config) {
this.setConfig(config); this.setConfig(config);
var data = { }; var data = { };
@ -60,13 +62,25 @@ var KeyBinding = function(element, editor, config) {
var hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) var hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0)
| (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0); | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0);
var key = (_self.keyNames[e.keyCode] || console.log(hashId, e.keyCode);
var key;
var keyCode = e.keyCode;
// TODO: Pressing the command key on Chrome/Mac causes the keyCode
// to be 91, which is the "[" character, without pressing this actually.
// Invastigate more further and make a proper work around.
if (hashId & 8 && keyCode == 91) {
key = "";
} else {
key = (_self.keyNames[e.keyCode] ||
String.fromCharCode(e.keyCode)).toLowerCase(); String.fromCharCode(e.keyCode)).toLowerCase();
}
var toExecute; var toExecute;
if (true) { if (true) {
var toExecute = var toExecute =
vim_mode.handleKeyboard(data, hashId, key, e); // vim_mode.handleKeyboard(data, hashId, key, e);
emacs_mode.handleKeyboard(data, hashId, key, e);
} }
// If there is nothing to execute yet, then use the default keymapping. // If there is nothing to execute yet, then use the default keymapping.

View file

@ -80,7 +80,7 @@ KeyboardStateMapper.prototype = {
if (hashId & 8) keyArray.push("Command"); if (hashId & 8) keyArray.push("Command");
if (hashId & 2) keyArray.push("Option"); if (hashId & 2) keyArray.push("Option");
if (hashId & 4) keyArray.push("Shift"); if (hashId & 4) keyArray.push("Shift");
keyArray.push(key); if (key) keyArray.push(key);
var symbolicName = keyArray.join("-").toLowerCase(); var symbolicName = keyArray.join("-").toLowerCase();
var bufferToUse = data.buffer + symbolicName; var bufferToUse = data.buffer + symbolicName;
@ -130,7 +130,7 @@ KeyboardStateMapper.prototype = {
if (binding.disallowMatches) { if (binding.disallowMatches) {
for (var i = 0; i < binding.disallowMatches.length; i++) { for (var i = 0; i < binding.disallowMatches.length; i++) {
if (!!match[binding.disallowMatches[i]]) { if (!!match[binding.disallowMatches[i]]) {
return true; return false;
} }
} }
} }
@ -172,10 +172,16 @@ KeyboardStateMapper.prototype = {
result.command = "null"; result.command = "null";
} }
console.log("KeyboardStateMapper#find", binding);
return true; return true;
}); });
return result.command ? result : false; if (result.command) {
return result;
} else {
data.buffer = "";
return false;
}
}, },
match: function(data, hashId, key) { match: function(data, hashId, key) {
@ -191,5 +197,32 @@ KeyboardStateMapper.prototype = {
} }
} }
/**
* This is a useful matching function and therefore is defined here so that
* users of KeyboardStateMapper can use it.
*
* @return boolean
* If no command key (Command|Option|Shift|Ctrl) is pressed, it
* returns true. If the only the Shift key is pressed + a character
* true is returned as well. Otherwise, false is returned.
* Summing up, the function returns true whenever the user typed
* a normal character on the keyboard and no shortcut.
*/
exports.matchCharacterOnly = function(buffer, hashId, key, symbolicName) {
// If no command keys are pressed, then catch the input.
if (hashId == 0) {
return true;
}
// If only the shift key is pressed and a character key, then
// catch that input as well.
else if ((hashId == 4) && key.length == 1) {
return true;
}
// Otherwise, we let the input got through.
else {
return false;
}
};
exports.KeyboardStateMapper = KeyboardStateMapper; exports.KeyboardStateMapper = KeyboardStateMapper;
}); });

152
lib/ace/mode/emacs.js Normal file
View file

@ -0,0 +1,152 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Skywriter.
*
* The Initial Developer of the Original Code is
* Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Julian Viereck (julian.viereck@gmail.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
var KeyboardStateMapper = require("ace/keyboardstate").KeyboardStateMapper;
var matchCharacterOnly = require("ace/keyboardstate").matchCharacterOnly;
var emacsState = {
start: [
{
key: "ctrl-x",
then: "c-x"
},
{
regex: [ "(?:command-([0-9]*))*", "(down|ctrl-n)" ],
exec: "golinedown",
params: [
{
name: "times",
match: 1,
type: "number",
defaultValue: 1
}
]
},
{
regex: [ "(?:command-([0-9]*))*", "(right|ctrl-f)" ],
exec: "gotoright",
params: [
{
name: "times",
match: 1,
type: "number",
defaultValue: 1
}
]
},
{
regex: [ "(?:command-([0-9]*))*", "(up|ctrl-p)" ],
exec: "golineup",
params: [
{
name: "times",
match: 1,
type: "number",
defaultValue: 1
}
]
},
{
regex: [ "(?:command-([0-9]*))*", "(left|ctrl-b)" ],
exec: "gotoleft",
params: [
{
name: "times",
match: 1,
type: "number",
defaultValue: 1
}
]
},
{
comment: "This binding matches all printable characters except numbers as long as they are no numbers and print them n times.",
regex: [ "(?:command-([0-9]*))", "([^0-9]+)*" ],
match: matchCharacterOnly,
exec: "inserttext",
params: [
{
name: "times",
match: 1,
type: "number",
defaultValue: "1"
},
{
name: "text",
match: 2
}
]
},
{
comment: "This binding matches numbers as long as there is no meta_number in the buffer.",
regex: [ "(command-[0-9]*)*", "([0-9]+)" ],
match: matchCharacterOnly,
disallowMatches: [ 1 ],
exec: "inserttext",
params: [
{
name: "text",
match: 2,
type: "text"
}
]
},
{
regex: [ "command-([0-9]*)", "(command-[0-9]|[0-9])" ],
comment: "Stops execution if the regex /meta_[0-9]+/ matches to avoid resetting the buffer."
}
],
"c-x": [
{
key: "ctrl-g",
then: "start"
},
{
key: "ctrl-s",
exec: "save",
then: "start"
}
]
};
var emacsKeyboardStateMapper = new KeyboardStateMapper(emacsState);
exports.handleKeyboard = function(data, hashId, key, e) {
return emacsKeyboardStateMapper.match(data, hashId, key);
}
});

View file

@ -38,6 +38,7 @@
define(function(require, exports, module) { define(function(require, exports, module) {
var KeyboardStateMapper = require("ace/keyboardstate").KeyboardStateMapper; var KeyboardStateMapper = require("ace/keyboardstate").KeyboardStateMapper;
var matchCharacterOnly = require("ace/keyboardstate").matchCharacterOnly;
var vimStates = { var vimStates = {
start: [ start: [
@ -95,21 +96,7 @@ var vimStates = {
}, },
{ {
comment: "Catch some keyboard input to stop it here", comment: "Catch some keyboard input to stop it here",
match: function(buffer, hashId, key, symbolicName) { match: matchCharacterOnly
// If no command keys are pressed, then catch the input.
if (hashId == 0) {
return true;
}
// If only the shift key is pressed and a character key, then
// catch that input as well.
else if ((hashId == 4) && key.length == 1) {
return true;
}
// Otherwise, we let the input got through.
else {
return false;
}
}
} }
], ],
insertMode: [ insertMode: [