diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/content/buffer.js | 2 | ||||
-rw-r--r-- | common/content/editor.js | 14 | ||||
-rw-r--r-- | common/locale/en-US/options.xml | 34 | ||||
-rw-r--r-- | common/modules/util.jsm | 67 |
4 files changed, 104 insertions, 13 deletions
diff --git a/common/content/buffer.js b/common/content/buffer.js index ad4ce71b..46fb9988 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -961,7 +961,7 @@ const Buffer = Module("buffer", { let doc = buffer.focusedFrame.document; if (isArray(url)) { - if (options.get("editor").has("l")) + if (options.get("editor").has("line")) this.viewSourceExternally(url[0] || doc, url[1]); else window.openDialog("chrome://global/content/viewSource.xul", diff --git a/common/content/editor.js b/common/content/editor.js index 61bcf06e..c79c89c5 100644 --- a/common/content/editor.js +++ b/common/content/editor.js @@ -242,7 +242,7 @@ const Editor = Module("editor", { }, editFileExternally: function (path, line, column) { - let args = options.get("editor").format({ f: path, l: line, c: column }); + let args = options.get("editor").format({ file: path, line: line, column: column }); dactyl.assert(args.length >= 1, "No editor specified"); @@ -772,19 +772,19 @@ const Editor = Module("editor", { options: function () { options.add(["editor"], "The external text editor", - "string", "gvim -f +%l %f", { + "string", "gvim -f +<line> <file>", { format: function (obj, value) { let args = commands.parseArgs(value || this.value, { argCount: "*", allowUnknownOptions: true }) - .map(util.compileFormat).filter(function (fmt) fmt.valid(obj)) + .map(util.compileMacro).filter(function (fmt) fmt.valid(obj)) .map(function (fmt) fmt(obj)); - if (obj["f"] && !this.has("f")) - args.push(obj["f"]); + if (obj["file"] && !this.has("file")) + args.push(obj["file"]); return args; }, - has: function (key) set.has(util.compileFormat(this.value).seen, key), + has: function (key) set.has(util.compileMacro(this.value).seen, key), validator: function (value) { this.format({}, value); - return Object.keys(util.compileFormat(value).seen).every(function (k) "cfl".indexOf(k) >= 0) + return Object.keys(util.compileMacro(value).seen).every(function (k) ["column", "file", "line"].indexOf(k) >= 0) } }); diff --git a/common/locale/en-US/options.xml b/common/locale/en-US/options.xml index d9d24776..e99d475f 100644 --- a/common/locale/en-US/options.xml +++ b/common/locale/en-US/options.xml @@ -88,6 +88,30 @@ <str>line: 32 'Lieder eines fahrenden Gesellen.txt'</str> </p> +<p tag="macro-string"> + Some options may be given format strings containing macro replacements in + the form of <em><<a>name</a>></em>. These tokens are replaced by + the parameter <a>name</a> as specified in the relevant documentation. + If the token is in the form <em><q-<a>name</a>></em>, the value of the + parameter is automatically <link topic="quoting">quoted</link>. +</p> +<p> + Any substring enclosed by <em><[</em> + and <em>]></em> is automatically elided if any of the contained macros + characters aren't currently valid. A literal <em><</em> or <em>></em> + character may be included with the special escape sequences <em><lt></em> + or <em><gt></em> respectively. +</p> + +<p> + For example, given the format string + <str><em><[</em>(cmd: <em><column></em>) <em>]><[</em>line: <em><line> ]><file></em></str>, + where <em>line</em>=<hl key="Number">32</hl> and + <em>file</em>=<str delim="'">Lieder eines fahrenden Gesellen.txt</str>, + the result is formatted as + <str>line: 32 'Lieder eines fahrenden Gesellen.txt'</str> +</p> + <h2 tag="set-option E764">Setting options</h2> <item> @@ -553,7 +577,7 @@ <tags>'editor'</tags> <spec>'editor'</spec> <type>string</type> - <default>gvim -f</default> + <default><![CDATA[gvim -f +<line> <file>]]></default> <description> <p> Set the external text editor. @@ -562,16 +586,16 @@ </p> <p> - Accepts a <t>format-string</t> with the following escapes available. + Accepts a <t>macro-string</t> with the following escapes available. Arguments containing escapes which are not relevant to a given call are automatically elided. All field splitting is done before format characters are processed. </p> <dl> - <dt>%f</dt> <dd>The file to edit. Appended as the final argument if missing.</dd> - <dt>%l</dt> <dd>The line number at which to position the cursor.</dd> - <dt>%c</dt> <dd>The column at which to position the cursor.</dd> + <dt><file></dt> <dd>The file to edit. Appended as the final argument if missing.</dd> + <dt><line></dt> <dd>The line number at which to position the cursor.</dd> + <dt><column></dt> <dd>The column at which to position the cursor.</dd> </dl> <warning> diff --git a/common/modules/util.jsm b/common/modules/util.jsm index 293ffb37..0e8c7734 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -244,6 +244,73 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) return stack.top; }, + compileMacro: function compileFormat(macro) { + let stack = [frame()]; + stack.__defineGetter__("top", function () this[this.length - 1]); + + function frame() update( + function _frame(obj) + _frame === stack.top || _frame.valid(obj) ? + _frame.elements.map(function (e) callable(e) ? e(obj) : e).join("") : "", + { + elements: [], + seen: {}, + valid: function (obj) this.elements.every(function (e) !e.test || e.test(obj)) + }); + + let defaults = { lt: "<", gt: ">" }; + + let match, end = 0; + let re = util.regexp(<![CDATA[ + (.*?) // 1 + (?: + (<\[) | // 2 + < (.*?) > | // 3 + (\]>) // 4 + ) + ]]>, "gy"); + while (match = re.exec(macro)) { + let [, prefix, open, macro, close] = match; + end += match[0].length; + + if (prefix) + stack.top.elements.push(prefix); + if (open) { + let f = frame(); + stack.top.elements.push(f); + stack.push(f); + } + else if (close) { + stack.pop(); + util.assert(stack.length, "Unmatched %] in macro"); + } + else { + let [, flags, name] = /^((?:[a-z]-)*)(.*)/.exec(macro); + flags = set(flags); + + let quote = util.identity; + if (flags.q) + quote = function quote(obj) typeof obj === "number" ? obj : Commands.quote(obj); + + if (set.has(defaults, name)) + stack.top.elements.push(quote(defaults[name])); + else { + stack.top.elements.push(update( + function (obj) obj[name] != null ? quote(obj[name]) : "", + { test: function (obj) obj[name] != null })); + + for (let elem in array.iterValues(stack)) + elem.seen[name] = true; + } + } + } + if (end < macro.length) + stack.top.elements.push(macro.substr(end)); + + util.assert(stack.length === 1, "Unmatched <[ in macro"); + return stack.top; + }, + /** * Returns an object representing a Node's computed CSS style. * |