Moving some files around/creating new ace/keyboard directory. Renaming KeyboardStateMapper to StateMapper

This commit is contained in:
Julian Viereck 2011-01-13 23:12:27 +01:00
commit 2d94d77dc4
7 changed files with 17 additions and 20 deletions

View file

@ -55,8 +55,8 @@ exports.launch = function(env) {
var TextMode = require("ace/mode/text").Mode;
var UndoManager = require("ace/undomanager").UndoManager;
var vim = require("ace/mode/vim");
var emacs = require("ace/mode/emacs");
var vim = require("ace/keyboard/vim").Vim;
var emacs = require("ace/keyboard/emacs").Emacs;
var docs = {};

View file

@ -40,8 +40,8 @@ define(function(require, exports, module) {
var oop = require("pilot/oop");
var event = require("pilot/event");
var lang = require("pilot/lang");
var TextInput = require("ace/textinput").TextInput;
var KeyBinding = require("ace/keybinding").KeyBinding;
var TextInput = require("ace/keyboard/textinput").TextInput;
var KeyBinding = require("ace/keyboard/keybinding").KeyBinding;
var Document = require("ace/document").Document;
var Search = require("ace/search").Search;
var BackgroundTokenizer = require("ace/background_tokenizer").BackgroundTokenizer;

149
lib/ace/keyboard/emacs.js Normal file
View file

@ -0,0 +1,149 @@
/* ***** 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 StateHandler = require("ace/keyboard/state_handler").StateHandler;
var matchCharacterOnly = require("ace/keyboard/state_handler").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"
}
]
};
exports.Emacs = new StateHandler(emacsState);
});

View file

@ -0,0 +1,161 @@
/* ***** 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 Ajax.org Code Editor (ACE).
*
* The Initial Developer of the Original Code is
* Ajax.org Services B.V.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* 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
* 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 useragent = require("pilot/useragent");
var keyUtil = require("pilot/keys");
var event = require("pilot/event");
var settings = require("pilot/settings").settings;
var default_mac = require("ace/conf/keybindings/default_mac").bindings;
var default_win = require("ace/conf/keybindings/default_win").bindings;
var canon = require("pilot/canon");
require("ace/commands/default_commands");
var KeyBinding = function(editor, config) {
this.$editor = editor;
this.$data = { };
this.$keyboardHandler = null;
this.setConfig(config);
};
(function() {
function splitSafe(s, separator, limit, bLowerCase) {
return (bLowerCase && s.toLowerCase() || s)
.replace(/(?:^\s+|\n|\s+$)/g, "")
.split(new RegExp("[\\s ]*" + separator + "[\\s ]*", "g"), limit || 999);
}
function parseKeys(keys, val, ret) {
var key,
hashId = 0,
parts = splitSafe(keys, "\\-", null, true),
i = 0,
l = parts.length;
for (; i < l; ++i) {
if (keyUtil.KEY_MODS[parts[i]])
hashId = hashId | keyUtil.KEY_MODS[parts[i]];
else
key = parts[i] || "-"; //when empty, the splitSafe removed a '-'
}
(ret[hashId] || (ret[hashId] = {}))[key] = val;
return ret;
}
function objectReverse(obj, keySplit) {
var i, j, l, key,
ret = {};
for (i in obj) {
key = obj[i];
if (keySplit && typeof key == "string") {
key = key.split(keySplit);
for (j = 0, l = key.length; j < l; ++j)
parseKeys.call(this, key[j], i, ret);
}
else {
parseKeys.call(this, key, i, ret);
}
}
return ret;
}
this.setConfig = function(config) {
this.config = config || (useragent.isMac
? default_mac
: default_win);
if (typeof this.config.reverse == "undefined")
this.config.reverse = objectReverse.call(this, this.config, "|");
};
this.setKeyboardHandler = function(keyboardHandler) {
if (this.$keyboardHandler != keyboardHandler) {
this.$data = { };
this.$keyboardHandler = keyboardHandler;
}
};
this.getKeyboardHandler = function() {
return this.$keyboardHandler;
};
this.onCommandKey = function(e, hashId, keyCode) {
key = (keyUtil[keyCode] ||
String.fromCharCode(keyCode)).toLowerCase();
var toExecute;
if (this.$keyboardHandler) {
toExecute =
this.$keyboardHandler.handleKeyboard(this.$data, hashId, key, e);
}
// If there is nothing to execute yet, then use the default keymapping.
if (!toExecute) {
toExecute = {
command: (this.config.reverse[hashId] || {})[key]
};
}
// If there is something to execute, then go for it.
if (toExecute) {
var success = canon.exec(toExecute.command,
{editor: this.$editor}, toExecute.args);
if (success) {
return event.stopEvent(e);
}
}
};
this.onTextInput = function(text) {
if (this.$keyboardHandler) {
var toExecute =
this.$keyboardHandler.handleKeyboard(this.$data, 0, text, {});
var success = canon.exec(toExecute.command,
{editor: this.$editor}, toExecute.args);
if (success) {
return;
}
}
this.$editor.insert(text);
}
}).call(KeyBinding.prototype);
exports.KeyBinding = KeyBinding;
});

View file

@ -0,0 +1,239 @@
/* ***** 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) {
// If you're developing a new keymapping and want to get an idea what's going
// on, then enable debugging.
var DEBUG = false;
function StateHandler(keymapping) {
this.keymapping = this.$buildKeymappingRegex(keymapping);
}
StateHandler.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");
if (key) 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 false;
}
}
}
// 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 no command is set, then execute the "null" fake command.
if (result.command == null) {
result.command = "null";
}
if (DEBUG) {
console.log("KeyboardStateMapper#find", binding);
}
return true;
});
if (result.command) {
return result;
} else {
data.buffer = "";
return false;
}
},
/**
* This function is called by keyBinding.
*/
handleKeyboard: 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);
if (DEBUG) {
console.log("KeyboardStateMapper#match", buffer, symbolicName, r);
}
return r;
}
}
/**
* 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.StateHandler = StateHandler;
});

View file

@ -0,0 +1,151 @@
/* ***** 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 Ajax.org Code Editor (ACE).
*
* The Initial Developer of the Original Code is
* Ajax.org Services B.V.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
*
* 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 event = require("pilot/event");
var TextInput = function(parentNode, host) {
var text = document.createElement("textarea");
var style = text.style;
style.position = "absolute";
style.left = "-10000px";
style.top = "-10000px";
parentNode.appendChild(text);
var PLACEHOLDER = String.fromCharCode(0);
sendText();
var inCompostion = false;
var copied = false;
function sendText() {
if (!copied) {
var value = text.value;
if (value) {
if (value.charCodeAt(value.length-1) == PLACEHOLDER.charCodeAt(0)) {
value = value.slice(0, -1);
if (value)
host.onTextInput(value);
} else
host.onTextInput(value);
}
}
copied = false;
// Safari doesn't fire copy events if no text is selected
text.value = PLACEHOLDER;
text.select();
}
var onTextInput = function(e) {
setTimeout(function() {
if (!inCompostion)
sendText();
}, 0);
};
var onCompositionStart = function(e) {
inCompostion = true;
sendText();
text.value = "";
host.onCompositionStart();
setTimeout(onCompositionUpdate, 0);
};
var onCompositionUpdate = function() {
host.onCompositionUpdate(text.value);
};
var onCompositionEnd = function() {
inCompostion = false;
host.onCompositionEnd();
onTextInput();
};
var onCopy = function() {
copied = true;
text.value = host.getCopyText();
text.select();
copied = true;
setTimeout(sendText, 0);
};
var onCut = function() {
copied = true;
text.value = host.getCopyText();
host.onCut();
text.select();
setTimeout(sendText, 0);
};
event.addCommandKeyListener(text, host.onCommandKey.bind(host));
event.addListener(text, "keypress", onTextInput);
event.addListener(text, "textInput", onTextInput);
event.addListener(text, "paste", onTextInput);
event.addListener(text, "propertychange", onTextInput);
event.addListener(text, "copy", onCopy);
event.addListener(text, "cut", onCut);
event.addListener(text, "compositionstart", onCompositionStart);
event.addListener(text, "compositionupdate", onCompositionUpdate);
event.addListener(text, "compositionend", onCompositionEnd);
event.addListener(text, "blur", function() {
host.onBlur();
});
event.addListener(text, "focus", function() {
host.onFocus();
text.select();
});
this.focus = function() {
host.onFocus();
text.select();
text.focus();
};
this.blur = function() {
text.blur();
};
};
exports.TextInput = TextInput;
});

112
lib/ace/keyboard/vim.js Normal file
View file

@ -0,0 +1,112 @@
/* ***** 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 StateHandler = require("ace/keyboard/state_handler").StateHandler;
var matchCharacterOnly = require("ace/keyboard/state_handler").matchCharacterOnly;
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: "Catch some keyboard input to stop it here",
match: matchCharacterOnly
}
],
insertMode: [
{
key: "esc",
then: "start"
}
]
};
exports.Vim = new StateHandler(vimStates);
});