+ Some options may be given format strings containing macro replacements in
+ the form of <name>. These tokens are replaced by
+ the parameter name as specified in the relevant documentation.
+ If the token is in the form <q-name>, the value of the
+ parameter is automatically quoted.
+
+
+ Any substring enclosed by <[
+ and ]> is automatically elided if any of the contained macros
+ characters aren't currently valid. A literal < or >
+ character may be included with the special escape sequences <lt>
+ or <gt> respectively.
+
+
+
+ For example, given the format string
+ <[(cmd: <column>) ]><[line: <line> ]><file>,
+ where line=32 and
+ file=Lieder eines fahrenden Gesellen.txt,
+ the result is formatted as
+ line: 32 'Lieder eines fahrenden Gesellen.txt'
+
Set the external text editor.
@@ -562,16 +586,16 @@
- Accepts a format-string with the following escapes available.
+ Accepts a macro-string 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.
-
%f
The file to edit. Appended as the final argument if missing.
-
%l
The line number at which to position the cursor.
-
%c
The column at which to position the cursor.
+
<file>
The file to edit. Appended as the final argument if missing.
+
<line>
The line number at which to position the cursor.
+
<column>
The column at which to position the cursor.
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( | // 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.
*