diff --git a/common/content/browser.js b/common/content/browser.js index fd00987e..ac647fee 100644 --- a/common/content/browser.js +++ b/common/content/browser.js @@ -65,7 +65,7 @@ var Browser = Module("browser", { mappings: function () { mappings.add([modes.NORMAL], - ["y"], "Yank current location to the clipboard", + ["y", ""], "Yank current location to the clipboard", function () { dactyl.clipboardWrite(buffer.uri.spec, true); }); // opening websites diff --git a/common/content/buffer.js b/common/content/buffer.js index 1bbaab99..05223fe4 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -1651,7 +1651,7 @@ var Buffer = Module("buffer", { mappings: function () { var myModes = config.browserModes; - mappings.add(myModes, ["."], + mappings.add(myModes, [".", ""], "Repeat the last key event", function (args) { if (mappings.repeat) { @@ -1670,31 +1670,31 @@ var Buffer = Module("buffer", { function () { ex.stop(); }); // scrolling - mappings.add(myModes, ["j", "", ""], + mappings.add(myModes, ["j", "", "", ""], "Scroll document down", function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); }, { count: true }); - mappings.add(myModes, ["k", "", ""], + mappings.add(myModes, ["k", "", "", ""], "Scroll document up", function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); }, { count: true }); - mappings.add(myModes, dactyl.has("mail") ? ["h"] : ["h", ""], + mappings.add(myModes, dactyl.has("mail") ? ["h", ""] : ["h", "", ""], "Scroll document to the left", function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); }, { count: true }); - mappings.add(myModes, dactyl.has("mail") ? ["l"] : ["l", ""], + mappings.add(myModes, dactyl.has("mail") ? ["l", ""] : ["l", "", ""], "Scroll document to the right", function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); }, { count: true }); - mappings.add(myModes, ["0", "^"], + mappings.add(myModes, ["0", "^", ""], "Scroll to the absolute left of the document", function () { buffer.scrollToPercent(0, null); }); - mappings.add(myModes, ["$"], + mappings.add(myModes, ["$", ""], "Scroll to the absolute right of the document", function () { buffer.scrollToPercent(100, null); }); @@ -1708,7 +1708,7 @@ var Buffer = Module("buffer", { function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); }, { count: true }); - mappings.add(myModes, ["%"], + mappings.add(myModes, ["%", ""], "Scroll to {count} percent of the document", function (args) { dactyl.assert(args.count > 0 && args.count <= 100); @@ -1716,59 +1716,59 @@ var Buffer = Module("buffer", { }, { count: true }); - mappings.add(myModes, [""], + mappings.add(myModes, ["", ""], "Scroll window downwards in the buffer", function (args) { buffer._scrollByScrollSize(args.count, true); }, { count: true }); - mappings.add(myModes, [""], + mappings.add(myModes, ["", ""], "Scroll window upwards in the buffer", function (args) { buffer._scrollByScrollSize(args.count, false); }, { count: true }); - mappings.add(myModes, ["", "", ""], + mappings.add(myModes, ["", "", "", ""], "Scroll up a full page", function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); }, { count: true }); - mappings.add(myModes, ["", "", ""], + mappings.add(myModes, ["", "", "", ""], "Scroll down a full page", function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); }, { count: true }); - mappings.add(myModes, ["]f"], + mappings.add(myModes, ["]f", ""], "Focus next frame", function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); }, { count: true }); - mappings.add(myModes, ["[f"], + mappings.add(myModes, ["[f", ""], "Focus previous frame", function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); }, { count: true }); - mappings.add(myModes, ["]]"], + mappings.add(myModes, ["]]", ""], "Follow the link labeled 'next' or '>' if it exists", function (args) { buffer.findLink("next", options["nextpattern"], (args.count || 1) - 1, true); }, { count: true }); - mappings.add(myModes, ["[["], + mappings.add(myModes, ["[[", ""], "Follow the link labeled 'prev', 'previous' or '<' if it exists", function (args) { buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true); }, { count: true }); - mappings.add(myModes, ["gf"], + mappings.add(myModes, ["gf", ""], "Toggle between rendered and source view", function () { buffer.viewSource(null, false); }); - mappings.add(myModes, ["gF"], + mappings.add(myModes, ["gF", ""], "View source with an external editor", function () { buffer.viewSource(null, true); }); - mappings.add(myModes, ["gi"], + mappings.add(myModes, ["gi", ""], "Focus last used input field", function (args) { let elem = buffer.lastInputField; @@ -1808,7 +1808,7 @@ var Buffer = Module("buffer", { dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB, background: true }); }); - mappings.add(myModes, ["p", ""], + mappings.add(myModes, ["p", "", ""], "Open (put) a URL based on the current clipboard contents in the current buffer", function () { let url = dactyl.clipboardRead(); @@ -1816,7 +1816,7 @@ var Buffer = Module("buffer", { dactyl.open(url); }); - mappings.add(myModes, ["P"], + mappings.add(myModes, ["P", ""], "Open (put) a URL based on the current clipboard contents in a new buffer", function () { let url = dactyl.clipboardRead(); @@ -1825,16 +1825,16 @@ var Buffer = Module("buffer", { }); // reloading - mappings.add(myModes, ["r"], + mappings.add(myModes, ["r", ""], "Reload the current web page", function () { tabs.reload(tabs.getTab(), false); }); - mappings.add(myModes, ["R"], + mappings.add(myModes, ["R", ""], "Reload while skipping the cache", function () { tabs.reload(tabs.getTab(), true); }); // yanking - mappings.add(myModes, ["Y"], + mappings.add(myModes, ["Y", ""], "Copy selected text or current word", function () { let sel = buffer.getCurrentWord(); @@ -1843,62 +1843,62 @@ var Buffer = Module("buffer", { }); // zooming - mappings.add(myModes, ["zi", "+"], + mappings.add(myModes, ["zi", "+", ""], "Enlarge text zoom of current web page", function (args) { buffer.zoomIn(Math.max(args.count, 1), false); }, { count: true }); - mappings.add(myModes, ["zm"], + mappings.add(myModes, ["zm", ""], "Enlarge text zoom of current web page by a larger amount", function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, false); }, { count: true }); - mappings.add(myModes, ["zo", "-"], + mappings.add(myModes, ["zo", "-", ""], "Reduce text zoom of current web page", function (args) { buffer.zoomOut(Math.max(args.count, 1), false); }, { count: true }); - mappings.add(myModes, ["zr"], + mappings.add(myModes, ["zr", ""], "Reduce text zoom of current web page by a larger amount", function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, false); }, { count: true }); - mappings.add(myModes, ["zz"], + mappings.add(myModes, ["zz", ""], "Set text zoom value of current web page", function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, false); }, { count: true }); - mappings.add(myModes, ["ZI", "zI"], + mappings.add(myModes, ["ZI", "zI", ""], "Enlarge full zoom of current web page", function (args) { buffer.zoomIn(Math.max(args.count, 1), true); }, { count: true }); - mappings.add(myModes, ["ZM", "zM"], + mappings.add(myModes, ["ZM", "zM", ""], "Enlarge full zoom of current web page by a larger amount", function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, true); }, { count: true }); - mappings.add(myModes, ["ZO", "zO"], + mappings.add(myModes, ["ZO", "zO", ""], "Reduce full zoom of current web page", function (args) { buffer.zoomOut(Math.max(args.count, 1), true); }, { count: true }); - mappings.add(myModes, ["ZR", "zR"], + mappings.add(myModes, ["ZR", "zR", ""], "Reduce full zoom of current web page by a larger amount", function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, true); }, { count: true }); - mappings.add(myModes, ["zZ"], + mappings.add(myModes, ["zZ", ""], "Set full zoom value of current web page", function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, true); }, { count: true }); // page info - mappings.add(myModes, [""], + mappings.add(myModes, ["", ""], "Print the current file name", function () { buffer.showPageInfo(false); }); - mappings.add(myModes, ["g"], + mappings.add(myModes, ["g", ""], "Print file information", function () { buffer.showPageInfo(true); }); }, diff --git a/common/content/commandline.js b/common/content/commandline.js index 0e98e956..159a1de9 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -372,7 +372,6 @@ var CommandMode = Class("CommandMode", { if (!this.completions.itemList.visible) this.autocompleteTimer.flush(); } - util.dump("input", commandline.command, this.widgets.command[1]); this.onChange(commandline.command); }, keyup: function onKeyUp(event) { diff --git a/common/content/events.js b/common/content/events.js index b0cfecee..e6c3400a 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -33,6 +33,7 @@ var ProcessorStack = Class("ProcessorStack", { process: function process(event) { function dbg() {} + let dbg = util.closure.dump; let key = events.toString(event); this.events.push(event); @@ -90,21 +91,20 @@ var ProcessorStack = Class("ProcessorStack", { if (processors.length) result = Events.KILL; else if (this.actions.length) { - if (actions.length == 0) + if (actions.length == 0) { dactyl.beep(); - - if (modes.replaying && !events.waitForPageLoad()) - result = Events.KILL; - else { - for (var res = this.actions[0]; callable(res);) - res = res(); - result = res === Events.PASS ? Events.PASS : Events.KILL; + events.feedingKeys = false; } + + for (var res = this.actions[0]; callable(res);) + res = res(); + result = res === Events.PASS ? Events.PASS : Events.KILL; } else if (result !== Events.KILL && !this.actions.length && processors.some(function (p) !p.main.passUnknown)) { result = Events.KILL; dactyl.beep(); + events.feedingKeys = false; } else if (result === undefined) result = Events.PASS; @@ -115,9 +115,11 @@ var ProcessorStack = Class("ProcessorStack", { if (result === Events.PASS || result === Events.ABORT) this.events.filter(function (e) e.getPreventDefault()) .forEach(function (event, i) { + let elem = event.originalTarget; if (event.originalTarget) { - let evt = events.create(event.originalTarget.ownerDocument, event.type, event); - events.dispatch(event.originalTarget, evt, { skipmap: true, isMacro: true }); + let doc = elem.ownerDocument || elem.document || elem; + let evt = events.create(doc, event.type, event); + events.dispatch(elem, evt, { skipmap: true, isMacro: true, isReplay: true }); } else if (i > 0) events.events.keypress.call(events, event); @@ -491,6 +493,8 @@ var Events = Module("events", { util.threadYield(1, true); for (let [, evt_obj] in Iterator(events.fromString(keys))) { + let now = Date.now(); + util.dump("+feed", events.toString(evt_obj)); for (let type in values(["keydown", "keyup", "keypress"])) { let evt = update({}, evt_obj, { type: type }); @@ -508,13 +512,10 @@ var Events = Module("events", { else events.events.keypress.call(events, event); } + util.dump("-feed", events.toString(evt_obj), this.feedingKeys); if (!this.feedingKeys) break; - - // Stop feeding keys if page loading failed. - if (modes.replaying && !this.waitForPageLoad()) - break; } } catch (e) { @@ -594,9 +595,14 @@ var Events = Module("events", { * of x. * * @param {string} keys Messy form. + * @param {boolean} unknownOk Whether unknown keys are passed + * through rather than being converted to keyname>. + * @default false * @returns {string} Canonical form. */ canonicalKeys: function (keys, unknownOk) { + if (arguments.length === 1) + unknownOk = true; return events.fromString(keys, unknownOk).map(events.closure.toString).join(""); }, @@ -1035,7 +1041,7 @@ var Events = Module("events", { if (!key) return null; - if (modes.recording && (!this._input || !mappings.user.hasMap(modes.main, this._input.buffer + key))) + if (modes.recording && !event.isReplay) events._macroKeys.push(key); // feedingKeys needs to be separate from interrupted so @@ -1043,8 +1049,6 @@ var Events = Module("events", { // interrupting whatever it's started and a real // interrupting our playback. if (events.feedingKeys && !event.isMacro) { - if (!event.originalTarget) - util.dumpStack(); if (key == "") { events.feedingKeys = false; if (modes.replaying) { @@ -1323,11 +1327,11 @@ var Events = Module("events", { }, mappings: function () { mappings.add(modes.all, - [""], "Temporarily ignore all " + config.appName + " key bindings", + ["", ""], "Temporarily ignore all " + config.appName + " key bindings", function () { modes.push(modes.PASS_THROUGH); }); mappings.add(modes.all, - [""], "Pass through next key", + ["", ""], "Pass through next key", function () { if (modes.main == modes.QUOTE) return Events.PASS; @@ -1340,7 +1344,7 @@ var Events = Module("events", { // macros mappings.add([modes.NORMAL, modes.TEXT_AREA, modes.PLAYER].filter(util.identity), - ["q"], "Record a key sequence into a macro", + ["q", ""], "Record a key sequence into a macro", function ({ arg }) { events._macroKeys.pop(); events[modes.recording ? "finishRecording" : "startRecording"](arg); @@ -1348,13 +1352,34 @@ var Events = Module("events", { { get arg() !modes.recording }); mappings.add([modes.NORMAL, modes.TEXT_AREA, modes.PLAYER].filter(util.identity), - ["@"], "Play a macro", + ["@", ""], "Play a macro", function ({ arg, count }) { count = Math.max(count, 1); while (count-- && events.playMacro(arg)) ; }, { arg: true, count: true }); + + mappings.add([modes.COMMAND], + ["s", ""], "Sleep for {count} milliseconds before continuing macro playback", + function ({ command, count }) { + let now = Date.now(); + util.dump("+", count); + dactyl.assert(count, "Count required for " + command); + if (events.feedingKeys) + util.sleep(count); + util.dump("-", count, now - Date.now()); + }, + { count: true }); + + mappings.add([modes.MAIN], + ["l", ""], "Wait for the current page to finish loading before continuing macro playback", + function () { + if (!events.waitForPageLoad()) { + util.interrupted = true; + throw Error("Interrupted"); + } + }); }, options: function () { options.add(["passkeys", "pk"], diff --git a/common/content/mappings.js b/common/content/mappings.js index b5255d77..1beae7bb 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -113,9 +113,17 @@ var Map = Class("Map", { if (this.executing) util.dumpStack("Attempt to execute mapping recursively: " + args.command); dactyl.assert(!this.executing, "Attempt to execute mapping recursively: " + args.command); - this.executing = true; - let res = dactyl.trapErrors(repeat); - this.executing = false; + + try { + this.executing = true; + var res = repeat(); + } + catch (e) { + events.feedingKeys = false; + } + finally { + this.executing = false; + } return res; } diff --git a/common/locale/en-US/browsing.xml b/common/locale/en-US/browsing.xml index fb48cf46..03011a37 100644 --- a/common/locale/en-US/browsing.xml +++ b/common/locale/en-US/browsing.xml @@ -20,7 +20,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to &dactyl.host; or to a web page, you have two options: - CTRL-Z]]> + CTRL-Z]]> <C-z>

@@ -33,7 +33,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to - CTRL-V]]> + CTRL-V]]> <C-v>

@@ -171,7 +171,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to - p]]> + p]]> p @@ -185,7 +185,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to - P + <tab-open-clipboard-url> P P @@ -343,7 +343,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to

Reloading

- r + <reload> r r

Reload the current web page.

@@ -351,7 +351,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
- R + <full-reload> R R

Reload the current web page without using the cache.

diff --git a/common/locale/en-US/buffer.xml b/common/locale/en-US/buffer.xml index 5dbcfae5..66d0d978 100644 --- a/common/locale/en-US/buffer.xml +++ b/common/locale/en-US/buffer.xml @@ -22,7 +22,7 @@

Buffer information

- ]]> + ]]> <C-g> @@ -35,7 +35,7 @@ - ]]> + g]]> g<C-g>

Print file information. Same as :pageinfo.

@@ -95,7 +95,7 @@

Motion commands

- ^ 0 + <scroll-begin> ^ 0 0 @@ -107,7 +107,7 @@ - $ + <scroll-end> $ $

Scroll to the absolute right of the document

@@ -140,7 +140,7 @@
- N% + <scroll-percent> N% count%

Scroll to count percent of the document.

@@ -148,7 +148,7 @@
- h]]> + h]]> counth @@ -160,7 +160,7 @@ - j]]> + j]]> countj @@ -172,7 +172,7 @@ - k]]> + k]]> countk @@ -184,7 +184,7 @@ - l]]> + l]]> countl @@ -196,7 +196,7 @@ - ]]> + ]]> count<C-d> @@ -209,7 +209,7 @@ - ]]> + ]]> count<C-u> @@ -222,7 +222,7 @@ - ]]> + ]]> count<C-b> @@ -234,7 +234,7 @@ - ]]> + ]]> count<C-f> @@ -264,7 +264,7 @@ - gi + <focus-input> gi countgi @@ -277,7 +277,7 @@ - ]f + <next-frame> ]f count]f @@ -290,7 +290,7 @@ - [f + <previous-frame> [f count[f @@ -303,7 +303,7 @@ - ]] + <next-page> ]] count]] @@ -316,7 +316,7 @@ - [[ + <previous-page> [[ count[[ @@ -361,7 +361,7 @@ - + zi + + zi]]> countzi

Enlarge text zoom of current web page. Mnemonic: zoom in.

@@ -369,7 +369,7 @@
- zm + zm]]> countzm @@ -378,7 +378,7 @@ - - zo + - zo]]> countzo

Reduce text zoom of current web page. Mnemonic: zoom out.

@@ -386,7 +386,7 @@
- zr + zr]]> countzr

Reduce text zoom of current web page by a larger amount. Mnemonic: zoom reduce.

@@ -394,7 +394,7 @@
- zz + zz]]> countzz @@ -407,7 +407,7 @@ - ZI zI + ZI zI]]> countZI

Enlarge full zoom of current web page. Mnemonic: zoom in.

@@ -415,7 +415,7 @@
- ZM zM + ZM zM]]> countZM @@ -424,7 +424,7 @@ - ZO zO + ZO zO]]> countZO

Reduce full zoom of current web page. Mnemonic: zoom out.

@@ -432,7 +432,7 @@
- ZR zR + ZR zR]]> countZR

Reduce full zoom of current web page by a larger amount. Mnemonic: zoom reduce.

@@ -440,7 +440,7 @@
- zZ + zZ]]> countzZ @@ -489,7 +489,7 @@

- y + <yank-location> y y

Yank current location to the clipboard.

@@ -497,7 +497,7 @@
- Y + <yank-word> Y Y

Copy currently selected text to the system clipboard.

diff --git a/common/locale/en-US/repeat.xml b/common/locale/en-US/repeat.xml index 753bc48f..8070ce21 100644 --- a/common/locale/en-US/repeat.xml +++ b/common/locale/en-US/repeat.xml @@ -21,7 +21,7 @@

Single repeats

- . + <repeat-key> . count. @@ -45,7 +45,7 @@

Macros

- q + <record-macro> q q0-9a-zA-Z @@ -82,7 +82,7 @@ - @ + <play-macro> @ count@a-z0-9

@@ -100,6 +100,38 @@ +

Macro utilities

+ +

+ The following key bindings facilitate the recording of efficient + macros. They have no effect when typed normally, but are + recorded and take effect during macro playback. +

+ + + s + + count<m>s + +

+ Sleep for count milliseconds before resuming playback. +

+
+
+ + + l]]> + + l]]> + +

+ Wait for the current page to finish loading before resuming + playback. +

+
+
+ +

Using scripts

diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS index 88787654..189ac3bf 100644 --- a/pentadactyl/NEWS +++ b/pentadactyl/NEWS @@ -66,6 +66,8 @@ and linking to source code locations). - :downloads now opens a download list in the multi-line output buffer. + - Added :mapgroup command and -group flag to :map, :unmap, and + :mapclear. - :extensions has been replaced with a more powerful :addons. - Added :cookies command. - :extadd now supports remote URLs as well as local files on Firefox 4. @@ -91,6 +93,14 @@ - :command now accepts comma-separated alternative command names. - :command -complete custom now also accepts a completions array, see :h :command-completion-custom. + * Mapping changes: + - Added site-specific mapping groups and related command + changes. + - Added named aliases for many mappings + - Added l and s to aid in the construction of macros. + - Removed the implicit page load delays during macro playback. + - Added the base modes Base, Main, and Command which other + modes inherit key bindings from. * Improvements to :style and :highlight: - Added -agent flag to :style. - The -append flag now updates existing properties rather than