1 // Copyright (c) 2008-2012 Kris Maglione <maglione.k at Gmail>
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
9 var ProcessorStack = Class("ProcessorStack", {
10 init: function (mode, hives, builtin) {
11 this.main = mode.main;
17 events.dbg("STACK " + mode);
19 let main = { __proto__: mode.main, params: mode.params };
20 this.modes = array([mode.params.keyModes, main, mode.main.allBases.slice(1)]).flatten().compact();
23 hives = hives.filter(function (h) h.name === "builtin");
25 this.processors = this.modes.map(function (m) hives.map(function (h) KeyProcessor(m, h)))
27 this.ownsBuffer = !this.processors.some(function (p) p.main.ownsBuffer);
29 for (let [i, input] in Iterator(this.processors)) {
30 let params = input.main.params;
32 if (params.preExecute)
33 input.preExecute = params.preExecute;
35 if (params.postExecute)
36 input.postExecute = params.postExecute;
38 if (params.onKeyPress && input.hive === mappings.builtin)
39 input.fallthrough = function fallthrough(events) {
40 return params.onKeyPress(events) === false ? Events.KILL : Events.PASS;
44 let hive = options.get("passkeys")[this.main.input ? "inputHive" : "commandHive"];
45 if (!builtin && hive.active && (!dactyl.focusedElement || events.isContentNode(dactyl.focusedElement)))
46 this.processors.unshift(KeyProcessor(modes.BASE, hive));
49 passUnknown: Class.Memoize(function () options.get("passunknown").getKey(this.modes)),
52 events.dbg("NOTIFY()");
53 events.keyEvents = [];
54 events.processor = null;
55 if (!this.execute(undefined, true)) {
56 events.processor = this;
57 events.keyEvents = this.keyEvents;
61 _result: function (result) (result === Events.KILL ? "KILL" :
62 result === Events.PASS ? "PASS" :
63 result === Events.PASS_THROUGH ? "PASS_THROUGH" :
64 result === Events.ABORT ? "ABORT" :
65 callable(result) ? result.toSource().substr(0, 50) : result),
67 execute: function execute(result, force) {
68 events.dbg("EXECUTE(" + this._result(result) + ", " + force + ") events:" + this.events.length
69 + " processors:" + this.processors.length + " actions:" + this.actions.length);
71 let processors = this.processors;
78 statusline.inputBuffer = this.processors.length ? this.buffer : "";
80 if (!this.processors.some(function (p) !p.extended) && this.actions.length) {
81 // We have matching actions and no processors other than
82 // those waiting on further arguments. Execute actions as
83 // long as they continue to return PASS.
85 for (var action in values(this.actions)) {
86 while (callable(action)) {
87 length = action.eventLength;
88 action = dactyl.trapErrors(action);
89 events.dbg("ACTION RES: " + length + " " + this._result(action));
91 if (action !== Events.PASS)
95 // Result is the result of the last action. Unless it's
96 // PASS, kill any remaining argument processors.
97 result = action !== undefined ? action : Events.KILL;
98 if (action !== Events.PASS)
99 this.processors.length = 0;
101 else if (this.processors.length) {
102 // We're still waiting on the longest matching processor.
103 // Kill the event, set a timeout to give up waiting if applicable.
105 result = Events.KILL;
106 if (options["timeout"] && (this.actions.length || events.hasNativeKey(this.events[0], this.main, this.passUnknown)))
107 this.timer = services.Timer(this, options["timeoutlen"], services.Timer.TYPE_ONE_SHOT);
109 else if (result !== Events.KILL && !this.actions.length &&
110 !(this.events[0].isReplay || this.passUnknown
111 || this.modes.some(function (m) m.passEvent(this), this.events[0]))) {
112 // No patching processors, this isn't a fake, pass-through
113 // event, we're not in pass-through mode, and we're not
114 // choosing to pass unknown keys. Kill the event and beep.
116 result = Events.ABORT;
117 if (!Events.isEscape(this.events.slice(-1)[0]))
119 events.feedingKeys = false;
121 else if (result === undefined)
122 // No matching processors, we're willing to pass this event,
123 // and we don't have a default action from a processor. Just
125 result = Events.PASS;
127 events.dbg("RESULT: " + length + " " + this._result(result) + "\n\n");
129 if (result !== Events.PASS || this.events.length > 1)
130 if (result !== Events.ABORT || !this.events[0].isReplay)
131 Events.kill(this.events[this.events.length - 1]);
133 if (result === Events.PASS_THROUGH || result === Events.PASS && this.passUnknown)
134 events.passing = true;
136 if (result === Events.PASS_THROUGH && this.keyEvents.length)
137 events.dbg("PASS_THROUGH:\n\t" + this.keyEvents.map(function (e) [e.type, DOM.Event.stringify(e)]).join("\n\t"));
139 if (result === Events.PASS_THROUGH)
140 events.feedevents(null, this.keyEvents, { skipmap: true, isMacro: true, isReplay: true });
142 let list = this.events.filter(function (e) e.getPreventDefault() && !e.dactylDefaultPrevented);
144 if (result === Events.PASS)
145 events.dbg("PASS THROUGH: " + list.slice(0, length).filter(function (e) e.type === "keypress").map(DOM.Event.closure.stringify));
146 if (list.length > length)
147 events.dbg("REFEED: " + list.slice(length).filter(function (e) e.type === "keypress").map(DOM.Event.closure.stringify));
149 if (result === Events.PASS)
150 events.feedevents(null, list.slice(0, length), { skipmap: true, isMacro: true, isReplay: true });
151 if (list.length > length && this.processors.length === 0)
152 events.feedevents(null, list.slice(length));
155 return this.processors.length === 0;
158 process: function process(event) {
162 let key = event.dactylString || DOM.Event.stringify(event);
163 this.events.push(event);
165 this.keyEvents.push(event);
172 events.dbg("PROCESS(" + key + ") skipmap: " + event.skipmap + " macro: " + event.isMacro + " replay: " + event.isReplay);
174 for (let [i, input] in Iterator(this.processors)) {
175 let res = input.process(event);
176 if (res !== Events.ABORT)
179 events.dbg("RES: " + input + " " + this._result(res));
181 if (res === Events.KILL)
187 if (res === Events.WAIT || input.waiting)
188 processors.push(input);
189 if (isinstance(res, KeyProcessor))
190 processors.push(res);
193 events.dbg("RESULT: " + event.getPreventDefault() + " " + this._result(result));
194 events.dbg("ACTIONS: " + actions.length + " " + this.actions.length);
195 events.dbg("PROCESSORS:", processors, "\n");
197 this._actions = actions;
198 this.actions = actions.concat(this.actions);
200 for (let action in values(actions))
201 if (!("eventLength" in action))
202 action.eventLength = this.events.length;
204 if (result === Events.KILL)
206 else if (!this.actions.length && !processors.length)
207 for (let input in values(this.processors))
208 if (input.fallthrough) {
209 if (result === Events.KILL)
211 result = dactyl.trapErrors(input.fallthrough, input, this.events);
214 this.processors = processors;
216 return this.execute(result, options["timeout"] && options["timeoutlen"] === 0);
220 var KeyProcessor = Class("KeyProcessor", {
221 init: function init(main, hive) {
225 this.wantCount = this.main.count;
228 get toStringParams() [this.main.name, this.hive.name],
232 get count() this.countStr ? Number(this.countStr) : this.main.params.count || null,
234 append: function append(event) {
235 this.events.push(event);
236 let key = event.dactylString || DOM.Event.stringify(event);
238 if (this.wantCount && !this.command &&
239 (this.countStr ? /^[0-9]$/ : /^[1-9]$/).test(key))
240 this.countStr += key;
246 process: function process(event) {
248 this.waiting = false;
249 return this.onKeyPress(event);
252 execute: function execute(map, args)
255 this.preExecute.apply(this, args);
257 args.self = this.main.params.mappingSelf || this.main.mappingSelf || map;
258 let res = map.execute.call(map, args);
260 if (this.postExecute)
261 this.postExecute.apply(this, args);
265 onKeyPress: function onKeyPress(event) {
272 var map = this.hive.get(this.main, this.command);
273 this.waiting = this.hive.getCandidates(this.main, this.command);
276 return KeyArgProcessor(this, map, false, "arg");
278 return KeyArgProcessor(this, map, true, "motion");
280 return this.execute(map, {
281 command: this.command,
283 keyEvents: events.keyEvents,
284 keypressEvents: this.events
289 return this.main.insert ? Events.PASS : Events.ABORT;
295 var KeyArgProcessor = Class("KeyArgProcessor", KeyProcessor, {
296 init: function init(input, map, wantCount, argName) {
297 init.supercall(this, input.main, input.hive);
300 this.argName = argName;
301 this.wantCount = wantCount;
306 onKeyPress: function onKeyPress(event) {
307 if (Events.isEscape(event))
313 command: this.parent.command,
314 count: this.count || this.parent.count,
315 keyEvents: events.keyEvents,
316 keypressEvents: this.parent.events.concat(this.events)
318 args[this.argName] = this.command;
320 return this.execute(this.map, args);
324 // vim: set fdm=marker sw=4 sts=4 ts=8 et: