Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First iteration of porting Bespin's keyboardmapping over to ace.

  • Loading branch information...
commit 1cba46b8d3340e17244157aede38d36970e3354b 1 parent 1d45d8b
@jviereck jviereck authored
View
12 lib/ace/commands/default_commands.js
@@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
+ * 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
@@ -40,6 +41,11 @@ define(function(require, exports, module) {
var canon = require("pilot/canon");
canon.addCommand({
+ name: "null",
+ exec: function(env, args, request) { }
+});
+
+canon.addCommand({
name: "selectall",
exec: function(env, args, request) { env.editor.getSelection().selectAll(); }
});
@@ -113,7 +119,7 @@ canon.addCommand({
});
canon.addCommand({
name: "golineup",
- exec: function(env, args, request) { env.editor.navigateUp(); }
+ exec: function(env, args, request) { env.editor.navigateUp(args.times); }
});
canon.addCommand({
name: "copylinesdown",
@@ -136,8 +142,8 @@ canon.addCommand({
exec: function(env, args, request) { env.editor.getSelection().selectDown(); }
});
canon.addCommand({
- name: "godown",
- exec: function(env, args, request) { env.editor.navigateDown(); }
+ name: "golinedown",
+ exec: function(env, args, request) { env.editor.navigateDown(args.times); }
});
canon.addCommand({
name: "selectwordleft",
View
2  lib/ace/conf/keybindings/default_mac.js
@@ -60,7 +60,7 @@ exports.bindings = {
"selecttoend": "Command-Shift-Down",
"gotoend": "Command-End|Command-Down",
"selectdown": "Shift-Down",
- "godown": "Down",
+ "golinedown": "Down",
"selectwordleft": "Option-Shift-Left",
"gotowordleft": "Option-Left",
"selecttolinestart": "Command-Shift-Left",
View
2  lib/ace/conf/keybindings/default_win.js
@@ -60,7 +60,7 @@ exports.bindings = {
"selecttoend": "Alt-Shift-Down",
"gotoend": "Ctrl-End|Ctrl-Down",
"selectdown": "Shift-Down",
- "godown": "Down",
+ "golinedown": "Down",
"selectwordleft": "Ctrl-Shift-Left",
"gotowordleft": "Ctrl-Left",
"selecttolinestart": "Alt-Shift-Left",
View
22 lib/ace/editor.js
@@ -857,9 +857,9 @@ var Editor =function(renderer, doc) {
this.$updateDesiredColumn(column);
};
- this.navigateUp = function() {
+ this.navigateUp = function(times) {
this.selection.clearSelection();
- this.selection.moveCursorBy(-1, 0);
+ this.selection.moveCursorBy(-(times || 1), 0);
if (this.$desiredColumn) {
var cursor = this.getCursorPosition();
@@ -868,9 +868,9 @@ var Editor =function(renderer, doc) {
}
};
- this.navigateDown = function() {
+ this.navigateDown = function(times) {
this.selection.clearSelection();
- this.selection.moveCursorBy(1, 0);
+ this.selection.moveCursorBy(times || 1, 0);
if (this.$desiredColumn) {
var cursor = this.getCursorPosition();
@@ -884,24 +884,30 @@ var Editor =function(renderer, doc) {
this.$desiredColumn = this.doc.documentToScreenColumn(cursor.row, cursor.column);
};
- this.navigateLeft = function() {
+ this.navigateLeft = function(times) {
if (!this.selection.isEmpty()) {
var selectionStart = this.getSelectionRange().start;
this.moveCursorToPosition(selectionStart);
}
else {
- this.selection.moveCursorLeft();
+ times = times | 1;
+ while (times--) {
+ this.selection.moveCursorLeft();
+ }
}
this.clearSelection();
};
- this.navigateRight = function() {
+ this.navigateRight = function(times) {
if (!this.selection.isEmpty()) {
var selectionEnd = this.getSelectionRange().end;
this.moveCursorToPosition(selectionEnd);
}
else {
- this.selection.moveCursorRight();
+ times = times | 1;
+ while (times--) {
+ this.selection.moveCursorRight();
+ }
}
this.clearSelection();
};
View
32 lib/ace/keybinding.js
@@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
+ * 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
@@ -41,11 +42,13 @@ var useragent = require("pilot/useragent");
var event = require("pilot/event");
var default_mac = require("ace/conf/keybindings/default_mac").bindings;
var default_win = require("ace/conf/keybindings/default_win").bindings;
+var vim_mode = require("ace/mode/vim");
var canon = require("pilot/canon");
require("ace/commands/default_commands");
var KeyBinding = function(element, editor, config) {
this.setConfig(config);
+ var data = { };
var _self = this;
event.addKeyListener(element, function(e) {
@@ -56,15 +59,30 @@ var KeyBinding = function(element, editor, config) {
else
var hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0)
| (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0);
-
- var key = _self.keyNames[e.keyCode];
- var commandName = (_self.config.reverse[hashId] || {})[(key
- || String.fromCharCode(e.keyCode)).toLowerCase()];
+ var key = (_self.keyNames[e.keyCode] ||
+ String.fromCharCode(e.keyCode)).toLowerCase();
- var success = canon.exec(commandName, {editor: editor});
- if (success) {
- return event.stopEvent(e);
+ var toExecute;
+ if (true) {
+ var toExecute =
+ vim_mode.handleKeyboard(data, hashId, key, e);
+ }
+
+ // If there is nothing to execute yet, then use the default keymapping.
+ if (!toExecute) {
+ toExecute = {
+ command: (_self.config.reverse[hashId] || {})[key]
+ };
+ }
+
+ // If there is something to execute, then go for it.
+ if (toExecute) {
+ var success = canon.exec(toExecute.command,
+ {editor: editor}, toExecute.args);
+ if (success) {
+ return event.stopEvent(e);
+ }
}
});
};
View
193 lib/ace/keyboardstate.js
@@ -0,0 +1,193 @@
+/* ***** 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) {
+
+function KeyboardStateMapper(keymapping) {
+ this.keymapping = this.$buildKeymappingRegex(keymapping);
+}
+
+KeyboardStateMapper.prototype = {
+ /**
+ * Build the RegExp from the keymapping as RegExp can't stored directly
+ * in the metadata JSON and as the RegExp used to match the keys/buffer
+ * need to be adapted.
+ */
+ $buildKeymappingRegex: function(keymapping) {
+ for (state in keymapping) {
+ this.$buildBindingsRegex(keymapping[state]);
+ }
+ return keymapping;
+ },
+
+ $buildBindingsRegex: function(bindings) {
+ // Escape a given Regex string.
+ bindings.forEach(function(binding) {
+ if (binding.key) {
+ binding.key = new RegExp('^' + binding.key + '$');
+ } else if (Array.isArray(binding.regex)) {
+ binding.key = new RegExp('^' + binding.regex[1] + '$');
+ binding.regex = new RegExp(binding.regex.join('') + '$');
+ } else if (binding.regex) {
+ binding.regex = new RegExp(binding.regex + '$');
+ }
+ });
+ },
+
+ $composeBuffer: function(data, hashId, key) {
+ // Initialize the data object.
+ if (data.state == null || data.buffer == null) {
+ data.state = "start";
+ data.buffer = "";
+ }
+
+ var keyArray = [];
+ if (hashId & 1) keyArray.push("Ctrl");
+ if (hashId & 8) keyArray.push("Command");
+ if (hashId & 2) keyArray.push("Option");
+ if (hashId & 4) keyArray.push("Shift");
+ keyArray.push(key);
+
+ var symbolicName = keyArray.join("-").toLowerCase();
+ var bufferToUse = data.buffer + symbolicName;
+
+ // Don't add the symbolic name to the key buffer if the alt_ key is
+ // part of the symbolic name. If it starts with alt_, this means
+ // that the user hit an alt keycombo and there will be a single,
+ // new character detected after this event, which then will be
+ // added to the buffer (e.g. alt_j will result in ∆).
+ //
+ // We test for 2 and not for & 2 as we only want to exclude the case where
+ // the option key is pressed alone.
+ if (hashId != 2) {
+ data.buffer = bufferToUse;
+ }
+
+ return {
+ bufferToUse: bufferToUse,
+ symbolicName: symbolicName
+ };
+ },
+
+ $find: function(data, buffer, symbolicName, hashId, key) {
+ // Holds the command to execute and the args if a command matched.
+ var result = {};
+
+ // Loop over all the bindings of the keymapp until a match is found.
+ this.keymapping[data.state].some(function(binding) {
+ var match;
+
+ // Check if the key matches.
+ if (binding.key && !binding.key.test(symbolicName)) {
+ return false;
+ }
+
+ // Check if the regex matches.
+ if (binding.regex && !(match = binding.regex.exec(buffer))) {
+ return false;
+ }
+
+ // Check if the match function matches.
+ if (binding.match && !binding.match(buffer, hashId, key, symbolicName)) {
+ return false;
+ }
+
+ // Check for disallowed matches.
+ if (binding.disallowMatches) {
+ for (var i = 0; i < binding.disallowMatches.length; i++) {
+ if (!!match[binding.disallowMatches[i]]) {
+ return true;
+ }
+ }
+ }
+
+ // If there is a command to execute, then figure out the
+ // comand and the arguments.
+ if (binding.exec) {
+ result.command = binding.exec;
+
+ // Bulid the arguments.
+ if (binding.params) {
+ var value;
+ result.args = {};
+ binding.params.forEach(function(param) {
+ if (param.match != null && match != null) {
+ value = match[param.match] || param.defaultValue;
+ } else {
+ value = param.defaultValue;
+ }
+
+ if (param.type === 'number') {
+ value = parseInt(value);
+ }
+
+ result.args[param.name] = value;
+ });
+ }
+ data.buffer = "";
+ }
+
+ // Handle the 'then' property.
+ if (binding.then) {
+ data.state = binding.then;
+ data.buffer = "";
+ if (result.command == null) {
+ result.command = "null";
+ }
+ }
+
+ return true;
+ });
+
+ return result.command ? result : false;
+ },
+
+ match: function(data, hashId, key) {
+ // Compute the current value of the keyboard input buffer.
+ var r = this.$composeBuffer(data, hashId, key);
+ var buffer = r.bufferToUse;
+ var symbolicName = r.symbolicName;
+
+ r = this.$find(data, buffer, symbolicName, hashId, key);
+ console.log("KeyboardStateMapper#match", buffer, symbolicName, r);
+
+ return r;
+ }
+}
+
+exports.KeyboardStateMapper = KeyboardStateMapper;
+});
View
121 lib/ace/mode/vim.js
@@ -0,0 +1,121 @@
+/* ***** 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 vimStates = {
+ start: [
+ {
+ key: "i",
+ then: "insertMode"
+ },
+ {
+ regex: [ "([0-9]*)", "(k|up)" ],
+ exec: "golineup",
+ params: [
+ {
+ name: "times",
+ match: 1,
+ type: "number",
+ defaultValue: 1
+ }
+ ]
+ },
+ {
+ regex: [ "([0-9]*)", "(j|down|enter)" ],
+ exec: "golinedown",
+ params: [
+ {
+ name: "times",
+ match: 1,
+ type: "number",
+ defaultValue: 1
+ }
+ ]
+ },
+ {
+ regex: [ "([0-9]*)", "(l|right)" ],
+ exec: "gotoright",
+ params: [
+ {
+ name: "times",
+ match: 1,
+ type: "number",
+ defaultValue: 1
+ }
+ ]
+ },
+ {
+ regex: [ "([0-9]*)", "(h|left)" ],
+ exec: "gotoleft",
+ params: [
+ {
+ name: "times",
+ match: 1,
+ type: "number",
+ defaultValue: 1
+ }
+ ]
+ },
+ {
+ comment: "Let all combos of Command, Ctrl, Optional pass...",
+ match: function(buffer, hashId, key, symbolicName) {
+ return hashId != 0 && !(hashId & 4);
+ }
+ },
+ {
+ comment: "...but stop all other input!",
+ key: ".*",
+ exec: "null"
+ }
+ ],
+ insertMode: [
+ {
+ key: "esc",
+ then: "start"
+ }
+ ]
+};
+
+var vimKeyboardStateMapper = new KeyboardStateMapper(vimStates);
+
+exports.handleKeyboard = function(data, hashId, key, e) {
+ return vimKeyboardStateMapper.match(data, hashId, key);
+}
+});
Please sign in to comment.
Something went wrong with that request. Please try again.