mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-20 23:57:59 +01:00
Popup and key processing reorganization.
This commit is contained in:
@@ -8,321 +8,6 @@
|
|||||||
|
|
||||||
/** @scope modules */
|
/** @scope modules */
|
||||||
|
|
||||||
var ProcessorStack = Class("ProcessorStack", {
|
|
||||||
init: function (mode, hives, builtin) {
|
|
||||||
this.main = mode.main;
|
|
||||||
this._actions = [];
|
|
||||||
this.actions = [];
|
|
||||||
this.buffer = "";
|
|
||||||
this.events = [];
|
|
||||||
|
|
||||||
events.dbg("STACK " + mode);
|
|
||||||
|
|
||||||
let main = { __proto__: mode.main, params: mode.params };
|
|
||||||
this.modes = array([mode.params.keyModes, main, mode.main.allBases.slice(1)]).flatten().compact();
|
|
||||||
|
|
||||||
if (builtin)
|
|
||||||
hives = hives.filter(function (h) h.name === "builtin");
|
|
||||||
|
|
||||||
this.processors = this.modes.map(function (m) hives.map(function (h) KeyProcessor(m, h)))
|
|
||||||
.flatten().array;
|
|
||||||
this.ownsBuffer = !this.processors.some(function (p) p.main.ownsBuffer);
|
|
||||||
|
|
||||||
for (let [i, input] in Iterator(this.processors)) {
|
|
||||||
let params = input.main.params;
|
|
||||||
|
|
||||||
if (params.preExecute)
|
|
||||||
input.preExecute = params.preExecute;
|
|
||||||
|
|
||||||
if (params.postExecute)
|
|
||||||
input.postExecute = params.postExecute;
|
|
||||||
|
|
||||||
if (params.onKeyPress && input.hive === mappings.builtin)
|
|
||||||
input.fallthrough = function fallthrough(events) {
|
|
||||||
return params.onKeyPress(events) === false ? Events.KILL : Events.PASS;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let hive = options.get("passkeys")[this.main.input ? "inputHive" : "commandHive"];
|
|
||||||
if (!builtin && hive.active && (!dactyl.focusedElement || events.isContentNode(dactyl.focusedElement)))
|
|
||||||
this.processors.unshift(KeyProcessor(modes.BASE, hive));
|
|
||||||
},
|
|
||||||
|
|
||||||
passUnknown: Class.Memoize(function () options.get("passunknown").getKey(this.modes)),
|
|
||||||
|
|
||||||
notify: function () {
|
|
||||||
events.dbg("NOTIFY()");
|
|
||||||
events.keyEvents = [];
|
|
||||||
events.processor = null;
|
|
||||||
if (!this.execute(undefined, true)) {
|
|
||||||
events.processor = this;
|
|
||||||
events.keyEvents = this.keyEvents;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_result: function (result) (result === Events.KILL ? "KILL" :
|
|
||||||
result === Events.PASS ? "PASS" :
|
|
||||||
result === Events.PASS_THROUGH ? "PASS_THROUGH" :
|
|
||||||
result === Events.ABORT ? "ABORT" :
|
|
||||||
callable(result) ? result.toSource().substr(0, 50) : result),
|
|
||||||
|
|
||||||
execute: function execute(result, force) {
|
|
||||||
events.dbg("EXECUTE(" + this._result(result) + ", " + force + ") events:" + this.events.length
|
|
||||||
+ " processors:" + this.processors.length + " actions:" + this.actions.length);
|
|
||||||
|
|
||||||
let processors = this.processors;
|
|
||||||
let length = 1;
|
|
||||||
|
|
||||||
if (force)
|
|
||||||
this.processors = [];
|
|
||||||
|
|
||||||
if (this.ownsBuffer)
|
|
||||||
statusline.inputBuffer = this.processors.length ? this.buffer : "";
|
|
||||||
|
|
||||||
if (!this.processors.some(function (p) !p.extended) && this.actions.length) {
|
|
||||||
// We have matching actions and no processors other than
|
|
||||||
// those waiting on further arguments. Execute actions as
|
|
||||||
// long as they continue to return PASS.
|
|
||||||
|
|
||||||
for (var action in values(this.actions)) {
|
|
||||||
while (callable(action)) {
|
|
||||||
length = action.eventLength;
|
|
||||||
action = dactyl.trapErrors(action);
|
|
||||||
events.dbg("ACTION RES: " + length + " " + this._result(action));
|
|
||||||
}
|
|
||||||
if (action !== Events.PASS)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result is the result of the last action. Unless it's
|
|
||||||
// PASS, kill any remaining argument processors.
|
|
||||||
result = action !== undefined ? action : Events.KILL;
|
|
||||||
if (action !== Events.PASS)
|
|
||||||
this.processors.length = 0;
|
|
||||||
}
|
|
||||||
else if (this.processors.length) {
|
|
||||||
// We're still waiting on the longest matching processor.
|
|
||||||
// Kill the event, set a timeout to give up waiting if applicable.
|
|
||||||
|
|
||||||
result = Events.KILL;
|
|
||||||
if (options["timeout"] && (this.actions.length || events.hasNativeKey(this.events[0], this.main, this.passUnknown)))
|
|
||||||
this.timer = services.Timer(this, options["timeoutlen"], services.Timer.TYPE_ONE_SHOT);
|
|
||||||
}
|
|
||||||
else if (result !== Events.KILL && !this.actions.length &&
|
|
||||||
!(this.events[0].isReplay || this.passUnknown
|
|
||||||
|| this.modes.some(function (m) m.passEvent(this), this.events[0]))) {
|
|
||||||
// No patching processors, this isn't a fake, pass-through
|
|
||||||
// event, we're not in pass-through mode, and we're not
|
|
||||||
// choosing to pass unknown keys. Kill the event and beep.
|
|
||||||
|
|
||||||
result = Events.ABORT;
|
|
||||||
if (!Events.isEscape(this.events.slice(-1)[0]))
|
|
||||||
dactyl.beep();
|
|
||||||
events.feedingKeys = false;
|
|
||||||
}
|
|
||||||
else if (result === undefined)
|
|
||||||
// No matching processors, we're willing to pass this event,
|
|
||||||
// and we don't have a default action from a processor. Just
|
|
||||||
// pass the event.
|
|
||||||
result = Events.PASS;
|
|
||||||
|
|
||||||
events.dbg("RESULT: " + length + " " + this._result(result) + "\n\n");
|
|
||||||
|
|
||||||
if (result !== Events.PASS || this.events.length > 1)
|
|
||||||
if (result !== Events.ABORT || !this.events[0].isReplay)
|
|
||||||
Events.kill(this.events[this.events.length - 1]);
|
|
||||||
|
|
||||||
if (result === Events.PASS_THROUGH || result === Events.PASS && this.passUnknown)
|
|
||||||
events.passing = true;
|
|
||||||
|
|
||||||
if (result === Events.PASS_THROUGH && this.keyEvents.length)
|
|
||||||
events.dbg("PASS_THROUGH:\n\t" + this.keyEvents.map(function (e) [e.type, events.toString(e)]).join("\n\t"));
|
|
||||||
|
|
||||||
if (result === Events.PASS_THROUGH)
|
|
||||||
events.feedevents(null, this.keyEvents, { skipmap: true, isMacro: true, isReplay: true });
|
|
||||||
else {
|
|
||||||
let list = this.events.filter(function (e) e.getPreventDefault() && !e.dactylDefaultPrevented);
|
|
||||||
|
|
||||||
if (result === Events.PASS)
|
|
||||||
events.dbg("PASS THROUGH: " + list.slice(0, length).filter(function (e) e.type === "keypress").map(events.closure.toString));
|
|
||||||
if (list.length > length)
|
|
||||||
events.dbg("REFEED: " + list.slice(length).filter(function (e) e.type === "keypress").map(events.closure.toString));
|
|
||||||
|
|
||||||
if (result === Events.PASS)
|
|
||||||
events.feedevents(null, list.slice(0, length), { skipmap: true, isMacro: true, isReplay: true });
|
|
||||||
if (list.length > length && this.processors.length === 0)
|
|
||||||
events.feedevents(null, list.slice(length));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.processors.length === 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
process: function process(event) {
|
|
||||||
if (this.timer)
|
|
||||||
this.timer.cancel();
|
|
||||||
|
|
||||||
let key = events.toString(event);
|
|
||||||
this.events.push(event);
|
|
||||||
if (this.keyEvents)
|
|
||||||
this.keyEvents.push(event);
|
|
||||||
|
|
||||||
this.buffer += key;
|
|
||||||
|
|
||||||
let actions = [];
|
|
||||||
let processors = [];
|
|
||||||
|
|
||||||
events.dbg("PROCESS(" + key + ") skipmap: " + event.skipmap + " macro: " + event.isMacro + " replay: " + event.isReplay);
|
|
||||||
|
|
||||||
for (let [i, input] in Iterator(this.processors)) {
|
|
||||||
let res = input.process(event);
|
|
||||||
if (res !== Events.ABORT)
|
|
||||||
var result = res;
|
|
||||||
|
|
||||||
events.dbg("RES: " + input + " " + this._result(res));
|
|
||||||
|
|
||||||
if (res === Events.KILL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (callable(res))
|
|
||||||
actions.push(res);
|
|
||||||
|
|
||||||
if (res === Events.WAIT || input.waiting)
|
|
||||||
processors.push(input);
|
|
||||||
if (isinstance(res, KeyProcessor))
|
|
||||||
processors.push(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
events.dbg("RESULT: " + event.getPreventDefault() + " " + this._result(result));
|
|
||||||
events.dbg("ACTIONS: " + actions.length + " " + this.actions.length);
|
|
||||||
events.dbg("PROCESSORS:", processors, "\n");
|
|
||||||
|
|
||||||
this._actions = actions;
|
|
||||||
this.actions = actions.concat(this.actions);
|
|
||||||
|
|
||||||
for (let action in values(actions))
|
|
||||||
if (!("eventLength" in action))
|
|
||||||
action.eventLength = this.events.length;
|
|
||||||
|
|
||||||
if (result === Events.KILL)
|
|
||||||
this.actions = [];
|
|
||||||
else if (!this.actions.length && !processors.length)
|
|
||||||
for (let input in values(this.processors))
|
|
||||||
if (input.fallthrough) {
|
|
||||||
if (result === Events.KILL)
|
|
||||||
break;
|
|
||||||
result = dactyl.trapErrors(input.fallthrough, input, this.events);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processors = processors;
|
|
||||||
|
|
||||||
return this.execute(result, options["timeout"] && options["timeoutlen"] === 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var KeyProcessor = Class("KeyProcessor", {
|
|
||||||
init: function init(main, hive) {
|
|
||||||
this.main = main;
|
|
||||||
this.events = [];
|
|
||||||
this.hive = hive;
|
|
||||||
this.wantCount = this.main.count;
|
|
||||||
},
|
|
||||||
|
|
||||||
get toStringParams() [this.main.name, this.hive.name],
|
|
||||||
|
|
||||||
countStr: "",
|
|
||||||
command: "",
|
|
||||||
get count() this.countStr ? Number(this.countStr) : this.main.params.count || null,
|
|
||||||
|
|
||||||
append: function append(event) {
|
|
||||||
this.events.push(event);
|
|
||||||
let key = events.toString(event);
|
|
||||||
|
|
||||||
if (this.wantCount && !this.command &&
|
|
||||||
(this.countStr ? /^[0-9]$/ : /^[1-9]$/).test(key))
|
|
||||||
this.countStr += key;
|
|
||||||
else
|
|
||||||
this.command += key;
|
|
||||||
return this.events;
|
|
||||||
},
|
|
||||||
|
|
||||||
process: function process(event) {
|
|
||||||
this.append(event);
|
|
||||||
this.waiting = false;
|
|
||||||
return this.onKeyPress(event);
|
|
||||||
},
|
|
||||||
|
|
||||||
execute: function execute(map, args)
|
|
||||||
let (self = this)
|
|
||||||
function execute() {
|
|
||||||
if (self.preExecute)
|
|
||||||
self.preExecute.apply(self, args);
|
|
||||||
|
|
||||||
args.self = self.main.params.mappingSelf || self.main.mappingSelf || map;
|
|
||||||
let res = map.execute.call(map, args);
|
|
||||||
|
|
||||||
if (self.postExecute)
|
|
||||||
self.postExecute.apply(self, args);
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
|
|
||||||
onKeyPress: function onKeyPress(event) {
|
|
||||||
if (event.skipmap)
|
|
||||||
return Events.ABORT;
|
|
||||||
|
|
||||||
if (!this.command)
|
|
||||||
return Events.WAIT;
|
|
||||||
|
|
||||||
var map = this.hive.get(this.main, this.command);
|
|
||||||
this.waiting = this.hive.getCandidates(this.main, this.command);
|
|
||||||
if (map) {
|
|
||||||
if (map.arg)
|
|
||||||
return KeyArgProcessor(this, map, false, "arg");
|
|
||||||
else if (map.motion)
|
|
||||||
return KeyArgProcessor(this, map, true, "motion");
|
|
||||||
|
|
||||||
return this.execute(map, {
|
|
||||||
keyEvents: this.keyEvents,
|
|
||||||
command: this.command,
|
|
||||||
count: this.count,
|
|
||||||
keypressEvents: this.events
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.waiting)
|
|
||||||
return this.main.insert ? Events.PASS : Events.ABORT;
|
|
||||||
|
|
||||||
return Events.WAIT;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var KeyArgProcessor = Class("KeyArgProcessor", KeyProcessor, {
|
|
||||||
init: function init(input, map, wantCount, argName) {
|
|
||||||
init.supercall(this, input.main, input.hive);
|
|
||||||
this.map = map;
|
|
||||||
this.parent = input;
|
|
||||||
this.argName = argName;
|
|
||||||
this.wantCount = wantCount;
|
|
||||||
},
|
|
||||||
|
|
||||||
extended: true,
|
|
||||||
|
|
||||||
onKeyPress: function onKeyPress(event) {
|
|
||||||
if (Events.isEscape(event))
|
|
||||||
return Events.KILL;
|
|
||||||
if (!this.command)
|
|
||||||
return Events.WAIT;
|
|
||||||
|
|
||||||
let args = {
|
|
||||||
command: this.parent.command,
|
|
||||||
count: this.count || this.parent.count,
|
|
||||||
events: this.parent.events.concat(this.events)
|
|
||||||
};
|
|
||||||
args[this.argName] = this.command;
|
|
||||||
|
|
||||||
return this.execute(this.map, args);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A hive used mainly for tracking event listeners and cleaning them up when a
|
* A hive used mainly for tracking event listeners and cleaning them up when a
|
||||||
* group is destroyed.
|
* group is destroyed.
|
||||||
@@ -491,12 +176,11 @@ var Events = Module("events", {
|
|||||||
this._code_key[60] = "lt";
|
this._code_key[60] = "lt";
|
||||||
}
|
}
|
||||||
|
|
||||||
this._activeMenubar = false;
|
|
||||||
this.listen(window, this, "events", true);
|
|
||||||
|
|
||||||
this.popups = {
|
this.popups = {
|
||||||
active: [],
|
active: [],
|
||||||
|
|
||||||
|
activeMenubar: null,
|
||||||
|
|
||||||
update: function update(elem) {
|
update: function update(elem) {
|
||||||
if (elem) {
|
if (elem) {
|
||||||
if (elem instanceof Ci.nsIAutoCompletePopup
|
if (elem instanceof Ci.nsIAutoCompletePopup
|
||||||
@@ -510,12 +194,52 @@ var Events = Module("events", {
|
|||||||
|
|
||||||
this.active = this.active.filter(function (e) e.popupBoxObject.popupState != "closed");
|
this.active = this.active.filter(function (e) e.popupBoxObject.popupState != "closed");
|
||||||
|
|
||||||
if (!this.active.length && !events._activeMenubar)
|
if (!this.active.length && !this.activeMenubar)
|
||||||
modes.remove(modes.MENU, true);
|
modes.remove(modes.MENU, true);
|
||||||
else if (modes.main != modes.MENU)
|
else if (modes.main != modes.MENU)
|
||||||
modes.push(modes.MENU);
|
modes.push(modes.MENU);
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
DOMMenuBarActive: function onDOMMenuBarActive(event) {
|
||||||
|
this.activeMenubar = event.target;
|
||||||
|
if (modes.main != modes.MENU)
|
||||||
|
modes.push(modes.MENU);
|
||||||
|
},
|
||||||
|
|
||||||
|
DOMMenuBarInactive: function onDOMMenuBarInactive(event) {
|
||||||
|
this.activeMenubar = null;
|
||||||
|
modes.remove(modes.MENU, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
popupshowing: function onPopupShowing(event) {
|
||||||
|
this.update(event.originalTarget);
|
||||||
|
},
|
||||||
|
|
||||||
|
popupshown: function onPopupShown(event) {
|
||||||
|
let elem = event.originalTarget;
|
||||||
|
this.update(elem);
|
||||||
|
|
||||||
|
if (elem instanceof Ci.nsIAutoCompletePopup) {
|
||||||
|
if (modes.main != modes.AUTOCOMPLETE)
|
||||||
|
modes.push(modes.AUTOCOMPLETE);
|
||||||
|
}
|
||||||
|
else if (elem.localName !== "tooltip") {
|
||||||
|
if (Events.isHidden(elem))
|
||||||
|
if (elem.hidePopup && Events.isHidden(elem.parentNode))
|
||||||
|
elem.hidePopup();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
popuphidden: function onPopupHidden(event) {
|
||||||
|
this.update();
|
||||||
|
modes.remove(modes.AUTOCOMPLETE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.listen(window, this, "events", true);
|
||||||
|
this.listen(window, this.popups, "events", true);
|
||||||
},
|
},
|
||||||
|
|
||||||
signals: {
|
signals: {
|
||||||
@@ -1141,17 +865,6 @@ var Events = Module("events", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
DOMMenuBarActive: function onDOMMenuBarActive() {
|
|
||||||
this._activeMenubar = true;
|
|
||||||
if (modes.main != modes.MENU)
|
|
||||||
modes.push(modes.MENU);
|
|
||||||
},
|
|
||||||
|
|
||||||
DOMMenuBarInactive: function onDOMMenuBarInactive() {
|
|
||||||
this._activeMenubar = false;
|
|
||||||
modes.remove(modes.MENU, true);
|
|
||||||
},
|
|
||||||
|
|
||||||
blur: function onBlur(event) {
|
blur: function onBlur(event) {
|
||||||
let elem = event.originalTarget;
|
let elem = event.originalTarget;
|
||||||
if (elem instanceof Window && services.focus.activeWindow == null
|
if (elem instanceof Window && services.focus.activeWindow == null
|
||||||
@@ -1399,30 +1112,6 @@ var Events = Module("events", {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
popupshowing: function onPopupShowing(event) {
|
|
||||||
this.popups.update(event.originalTarget);
|
|
||||||
},
|
|
||||||
|
|
||||||
popupshown: function onPopupShown(event) {
|
|
||||||
let elem = event.originalTarget;
|
|
||||||
this.popups.update(elem);
|
|
||||||
|
|
||||||
if (elem instanceof Ci.nsIAutoCompletePopup) {
|
|
||||||
if (modes.main != modes.AUTOCOMPLETE)
|
|
||||||
modes.push(modes.AUTOCOMPLETE);
|
|
||||||
}
|
|
||||||
else if (elem.localName !== "tooltip") {
|
|
||||||
if (Events.isHidden(elem))
|
|
||||||
if (elem.hidePopup && Events.isHidden(elem.parentNode))
|
|
||||||
elem.hidePopup();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
popuphidden: function onPopupHidden(event) {
|
|
||||||
this.popups.update();
|
|
||||||
modes.remove(modes.AUTOCOMPLETE);
|
|
||||||
},
|
|
||||||
|
|
||||||
resize: function onResize(event) {
|
resize: function onResize(event) {
|
||||||
if (window.fullScreen != this._fullscreen) {
|
if (window.fullScreen != this._fullscreen) {
|
||||||
statusline.statusBar.removeAttribute("moz-collapsed");
|
statusline.statusBar.removeAttribute("moz-collapsed");
|
||||||
|
|||||||
323
common/content/key-processors.js
Normal file
323
common/content/key-processors.js
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
// Copyright (c) 2008-2011 by Kris Maglione <maglione.k at Gmail>
|
||||||
|
//
|
||||||
|
// This work is licensed for reuse under an MIT license. Details are
|
||||||
|
// given in the LICENSE.txt file included with this file.
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/** @scope modules */
|
||||||
|
|
||||||
|
var ProcessorStack = Class("ProcessorStack", {
|
||||||
|
init: function (mode, hives, builtin) {
|
||||||
|
this.main = mode.main;
|
||||||
|
this._actions = [];
|
||||||
|
this.actions = [];
|
||||||
|
this.buffer = "";
|
||||||
|
this.events = [];
|
||||||
|
|
||||||
|
events.dbg("STACK " + mode);
|
||||||
|
|
||||||
|
let main = { __proto__: mode.main, params: mode.params };
|
||||||
|
this.modes = array([mode.params.keyModes, main, mode.main.allBases.slice(1)]).flatten().compact();
|
||||||
|
|
||||||
|
if (builtin)
|
||||||
|
hives = hives.filter(function (h) h.name === "builtin");
|
||||||
|
|
||||||
|
this.processors = this.modes.map(function (m) hives.map(function (h) KeyProcessor(m, h)))
|
||||||
|
.flatten().array;
|
||||||
|
this.ownsBuffer = !this.processors.some(function (p) p.main.ownsBuffer);
|
||||||
|
|
||||||
|
for (let [i, input] in Iterator(this.processors)) {
|
||||||
|
let params = input.main.params;
|
||||||
|
|
||||||
|
if (params.preExecute)
|
||||||
|
input.preExecute = params.preExecute;
|
||||||
|
|
||||||
|
if (params.postExecute)
|
||||||
|
input.postExecute = params.postExecute;
|
||||||
|
|
||||||
|
if (params.onKeyPress && input.hive === mappings.builtin)
|
||||||
|
input.fallthrough = function fallthrough(events) {
|
||||||
|
return params.onKeyPress(events) === false ? Events.KILL : Events.PASS;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let hive = options.get("passkeys")[this.main.input ? "inputHive" : "commandHive"];
|
||||||
|
if (!builtin && hive.active && (!dactyl.focusedElement || events.isContentNode(dactyl.focusedElement)))
|
||||||
|
this.processors.unshift(KeyProcessor(modes.BASE, hive));
|
||||||
|
},
|
||||||
|
|
||||||
|
passUnknown: Class.Memoize(function () options.get("passunknown").getKey(this.modes)),
|
||||||
|
|
||||||
|
notify: function () {
|
||||||
|
events.dbg("NOTIFY()");
|
||||||
|
events.keyEvents = [];
|
||||||
|
events.processor = null;
|
||||||
|
if (!this.execute(undefined, true)) {
|
||||||
|
events.processor = this;
|
||||||
|
events.keyEvents = this.keyEvents;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_result: function (result) (result === Events.KILL ? "KILL" :
|
||||||
|
result === Events.PASS ? "PASS" :
|
||||||
|
result === Events.PASS_THROUGH ? "PASS_THROUGH" :
|
||||||
|
result === Events.ABORT ? "ABORT" :
|
||||||
|
callable(result) ? result.toSource().substr(0, 50) : result),
|
||||||
|
|
||||||
|
execute: function execute(result, force) {
|
||||||
|
events.dbg("EXECUTE(" + this._result(result) + ", " + force + ") events:" + this.events.length
|
||||||
|
+ " processors:" + this.processors.length + " actions:" + this.actions.length);
|
||||||
|
|
||||||
|
let processors = this.processors;
|
||||||
|
let length = 1;
|
||||||
|
|
||||||
|
if (force)
|
||||||
|
this.processors = [];
|
||||||
|
|
||||||
|
if (this.ownsBuffer)
|
||||||
|
statusline.inputBuffer = this.processors.length ? this.buffer : "";
|
||||||
|
|
||||||
|
if (!this.processors.some(function (p) !p.extended) && this.actions.length) {
|
||||||
|
// We have matching actions and no processors other than
|
||||||
|
// those waiting on further arguments. Execute actions as
|
||||||
|
// long as they continue to return PASS.
|
||||||
|
|
||||||
|
for (var action in values(this.actions)) {
|
||||||
|
while (callable(action)) {
|
||||||
|
length = action.eventLength;
|
||||||
|
action = dactyl.trapErrors(action);
|
||||||
|
events.dbg("ACTION RES: " + length + " " + this._result(action));
|
||||||
|
}
|
||||||
|
if (action !== Events.PASS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result is the result of the last action. Unless it's
|
||||||
|
// PASS, kill any remaining argument processors.
|
||||||
|
result = action !== undefined ? action : Events.KILL;
|
||||||
|
if (action !== Events.PASS)
|
||||||
|
this.processors.length = 0;
|
||||||
|
}
|
||||||
|
else if (this.processors.length) {
|
||||||
|
// We're still waiting on the longest matching processor.
|
||||||
|
// Kill the event, set a timeout to give up waiting if applicable.
|
||||||
|
|
||||||
|
result = Events.KILL;
|
||||||
|
if (options["timeout"] && (this.actions.length || events.hasNativeKey(this.events[0], this.main, this.passUnknown)))
|
||||||
|
this.timer = services.Timer(this, options["timeoutlen"], services.Timer.TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
else if (result !== Events.KILL && !this.actions.length &&
|
||||||
|
!(this.events[0].isReplay || this.passUnknown
|
||||||
|
|| this.modes.some(function (m) m.passEvent(this), this.events[0]))) {
|
||||||
|
// No patching processors, this isn't a fake, pass-through
|
||||||
|
// event, we're not in pass-through mode, and we're not
|
||||||
|
// choosing to pass unknown keys. Kill the event and beep.
|
||||||
|
|
||||||
|
result = Events.ABORT;
|
||||||
|
if (!Events.isEscape(this.events.slice(-1)[0]))
|
||||||
|
dactyl.beep();
|
||||||
|
events.feedingKeys = false;
|
||||||
|
}
|
||||||
|
else if (result === undefined)
|
||||||
|
// No matching processors, we're willing to pass this event,
|
||||||
|
// and we don't have a default action from a processor. Just
|
||||||
|
// pass the event.
|
||||||
|
result = Events.PASS;
|
||||||
|
|
||||||
|
events.dbg("RESULT: " + length + " " + this._result(result) + "\n\n");
|
||||||
|
|
||||||
|
if (result !== Events.PASS || this.events.length > 1)
|
||||||
|
if (result !== Events.ABORT || !this.events[0].isReplay)
|
||||||
|
Events.kill(this.events[this.events.length - 1]);
|
||||||
|
|
||||||
|
if (result === Events.PASS_THROUGH || result === Events.PASS && this.passUnknown)
|
||||||
|
events.passing = true;
|
||||||
|
|
||||||
|
if (result === Events.PASS_THROUGH && this.keyEvents.length)
|
||||||
|
events.dbg("PASS_THROUGH:\n\t" + this.keyEvents.map(function (e) [e.type, events.toString(e)]).join("\n\t"));
|
||||||
|
|
||||||
|
if (result === Events.PASS_THROUGH)
|
||||||
|
events.feedevents(null, this.keyEvents, { skipmap: true, isMacro: true, isReplay: true });
|
||||||
|
else {
|
||||||
|
let list = this.events.filter(function (e) e.getPreventDefault() && !e.dactylDefaultPrevented);
|
||||||
|
|
||||||
|
if (result === Events.PASS)
|
||||||
|
events.dbg("PASS THROUGH: " + list.slice(0, length).filter(function (e) e.type === "keypress").map(events.closure.toString));
|
||||||
|
if (list.length > length)
|
||||||
|
events.dbg("REFEED: " + list.slice(length).filter(function (e) e.type === "keypress").map(events.closure.toString));
|
||||||
|
|
||||||
|
if (result === Events.PASS)
|
||||||
|
events.feedevents(null, list.slice(0, length), { skipmap: true, isMacro: true, isReplay: true });
|
||||||
|
if (list.length > length && this.processors.length === 0)
|
||||||
|
events.feedevents(null, list.slice(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.processors.length === 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
process: function process(event) {
|
||||||
|
if (this.timer)
|
||||||
|
this.timer.cancel();
|
||||||
|
|
||||||
|
let key = events.toString(event);
|
||||||
|
this.events.push(event);
|
||||||
|
if (this.keyEvents)
|
||||||
|
this.keyEvents.push(event);
|
||||||
|
|
||||||
|
this.buffer += key;
|
||||||
|
|
||||||
|
let actions = [];
|
||||||
|
let processors = [];
|
||||||
|
|
||||||
|
events.dbg("PROCESS(" + key + ") skipmap: " + event.skipmap + " macro: " + event.isMacro + " replay: " + event.isReplay);
|
||||||
|
|
||||||
|
for (let [i, input] in Iterator(this.processors)) {
|
||||||
|
let res = input.process(event);
|
||||||
|
if (res !== Events.ABORT)
|
||||||
|
var result = res;
|
||||||
|
|
||||||
|
events.dbg("RES: " + input + " " + this._result(res));
|
||||||
|
|
||||||
|
if (res === Events.KILL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (callable(res))
|
||||||
|
actions.push(res);
|
||||||
|
|
||||||
|
if (res === Events.WAIT || input.waiting)
|
||||||
|
processors.push(input);
|
||||||
|
if (isinstance(res, KeyProcessor))
|
||||||
|
processors.push(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
events.dbg("RESULT: " + event.getPreventDefault() + " " + this._result(result));
|
||||||
|
events.dbg("ACTIONS: " + actions.length + " " + this.actions.length);
|
||||||
|
events.dbg("PROCESSORS:", processors, "\n");
|
||||||
|
|
||||||
|
this._actions = actions;
|
||||||
|
this.actions = actions.concat(this.actions);
|
||||||
|
|
||||||
|
for (let action in values(actions))
|
||||||
|
if (!("eventLength" in action))
|
||||||
|
action.eventLength = this.events.length;
|
||||||
|
|
||||||
|
if (result === Events.KILL)
|
||||||
|
this.actions = [];
|
||||||
|
else if (!this.actions.length && !processors.length)
|
||||||
|
for (let input in values(this.processors))
|
||||||
|
if (input.fallthrough) {
|
||||||
|
if (result === Events.KILL)
|
||||||
|
break;
|
||||||
|
result = dactyl.trapErrors(input.fallthrough, input, this.events);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processors = processors;
|
||||||
|
|
||||||
|
return this.execute(result, options["timeout"] && options["timeoutlen"] === 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var KeyProcessor = Class("KeyProcessor", {
|
||||||
|
init: function init(main, hive) {
|
||||||
|
this.main = main;
|
||||||
|
this.events = [];
|
||||||
|
this.hive = hive;
|
||||||
|
this.wantCount = this.main.count;
|
||||||
|
},
|
||||||
|
|
||||||
|
get toStringParams() [this.main.name, this.hive.name],
|
||||||
|
|
||||||
|
countStr: "",
|
||||||
|
command: "",
|
||||||
|
get count() this.countStr ? Number(this.countStr) : this.main.params.count || null,
|
||||||
|
|
||||||
|
append: function append(event) {
|
||||||
|
this.events.push(event);
|
||||||
|
let key = events.toString(event);
|
||||||
|
|
||||||
|
if (this.wantCount && !this.command &&
|
||||||
|
(this.countStr ? /^[0-9]$/ : /^[1-9]$/).test(key))
|
||||||
|
this.countStr += key;
|
||||||
|
else
|
||||||
|
this.command += key;
|
||||||
|
return this.events;
|
||||||
|
},
|
||||||
|
|
||||||
|
process: function process(event) {
|
||||||
|
this.append(event);
|
||||||
|
this.waiting = false;
|
||||||
|
return this.onKeyPress(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
execute: function execute(map, args)
|
||||||
|
let (self = this)
|
||||||
|
function execute() {
|
||||||
|
if (self.preExecute)
|
||||||
|
self.preExecute.apply(self, args);
|
||||||
|
|
||||||
|
args.self = self.main.params.mappingSelf || self.main.mappingSelf || map;
|
||||||
|
let res = map.execute.call(map, args);
|
||||||
|
|
||||||
|
if (self.postExecute)
|
||||||
|
self.postExecute.apply(self, args);
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
|
||||||
|
onKeyPress: function onKeyPress(event) {
|
||||||
|
if (event.skipmap)
|
||||||
|
return Events.ABORT;
|
||||||
|
|
||||||
|
if (!this.command)
|
||||||
|
return Events.WAIT;
|
||||||
|
|
||||||
|
var map = this.hive.get(this.main, this.command);
|
||||||
|
this.waiting = this.hive.getCandidates(this.main, this.command);
|
||||||
|
if (map) {
|
||||||
|
if (map.arg)
|
||||||
|
return KeyArgProcessor(this, map, false, "arg");
|
||||||
|
else if (map.motion)
|
||||||
|
return KeyArgProcessor(this, map, true, "motion");
|
||||||
|
|
||||||
|
return this.execute(map, {
|
||||||
|
keyEvents: this.keyEvents,
|
||||||
|
command: this.command,
|
||||||
|
count: this.count,
|
||||||
|
keypressEvents: this.events
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.waiting)
|
||||||
|
return this.main.insert ? Events.PASS : Events.ABORT;
|
||||||
|
|
||||||
|
return Events.WAIT;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var KeyArgProcessor = Class("KeyArgProcessor", KeyProcessor, {
|
||||||
|
init: function init(input, map, wantCount, argName) {
|
||||||
|
init.supercall(this, input.main, input.hive);
|
||||||
|
this.map = map;
|
||||||
|
this.parent = input;
|
||||||
|
this.argName = argName;
|
||||||
|
this.wantCount = wantCount;
|
||||||
|
},
|
||||||
|
|
||||||
|
extended: true,
|
||||||
|
|
||||||
|
onKeyPress: function onKeyPress(event) {
|
||||||
|
if (Events.isEscape(event))
|
||||||
|
return Events.KILL;
|
||||||
|
if (!this.command)
|
||||||
|
return Events.WAIT;
|
||||||
|
|
||||||
|
let args = {
|
||||||
|
command: this.parent.command,
|
||||||
|
count: this.count || this.parent.count,
|
||||||
|
events: this.parent.events.concat(this.events)
|
||||||
|
};
|
||||||
|
args[this.argName] = this.command;
|
||||||
|
|
||||||
|
return this.execute(this.map, args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@@ -609,7 +609,7 @@ var Modes = Module("modes", {
|
|||||||
mappings.add([modes.MENU], ["<Esc>"],
|
mappings.add([modes.MENU], ["<Esc>"],
|
||||||
"Close the current popup",
|
"Close the current popup",
|
||||||
function () {
|
function () {
|
||||||
if (modes.popup.active.length)
|
if (events.popups.active.length)
|
||||||
return Events.PASS_THROUGH;
|
return Events.PASS_THROUGH;
|
||||||
modes.pop();
|
modes.pop();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ var ConfigBase = Class("ConfigBase", {
|
|||||||
"editor",
|
"editor",
|
||||||
"events",
|
"events",
|
||||||
"hints",
|
"hints",
|
||||||
|
"key-processors",
|
||||||
"mappings",
|
"mappings",
|
||||||
"marks",
|
"marks",
|
||||||
"mow",
|
"mow",
|
||||||
|
|||||||
Reference in New Issue
Block a user