diff --git a/common/content/autocommands.js b/common/content/autocommands.js index d225c713..8aad6061 100644 --- a/common/content/autocommands.js +++ b/common/content/autocommands.js @@ -169,7 +169,7 @@ const AutoCommands = Module("autocommands", { cmd.toString = function toString() "-javascript " + cmd.source; } else { - cmd = function cmd(args) dactyl.execute(commands.replaceTokens(cmd.source, args), null, true, cmd.sourcing); + cmd = function cmd(args) commands.execute(cmd.source, args, false, null, cmd.sourcing); cmd.sourcing = io.sourcing && update({}, io.sourcing); cmd.toString = function toString() cmd.source; } diff --git a/common/content/commands.js b/common/content/commands.js index f1bcd8e4..bd37d271 100644 --- a/common/content/commands.js +++ b/common/content/commands.js @@ -412,6 +412,64 @@ const Commands = Module("commands", { return res.join(" "); }, + /** + * Executes an Ex command script. + * + * @param {string} string A string containing the commands to execute. + * @param {object} tokens An optional object containing tokens to be + * interpolated into the command string. + * @param {object} args Optional arguments object to be passed to + * command actions. + * @param {object} sourcing An object containing information about + * the file that is being or has been sourced to obtain the + * command string. + */ + execute: function (string, tokens, silent, args, sourcing) { + io.withSavedValues(["readHeredoc", "sourcing"], function () { + this.sourcing = update({}, sourcing); + + args = update({ setFrom: this.file }, args || {}); + + if (tokens) + string = commands.replaceTokens(string, tokens); + + let lines = string.split(/\r\n|[\r\n]/); + + this.readHeredoc = function (end) { + let res = []; + this.sourcing.line++; + while (++i < lines.length) { + if (lines[i] === end) + return res.join("\n"); + res.push(lines[i]); + } + dactyl.assert(false, "Unexpected end of file waiting for " + end); + }; + + for (var i = 0; i < lines.length && !this.sourcing.finished; i++) { + // Deal with editors from Silly OSs. + let line = lines[i].replace(/\r$/, ""); + + this.sourcing.line = sourcing.line + i; + + // Process escaped new lines + while (i < lines.length && /^\s*\\/.test(lines[i + 1])) + line += "\n" + lines[++i].replace(/^\s*\\/, ""); + + try { + dactyl.execute(line, args); + } + catch (e) { + if (!silent) { + dactyl.echoerr("Error detected while processing " + this.sourcing.file); + dactyl.echomsg("line\t" + this.sourcing.line + ":"); + dactyl.reportError(e, true); + } + } + } + }); + }, + /** * Returns the command with matching name. * @@ -595,9 +653,11 @@ const Commands = Module("commands", { outer: while (i < str.length || complete) { - // skip whitespace - while (/\s/.test(str[i]) && i < str.length) - i++; + var argStart = i; + let re = /^\s*/gy; + re.lastIndex = i; + i += re.exec(str)[0].length; + if (str[i] == "|") { args.string = str.slice(0, i); args.trailing = str.slice(i + 1); @@ -700,14 +760,20 @@ const Commands = Module("commands", { complete.highlight(i, sub.length, "SPELLCHECK"); } - if (args.length == literal) { + if (args.length === literal) { if (complete) args.completeArg = args.length; + + let re = /^(?:\s*(?=\n)|\s*)([^]*)/gy; + re.lastIndex = argStart || 0; + sub = re.exec(str)[1]; + // Hack. if (sub.substr(0, 2) === "<<" && hereDoc) let ([count, arg] = getNextArg(sub)) { sub = arg + sub.substr(count); } + args.literalArg = sub; args.push(sub); args.quote = null; @@ -997,7 +1063,7 @@ const Commands = Module("commands", { count: this.count && args.count }; - dactyl.execute(commands.replaceTokens(this.replacementText, tokens), null, true, this.sourcing); + commands.execute(this.replacementText, tokens, false, null, this.sourcing); } // TODO: offer completion.ex? diff --git a/common/content/configbase.js b/common/content/configbase.js index d316ac5b..c4f9f9ee 100644 --- a/common/content/configbase.js +++ b/common/content/configbase.js @@ -153,7 +153,7 @@ const ConfigBase = Class(ModuleBase, { Null color: blue; Number color: blue; Object color: maroon; - String color: green; + String color: green; white-space: pre; Key font-weight: bold; diff --git a/common/content/dactyl.js b/common/content/dactyl.js index d0013a2c..c9dec706 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -1994,7 +1994,7 @@ const Dactyl = Module("dactyl", { dactyl.execute(init); else { if (rcFile) { - io.source(rcFile.path, true); + io.source(rcFile.path, false); services.get("environment").set("MY_" + config.idName + "RC", rcFile.path); } else diff --git a/common/content/io.js b/common/content/io.js index 5a274dc6..763f0e05 100644 --- a/common/content/io.js +++ b/common/content/io.js @@ -319,99 +319,61 @@ lookup: source: function (filename, silent) { defineModule.loadLog.push("sourcing " + filename); let time = Date.now(); - this.withSavedValues(["readHeredoc", "sourcing"], function () { - try { - var file = io.File(filename); - this.sourcing = { - file: file.path, - line: 0 - }; + try { + var file = io.File(filename); - 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 (e.fileName) - try { - e.fileName = e.fileName.replace(/^(chrome|resource):.*? -> /, ""); - if (e.fileName == uri.spec) - e.fileName = filename; - e.echoerr = <>{e.fileName}:{e.lineNumber}: {e} - } - catch (e) {} - throw e; - } - } - else if (/\.css$/.test(filename)) - storage.styles.registerSheet(uri.spec, false, true); - else { - let lines = file.read().split(/\r\n|[\r\n]/); - - this.readHeredoc = function (end) { - let res = []; - io.sourcing.line++; - while (++i < lines.length) { - if (lines[i] === end) - return res.join("\n"); - res.push(lines[i]); - } - dactyl.assert(false, "Unexpected end of file waiting for " + end); - }; - - for (var i = 0; i < lines.length && !this.sourcing.finished; i++) { - // Deal with editors from Silly OSs. - let line = lines[i].replace(/\r$/, ""); - - this.sourcing.line = i + 1; - - // Process escaped new lines - while (i < lines.length && /^\s*\\/.test(lines[i + 1])) - line += "\n" + lines[++i].replace(/^\s*\\/, ""); - - try { - dactyl.execute(line, { setFrom: file }); - } - catch (e) { - if (!silent) { - 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); - } - catch (e) { - if (!(e instanceof FailedAssertion)) - dactyl.reportError(e); - let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e); + if (!file.exists() || !file.isReadable() || file.isDirectory()) { if (!silent) - dactyl.echoerr(message); + dactyl.echoerr("E484: Can't open file " + filename.quote()); + return; } - finally { - defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms"); + + 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 (e.fileName) + try { + e.fileName = e.fileName.replace(/^(chrome|resource):.*? -> /, ""); + if (e.fileName == uri.spec) + e.fileName = filename; + e.echoerr = <>{e.fileName}:{e.lineNumber}: {e} + } + catch (e) {} + throw e; + } } - }); + else if (/\.css$/.test(filename)) + storage.styles.registerSheet(uri.spec, false, true); + else { + commands.execute(file.read(), null, silent, null, + { file: file.path, line: 1 }); + } + + 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) { + if (!(e instanceof FailedAssertion)) + 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 a3ec6461..da98c0c6 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -384,8 +384,8 @@ const Mappings = Module("mappings", { } else if (args["-ex"]) { rhs = ["-ex", rhs]; - action = function action(count) - dactyl.execute(commands.replaceTokens(rhs[1], { count: count }), null, true, action.sourcing); + action = function action(count) commands.execute(rhs[1], { count: count }, + false, null, action.sourcing); action.sourcing = io.sourcing && update({}, io.sourcing); } else {