diff --git a/common/content/autocommands.js b/common/content/autocommands.js index 65b083ec..d225c713 100644 --- a/common/content/autocommands.js +++ b/common/content/autocommands.js @@ -128,16 +128,7 @@ const AutoCommands = Module("autocommands", { lastPattern = autoCmd.pattern; dactyl.echomsg("autocommand " + autoCmd.command, 9); - if (typeof autoCmd.command == "function") { - try { - autoCmd.command.call(autoCmd, args); - } - catch (e) { - dactyl.echoerr(e); - } - } - else - dactyl.execute(commands.replaceTokens(autoCmd.command, args), null, true); + autoCmd.command(args); } } } @@ -173,8 +164,16 @@ const AutoCommands = Module("autocommands", { if (args.length > 2) { // add new command, possibly removing all others with the same event/pattern if (args.bang) autocommands.remove(event, regex); - if (args["-javascript"]) + if (args["-javascript"]) { cmd = dactyl.userFunc("args", "with(args) {" + cmd + "}"); + cmd.toString = function toString() "-javascript " + cmd.source; + } + else { + cmd = function cmd(args) dactyl.execute(commands.replaceTokens(cmd.source, args), null, true, cmd.sourcing); + cmd.sourcing = io.sourcing && update({}, io.sourcing); + cmd.toString = function toString() cmd.source; + } + cmd.source = args[2]; autocommands.add(events, regex, cmd); } else { diff --git a/common/content/commandline.js b/common/content/commandline.js index 8f5c3a90..f9895d27 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -404,14 +404,10 @@ const CommandLine = Module("commandline", { }, runSilently: function (func, self) { - let wasSilent = this._silent; - this._silent = true; - try { + this.withSavedValues(["_silent"], function () { + this._silent = true; func.call(self); - } - finally { - this._silent = wasSilent; - } + }); }, hideCompletions: function () { diff --git a/common/content/commands.js b/common/content/commands.js index 5d594579..674fec5b 100644 --- a/common/content/commands.js +++ b/common/content/commands.js @@ -997,7 +997,7 @@ const Commands = Module("commands", { count: this.count && args.count }; - dactyl.execute(commands.replaceTokens(this.replacementText, tokens)); + dactyl.execute(commands.replaceTokens(this.replacementText, tokens), null, true, this.sourcing); } // TODO: offer completion.ex? @@ -1062,7 +1062,8 @@ const Commands = Module("commands", { bang: bangOpt, count: countOpt, completer: completeFunc, - replacementText: args.literalArg + replacementText: args.literalArg, + sourcing: io.sourcing && update({}, io.sourcing) }, args.bang); if (!added) diff --git a/common/content/dactyl.js b/common/content/dactyl.js index cd1db29e..d2a0bdf6 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -330,7 +330,7 @@ const Dactyl = Module("dactyl", { */ userFunc: function () { return this.userEval( - "(function (" + + "(function userFunction(" + Array.slice(arguments, 0, -1).join(", ") + ") { " + arguments[arguments.length - 1] + " })"); }, @@ -344,7 +344,7 @@ const Dactyl = Module("dactyl", { * @param {boolean} silent Whether the command should be echoed on the * command line. */ - execute: function (str, modifiers, silent) { + execute: function (str, modifiers, silent, sourcing) { // skip comments and blank lines if (/^\s*("|$)/.test(str)) return; @@ -359,7 +359,10 @@ const Dactyl = Module("dactyl", { if (!silent) commandline.command = str.replace(/^\s*:\s*/, ""); - command.execute(args, modifiers); + io.withSavedValues(["sourcing"], function () { + io.sourcing = sourcing || io.sourcing; + command.execute(args, modifiers); + }); } }, diff --git a/common/content/finder.js b/common/content/finder.js index 8053ac98..d3182b7e 100644 --- a/common/content/finder.js +++ b/common/content/finder.js @@ -412,7 +412,7 @@ const RangeFind = Class("RangeFind", { }, iter: function (word) { - let saved = ["range", "lastRange", "lastString"].map(function (s) [s, this[s]], this); + let saved = ["lastRange", "lastString", "range"].map(function (s) [s, this[s]], this); try { this.range = this.ranges[0]; this.lastRange = null; diff --git a/common/content/io.js b/common/content/io.js index bc3abb5c..c8ad1faa 100644 --- a/common/content/io.js +++ b/common/content/io.js @@ -317,104 +317,102 @@ lookup: * @param {boolean} silent Whether errors should be reported. */ source: function (filename, silent) { - let wasSourcing = this.sourcing; - let readHeredoc = this.readHeredoc; defineModule.loadLog.push("sourcing " + filename); let time = Date.now(); - try { - var file = io.File(filename); - this.sourcing = { - file: file.path, - line: 0 - }; - - if (!file.exists() || !file.isReadable() || file.isDirectory()) { - if (!silent) - dactyl.echoerr("E484: Can't open file " + filename.quote()); - return; - } - - dactyl.echomsg("sourcing " + filename.quote(), 2); - - let uri = services.get("io").newFileURI(file); - - // handle pure JavaScript files specially - if (/\.js$/.test(filename)) { - try { - dactyl.loadScript(uri.spec, Script(file)); - dactyl.helpInitialized = false; - } - catch (e) { - if (isString(e)) - e = { message: e }; - let err = new Error(); - for (let [k, v] in Iterator(e)) - err[k] = v; - err.echoerr = <>{file.path}:{e.lineNumber}: {e}; - throw err; - } - } - else if (/\.css$/.test(filename)) - storage.styles.registerSheet(uri.spec, false, true); - else { - let heredoc = ""; - let heredocEnd = null; // the string which ends the heredoc - let str = file.read(); - let lines = str.split(/\r\n|[\r\n]/); - - this.readHeredoc = function (end) { - let res = []; - try { - io.sourcing.line++; - while (true) - let ([i, line] = iter.next()) { - if (line === end) - return res.join("\n"); - res.push(line); - } - } - catch (e if e instanceof StopIteration) {} - dactyl.assert(false, "Unexpected end of file waiting for " + end); + this.withSavedValues(["readHeredoc", "sourcing"], function () { + try { + var file = io.File(filename); + this.sourcing = { + file: file.path, + line: 0 }; - let iter = Iterator(lines); - for (let [i, line] in iter) { - if (this.sourcing.finished) - break; - this.sourcing.line = i + 1; - // skip line comments and blank lines - line = line.replace(/\r$/, ""); - - if (!/^\s*(".*)?$/.test(line)) - try { - dactyl.execute(line, { setFrom: file }, true); - } - catch (e) { - dactyl.echoerr("Error detected while processing " + file.path); - dactyl.echomsg("line\t" + this.sourcing.line + ":"); - dactyl.reportError(e, true); - } + if (!file.exists() || !file.isReadable() || file.isDirectory()) { + if (!silent) + dactyl.echoerr("E484: Can't open file " + filename.quote()); + return; } + + dactyl.echomsg("sourcing " + filename.quote(), 2); + + let uri = services.get("io").newFileURI(file); + + // handle pure JavaScript files specially + if (/\.js$/.test(filename)) { + try { + dactyl.loadScript(uri.spec, Script(file)); + dactyl.helpInitialized = false; + } + catch (e) { + if (isString(e)) + e = { message: e }; + let err = new Error(); + for (let [k, v] in Iterator(e)) + err[k] = v; + err.echoerr = <>{file.path}:{e.lineNumber}: {e}; + throw err; + } + } + else if (/\.css$/.test(filename)) + storage.styles.registerSheet(uri.spec, false, true); + else { + let heredoc = ""; + let heredocEnd = null; // the string which ends the heredoc + let str = file.read(); + let lines = str.split(/\r\n|[\r\n]/); + + this.readHeredoc = function (end) { + let res = []; + try { + io.sourcing.line++; + while (true) + let ([i, line] = iter.next()) { + if (line === end) + return res.join("\n"); + res.push(line); + } + } + catch (e if e instanceof StopIteration) {} + dactyl.assert(false, "Unexpected end of file waiting for " + end); + }; + + let iter = Iterator(lines); + for (let [i, line] in iter) { + if (this.sourcing.finished) + break; + this.sourcing.line = i + 1; + // skip line comments and blank lines + line = line.replace(/\r$/, ""); + + if (!/^\s*(".*)?$/.test(line)) + try { + dactyl.execute(line, { setFrom: file }, true); + } + catch (e) { + dactyl.echoerr("Error detected while processing " + file.path); + dactyl.echomsg("line\t" + this.sourcing.line + ":"); + dactyl.reportError(e, true); + } + } + } + + if (this._scriptNames.indexOf(file.path) == -1) + this._scriptNames.push(file.path); + + dactyl.echomsg("finished sourcing " + filename.quote(), 2); + + dactyl.log("Sourced: " + filename, 3); } - - if (this._scriptNames.indexOf(file.path) == -1) - this._scriptNames.push(file.path); - - dactyl.echomsg("finished sourcing " + filename.quote(), 2); - - dactyl.log("Sourced: " + filename, 3); - } - catch (e) { - dactyl.reportError(e); - let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e); - if (!silent) - dactyl.echoerr(message); - } - finally { - defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms"); - this.sourcing = wasSourcing; - this.readHeredoc = readHeredoc; - } + catch (e) { + dactyl.reportError(e); + let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e); + if (!silent) + dactyl.echoerr(message); + } + finally { + defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms"); + } + }); }, // TODO: when https://bugzilla.mozilla.org/show_bug.cgi?id=68702 is diff --git a/common/content/mappings.js b/common/content/mappings.js index 84d3be1e..a3ec6461 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -384,9 +384,9 @@ const Mappings = Module("mappings", { } else if (args["-ex"]) { rhs = ["-ex", rhs]; - action = function (count) { - dactyl.execute(commands.replaceTokens(rhs[1], { count: count })); - }; + action = function action(count) + dactyl.execute(commands.replaceTokens(rhs[1], { count: count }), null, true, action.sourcing); + action.sourcing = io.sourcing && update({}, io.sourcing); } else { rhs = [events.canonicalKeys(rhs)]; diff --git a/common/modules/base.jsm b/common/modules/base.jsm index 1ca65827..859aeed8 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -764,6 +764,16 @@ Class.prototype = { */ init: function () {}, + withSavedValues: function (names, callback, self) { + let vals = names.map(function (name) this[name], this); + try { + return callback.call(self || this); + } + finally { + names.forEach(function (name, i) this[name] = vals[i], this); + } + }, + toString: function () "[instance " + this.constructor.className + "]", /**