From 3ebd2865ea5cbea19dfd29b677b73cf19e91340e Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Fri, 31 Dec 2010 18:32:53 +0100 Subject: [PATCH] Finalize current keymapping work: - catchup with latest changes to pilot/keys.js - the textinput adds a event.addCommandKeyEvent(...) to the input - the onTextInput and onCommandKey event sent by the textinput are forwareded to the edtior which then forwards the input to the keybinding, which handles the input based on the current inputMode. - the current inputMode can get changed by setting the setting "inputMode". Currently there are the modes "vim" and "emacs". If the inputMode doesn't match, the "default" mode is used. --- lib/ace/commands/default_commands.js | 4 +- lib/ace/editor.js | 18 +++- lib/ace/keybinding.js | 152 ++++++++++++--------------- lib/ace/keyboardstate.js | 12 ++- lib/ace/textinput.js | 1 + support/cockpit | 2 +- 6 files changed, 95 insertions(+), 94 deletions(-) diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index e02dd9bd..817e8fab 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -253,8 +253,8 @@ canon.addCommand({ canon.addCommand({ name: "inserttext", exec: function(env, args, request) { - env.editor.onTextInput(lang.stringRepeat(args.text || "", - args.times || 1)); + env.editor.insert(lang.stringRepeat(args.text || "", + args.times || 1)); } }); diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 00e7e30a..953620d0 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -54,7 +54,7 @@ var Editor =function(renderer, doc) { this.renderer = renderer; this.textInput = new TextInput(container, this); - this.keyBinding = new KeyBinding(container, this); + this.keyBinding = new KeyBinding(this); var self = this; event.addListener(container, "mousedown", function(e) { setTimeout(function() {self.focus();}); @@ -64,7 +64,7 @@ var Editor =function(renderer, doc) { return event.preventDefault(e); }); - var mouseTarget = renderer.getMouseEventTarget(); + var mouseTarget = renderer.getMouseEventTarget(); event.addListener(mouseTarget, "mousedown", this.onMouseDown.bind(this)); event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseDoubleClick.bind(this)); event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseTripleClick.bind(this)); @@ -403,7 +403,7 @@ var Editor =function(renderer, doc) { } }; - this.onTextInput = function(text) { + this.insert = function(text) { if (this.$readOnly) return; @@ -480,6 +480,14 @@ var Editor =function(renderer, doc) { _self.renderer.scrollCursorIntoView(); }); }); + } + + this.onTextInput = function(text) { + this.keyBinding.onTextInput(text); + }; + + this.onCommandKey = function(e, hashId, keyCode) { + this.keyBinding.onCommandKey(e, hashId, keyCode); }; this.$overwrite = false; @@ -592,7 +600,7 @@ var Editor =function(renderer, doc) { if (this.selection.isEmpty()) this.selection.selectLeft(); - + this.moveCursorToPosition(this.doc.remove(this.getSelectionRange())); this.clearSelection(); }; @@ -892,7 +900,7 @@ var Editor =function(renderer, doc) { else { times = times || 1; while (times--) { - this.selection.moveCursorLeft(); + this.selection.moveCursorLeft(); } } this.clearSelection(); diff --git a/lib/ace/keybinding.js b/lib/ace/keybinding.js index 35699456..3f0b931d 100644 --- a/lib/ace/keybinding.js +++ b/lib/ace/keybinding.js @@ -39,102 +39,45 @@ 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 vim_mode = require("ace/mode/vim"); -var emacs_mode = require("ace/mode/emacs"); +var inputModes = { + vim: require("ace/mode/vim"), + emacs: require("ace/mode/emacs") +}; -var KeyBinding = function(element, editor, config) { +var KeyBinding = function(editor, config) { + this.$editor = editor; + this.$data = { }; this.setConfig(config); - var data = { }; - var _self = this; - event.addKeyListener(element, function(e) { - // opera on mac swaps ctrl and meta keys - if (useragent.isOpera && useragent.isMac) - var hashId = 0 | (e.metaKey ? 1 : 0) | (e.altKey ? 2 : 0) - | (e.shiftKey ? 4 : 0) | (e.ctrlKey ? 8 : 0); - else - var hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) - | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0); + // PROBLEM: When this file is loaded via require.js, the types are already + // declaired, BUT the startup() function is not called in pilot/types/basic. + // Therefore the settings are not declaired. As a workaround, the setting + // is declaired when the KeyBinding is created. + var InputModeSetting = { + name: "inputMode", + description: "Which input mode do you want to use?", + type: "text", + defaultValue: "standard" + }; - console.log(hashId, e.keyCode); - var key; - var keyCode = e.keyCode; + this.$inputMode = null; + settings.addSetting(InputModeSetting); - // 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(); - } - - var toExecute; - if (true) { - var toExecute = - // 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 (!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); - } - } - }); + settings.getSetting("inputMode").addEventListener("change", function(e) { + this.$inputMode = inputModes[e.value]; + this.$data = { }; + }.bind(this)); }; (function() { - this.keyMods = {"ctrl": 1, "alt": 2, "option" : 2, "shift": 4, "meta": 8, "command": 8}; - - this.keyNames = { - "8" : "Backspace", - "9" : "Tab", - "13" : "Enter", - "27" : "Esc", - "32" : "Space", - "33" : "PageUp", - "34" : "PageDown", - "35" : "End", - "36" : "Home", - "37" : "Left", - "38" : "Up", - "39" : "Right", - "40" : "Down", - "45" : "Insert", - "46" : "Delete", - "107": "+", - "112": "F1", - "113": "F2", - "114": "F3", - "115": "F4", - "116": "F5", - "117": "F6", - "118": "F7", - "119": "F8", - "120": "F9", - "121": "F10", - "122": "F11", - "123": "F12" - }; - function splitSafe(s, separator, limit, bLowerCase) { return (bLowerCase && s.toLowerCase() || s) .replace(/(?:^\s+|\n|\s+$)/g, "") @@ -149,8 +92,8 @@ var KeyBinding = function(element, editor, config) { l = parts.length; for (; i < l; ++i) { - if (this.keyMods[parts[i]]) - hashId = hashId | this.keyMods[parts[i]]; + if (keyUtil.KEY_MODS[parts[i]]) + hashId = hashId | keyUtil.KEY_MODS[parts[i]]; else key = parts[i] || "-"; //when empty, the splitSafe removed a '-' } @@ -184,6 +127,47 @@ var KeyBinding = function(element, editor, config) { this.config.reverse = objectReverse.call(this, this.config, "|"); }; + + this.onCommandKey = function(e, hashId, keyCode) { + key = (keyUtil[keyCode] || + String.fromCharCode(keyCode)).toLowerCase(); + + var toExecute; + if (this.$inputMode) { + toExecute = + this.$inputMode.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.$inputMode) { + var toExecute = + this.$inputMode.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; diff --git a/lib/ace/keyboardstate.js b/lib/ace/keyboardstate.js index 600447d5..7a76563a 100644 --- a/lib/ace/keyboardstate.js +++ b/lib/ace/keyboardstate.js @@ -37,6 +37,10 @@ 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 KeyboardStateMapper(keymapping) { this.keymapping = this.$buildKeymappingRegex(keymapping); } @@ -172,7 +176,9 @@ KeyboardStateMapper.prototype = { result.command = "null"; } - console.log("KeyboardStateMapper#find", binding); + if (DEBUG) { + console.log("KeyboardStateMapper#find", binding); + } return true; }); @@ -191,7 +197,9 @@ KeyboardStateMapper.prototype = { var symbolicName = r.symbolicName; r = this.$find(data, buffer, symbolicName, hashId, key); - console.log("KeyboardStateMapper#match", buffer, symbolicName, r); + if (DEBUG) { + console.log("KeyboardStateMapper#match", buffer, symbolicName, r); + } return r; } diff --git a/lib/ace/textinput.js b/lib/ace/textinput.js index 076f7875..6b4a673f 100644 --- a/lib/ace/textinput.js +++ b/lib/ace/textinput.js @@ -114,6 +114,7 @@ var TextInput = function(parentNode, host) { 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); diff --git a/support/cockpit b/support/cockpit index 36c55c39..cd074fd3 160000 --- a/support/cockpit +++ b/support/cockpit @@ -1 +1 @@ -Subproject commit 36c55c39e8ede94e964364e01ab5308f3f51c2eb +Subproject commit cd074fd37a02bc40300aa7b120d395525767b156