diff --git a/common/content/contexts.js b/common/content/contexts.js
index 714b45c4..caa5b8c5 100644
--- a/common/content/contexts.js
+++ b/common/content/contexts.js
@@ -148,6 +148,23 @@ var Contexts = Module("contexts", {
context: null,
+ /**
+ * Returns a frame object describing the currently executing
+ * command, if applicable, otherwise returns the passed frame.
+ *
+ * @param {nsIStackFrame} frame
+ */
+ getCaller: function getCaller(frame) {
+ if (this.context && this.context.file)
+ return {
+ __proto__: frame,
+ filename: this.context.file[0] == "[" ? this.context.file
+ : services.io.newFileURI(File(this.context.file)).spec,
+ lineNumber: this.context.line
+ };
+ return frame;
+ },
+
groups: Class.memoize(function () Object.create(Group.groupsProto, {
groups: { value: this.activeGroups().filter(function (g) g.filter(buffer.uri)) }
})),
@@ -216,8 +233,8 @@ var Contexts = Module("contexts", {
getGroup: function getGroup(name, hive) {
if (name === "default")
var group = this.context && this.context.context && this.context.context.GROUP;
- else
- group = set.has(this.groupMap, name) && this.groupMap[name];
+ else if (set.has(this.groupMap, name))
+ group = this.groupMap[name];
if (group && hive)
return group[hive];
diff --git a/common/content/mappings.js b/common/content/mappings.js
index 9512e087..6f1c81af 100644
--- a/common/content/mappings.js
+++ b/common/content/mappings.js
@@ -170,7 +170,7 @@ var MapHive = Class("MapHive", Group.Hive, {
extra = extra || {};
let map = Map(modes, keys, description, action, extra);
- map.definedAt = Commands.getCaller(Components.stack.caller);
+ map.definedAt = contexts.getCaller(Components.stack.caller);
map.hive = this;
if (this.name !== "builtin")
@@ -341,7 +341,7 @@ var Mappings = Module("mappings", {
*/
add: function () {
let map = this.builtin.add.apply(this.builtin, arguments);
- map.definedAt = Commands.getCaller(Components.stack.caller);
+ map.definedAt = contexts.getCaller(Components.stack.caller);
return map;
},
@@ -358,7 +358,7 @@ var Mappings = Module("mappings", {
*/
addUserMap: function () {
let map = this.user.add.apply(this.user, arguments);
- map.definedAt = Commands.getCaller(Components.stack.caller);
+ map.definedAt = contexts.getCaller(Components.stack.caller);
return map;
},
@@ -454,6 +454,9 @@ var Mappings = Module("mappings", {
if (!rhs) // list the mapping
mappings.list(mapmodes, mappings.expandLeader(lhs), hives);
else {
+ util.assert(args["-group"] !== mappings.builtin,
+ "Cannot change mappings in the builtin group");
+
args["-group"].add(mapmodes, [lhs],
args["-description"],
contexts.bindMacro(args, "-keys", function (params) params),
@@ -572,6 +575,9 @@ var Mappings = Module("mappings", {
commands.add([ch + "mapc[lear]"],
"Remove all mappings" + modeDescription,
function (args) {
+ util.assert(args["-group"] !== mappings.builtin,
+ "Cannot change mappings in the builtin group");
+
let mapmodes = array.uniq(args["-modes"].map(findMode));
mapmodes.forEach(function (mode) {
args["-group"].clear(mode);
@@ -593,6 +599,9 @@ var Mappings = Module("mappings", {
commands.add([ch + "unm[ap]"],
"Remove a mapping" + modeDescription,
function (args) {
+ util.assert(args["-group"] !== mappings.builtin,
+ "Cannot change mappings in the builtin group");
+
let mapmodes = array.uniq(args["-modes"].map(findMode));
let found = false;
diff --git a/common/content/commands.js b/common/modules/commands.jsm
similarity index 78%
rename from common/content/commands.js
rename to common/modules/commands.jsm
index c9118a6a..c426c106 100644
--- a/common/content/commands.js
+++ b/common/modules/commands.jsm
@@ -6,6 +6,15 @@
// given in the LICENSE.txt file included with this file.
"use strict";
+try {
+
+Components.utils.import("resource://dactyl/bootstrap.jsm");
+defineModule("commands", {
+ exports: ["ArgType", "Command", "Commands", "CommandOption", "Ex", "commands"],
+ use: ["config", "options", "template", "util"]
+}, this);
+
+
/** @scope modules */
/**
@@ -141,6 +150,8 @@ var Command = Class("Command", {
* @param {Object} modifiers Any modifiers to be passed to {@link #action}.
*/
execute: function (args, modifiers) {
+ const { dactyl } = this.modules;
+
let context = args.context;
if (this.deprecated && !set.add(this.complained, context ? context.file : "[Command Line]")) {
let loc = contexts.context ? context.file + ":" + context.line + ": " : "";
@@ -190,7 +201,7 @@ var Command = Class("Command", {
* @returns {Args}
* @see Commands#parseArgs
*/
- parseArgs: function (args, complete, extra) commands.parseArgs(args, {
+ parseArgs: function parseArgs(args, complete, extra) this.modules.commands.parseArgs(args, {
__proto__: this,
complete: complete,
extra: extra
@@ -265,8 +276,9 @@ var Command = Class("Command", {
optionMap: Class.memoize(function () array(this.options)
.map(function (opt) opt.names.map(function (name) [name, opt]))
.flatten().toObject()),
- newArgs: function () {
+ newArgs: function (base) {
let res = [];
+ update(res, base);
res.__proto__ = this.argsPrototype;
return res;
},
@@ -279,8 +291,6 @@ var Command = Class("Command", {
command: this,
- get context() contexts.context,
-
explicitOpts: Class.memoize(function () ({})),
has: function (opt) set.has(this.explicitOpts, opt) || typeof opt === "number" && set.has(this, opt),
@@ -290,13 +300,13 @@ var Command = Class("Command", {
// TODO: string: Class.memoize(function () { ... }),
verify: function verify() {
if (this.command.argCount) {
- dactyl.assert((this.length > 0 || !/^[1+]$/.test(this.command.argCount)) &&
- (this.literal == null || !/[1+]/.test(this.command.argCount) || /\S/.test(this.literalArg || "")),
- "E471: Argument required");
+ util.assert((this.length > 0 || !/^[1+]$/.test(this.command.argCount)) &&
+ (this.literal == null || !/[1+]/.test(this.command.argCount) || /\S/.test(this.literalArg || "")),
+ "E471: Argument required");
- dactyl.assert((this.length == 0 || this.command.argCount !== "0") &&
- (this.length <= 1 || !/^[01?]$/.test(this.command.argCount)),
- "E488: Trailing characters");
+ util.assert((this.length == 0 || this.command.argCount !== "0") &&
+ (this.length <= 1 || !/^[01?]$/.test(this.command.argCount)),
+ "E488: Trailing characters");
}
}
})),
@@ -352,11 +362,16 @@ var Command = Class("Command", {
});
// Prototype.
-var ex = {
+var Ex = Module("Ex", {
+ Local: function Local(dactyl, modules, window) ({
+ get commands() modules.commands,
+ get context() modules.contexts.context
+ }),
+
_args: function (cmd, args) {
args = Array.slice(args);
- let res = cmd.newArgs();
+ let res = cmd.newArgs({ context: this.context });
if (isObject(args[0]))
for (let [k, v] in Iterator(args.shift()))
if (k == "!")
@@ -366,8 +381,8 @@ var ex = {
else {
let opt = cmd.optionMap["-" + k];
let val = opt.type && opt.type.parse(v);
- dactyl.assert(val != null && (typeof val !== "number" || !isNaN(val)),
- "No such option: " + k);
+ util.assert(val != null && (typeof val !== "number" || !isNaN(val)),
+ "No such option: " + k);
Class.replaceProperty(args, opt.names[0], val);
args.explicitOpts[opt.names[0]] = val;
}
@@ -376,137 +391,264 @@ var ex = {
return res;
},
- _complete: function (cmd)
+ _complete: function (cmd) let (self = this)
function _complete(context, func, obj, args) {
- args = ex._args(cmd, args);
+ args = self._args(cmd, args);
args.completeArg = args.length - 1;
if (cmd.completer && args.length)
return cmd.completer(context, args);
},
_run: function (name) {
- let cmd = commands.get(name);
- dactyl.assert(cmd, "No such command");
+ const self = this;
+ let cmd = this.commands.get(name);
+ util.assert(cmd, "No such command");
return update(function exCommand(options) {
- let args = ex._args(cmd, arguments);
+ let args = self._args(cmd, arguments);
args.verify();
return cmd.execute(args);
}, {
- dactylCompleter: ex._complete(cmd)
+ dactylCompleter: self._complete(cmd)
});
},
__noSuchMethod__: function (meth, args) this._run(meth).apply(this, args)
-};
-
-var CommandHive = Class("CommandHive", Group.Hive, {
- init: function init(group) {
- init.supercall(this, group);
- this._map = {};
- this._list = [];
- },
-
- /** @property {Iterator(Command)} @private */
- __iterator__: function () array.iterValues(this._list.sort(function (a, b) a.name > b.name)),
-
- /** @property {string} The last executed Ex command line. */
- repeat: null,
-
- /**
- * Adds a new command.
- *
- * @param {string[]} names The names by which this command can be
- * invoked. The first name specified is the command's canonical
- * name.
- * @param {string} description A description of the command.
- * @param {function} action The action invoked by this command.
- * @param {Object} extra An optional extra configuration hash.
- * @optional
- */
- add: function (names, description, action, extra, replace) {
- extra = extra || {};
- if (!extra.definedAt)
- extra.definedAt = Commands.getCaller(Components.stack.caller);
-
- extra.hive = this;
- extra.parsedSpecs = Command.parseSpecs(names);
-
- let names = array.flatten(extra.parsedSpecs);
- let name = names[0];
-
- dactyl.assert(!names.some(function (name) name in commands.builtin._map),
- "E182: Can't replace non-user command: " + name);
-
- dactyl.assert(replace || names.every(function (name) !(name in this._map), this),
- "Not replacing command " + name);
-
- for (let name in values(names)) {
- ex.__defineGetter__(name, function () this._run(name));
- if (name in this._map)
- this.remove(name);
- }
-
- let self = this;
- let closure = function () self._map[name];
-
- memoize(this._map, name, function () Command(names, description, action, extra));
- memoize(this._list, this._list.length, closure);
- for (let alias in values(names.slice(1)))
- memoize(this._map, alias, closure);
-
- return name;
- },
- _add: function (names, description, action, extra, replace) {
- extra = extra || {};
- extra.definedAt = Commands.getCaller(Components.stack.caller.caller);
- return this.add.apply(this, arguments);
- },
-
- /**
- * Returns the command with matching *name*.
- *
- * @param {string} name The name of the command to return. This can be
- * any of the command's names.
- * @param {boolean} full If true, only return a command if one of
- * its names matches *name* exactly.
- * @returns {Command}
- */
- get: function (name, full) this._map[name]
- || !full && array.nth(this._list, function (cmd) cmd.hasName(name), 0)
- || null,
-
- /**
- * Remove the user-defined command with matching *name*.
- *
- * @param {string} name The name of the command to remove. This can be
- * any of the command's names.
- */
- remove: function (name) {
- dactyl.assert(this.group !== contexts.default,
- "Cannot delete non-user commands");
-
- let cmd = this.get(name);
- this._list = this._list.filter(function (c) c !== cmd);
- for (let name in values(cmd.names))
- delete this._map[name];
- }
});
/**
* @instance commands
*/
var Commands = Module("commands", {
- init: function () {
- this.user = contexts.hives.commands.user;
- this.builtin = contexts.hives.commands.builtin;
- },
+ lazyInit: true,
- hives: Group.Hives("commands", CommandHive),
+ Local: function Local(dactyl, modules, window) let ({ Group, contexts } = modules) ({
+ init: function () {
+ this.Command = Class("Command", Command, { modules: modules });
+ this.user = contexts.hives.commands.user;
+ this.builtin = contexts.hives.commands.builtin;
+ },
- get allHives() contexts.allGroups.commands,
+ get context() contexts.context,
- get userHives() this.allHives.filter(function (h) h !== this.builtin, this),
+ get readHeredoc() modules.io.readHeredoc,
+
+ hives: Group.Hives("commands", Class("CommandHive", Group.Hive, {
+ init: function init(group) {
+ init.supercall(this, group);
+ this._map = {};
+ this._list = [];
+ },
+
+ /** @property {Iterator(Command)} @private */
+ __iterator__: function () array.iterValues(this._list.sort(function (a, b) a.name > b.name)),
+
+ /** @property {string} The last executed Ex command line. */
+ repeat: null,
+
+ /**
+ * Adds a new command.
+ *
+ * @param {string[]} names The names by which this command can be
+ * invoked. The first name specified is the command's canonical
+ * name.
+ * @param {string} description A description of the command.
+ * @param {function} action The action invoked by this command.
+ * @param {Object} extra An optional extra configuration hash.
+ * @optional
+ */
+ add: function (names, description, action, extra, replace) {
+ const { commands } = modules;
+
+ extra = extra || {};
+ if (!extra.definedAt)
+ extra.definedAt = contexts.getCaller(Components.stack.caller);
+
+ extra.hive = this;
+ extra.parsedSpecs = Command.parseSpecs(names);
+
+ let names = array.flatten(extra.parsedSpecs);
+ let name = names[0];
+
+ util.assert(!names.some(function (name) name in commands.builtin._map),
+ "E182: Can't replace non-user command: " + name);
+
+ util.assert(replace || names.every(function (name) !(name in this._map), this),
+ "Not replacing command " + name);
+
+ for (let name in values(names)) {
+ ex.__defineGetter__(name, function () this._run(name));
+ if (name in this._map)
+ this.remove(name);
+ }
+
+ let self = this;
+ let closure = function () self._map[name];
+
+ memoize(this._map, name, function () commands.Command(names, description, action, extra));
+ memoize(this._list, this._list.length, closure);
+ for (let alias in values(names.slice(1)))
+ memoize(this._map, alias, closure);
+
+ return name;
+ },
+
+ _add: function (names, description, action, extra, replace) {
+ extra = extra || {};
+ extra.definedAt = contexts.getCaller(Components.stack.caller.caller);
+ return this.add.apply(this, arguments);
+ },
+
+ /**
+ * Clear all commands.
+ * @returns {Command}
+ */
+ clear: function clear() {
+ util.assert(this.group !== contexts.default,
+ "Cannot delete non-user commands");
+ this._map = {};
+ this._list = [];
+ },
+
+ /**
+ * Returns the command with matching *name*.
+ *
+ * @param {string} name The name of the command to return. This can be
+ * any of the command's names.
+ * @param {boolean} full If true, only return a command if one of
+ * its names matches *name* exactly.
+ * @returns {Command}
+ */
+ get: function get(name, full) this._map[name]
+ || !full && array.nth(this._list, function (cmd) cmd.hasName(name), 0)
+ || null,
+
+ /**
+ * Remove the user-defined command with matching *name*.
+ *
+ * @param {string} name The name of the command to remove. This can be
+ * any of the command's names.
+ */
+ remove: function remove(name) {
+ util.assert(this.group !== contexts.default,
+ "Cannot delete non-user commands");
+
+ let cmd = this.get(name);
+ this._list = this._list.filter(function (c) c !== cmd);
+ for (let name in values(cmd.names))
+ delete this._map[name];
+ }
+ })),
+
+ get allHives() contexts.allGroups.commands,
+
+ get userHives() this.allHives.filter(function (h) h !== this.builtin, this),
+
+ /**
+ * 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} context 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, context) {
+ contexts.withContext(context || this.context || { file: "[Command Line]", line: 1 },
+ function (context) {
+ modules.io.withSavedValues(["readHeredoc"], function () {
+ this.readHeredoc = function (end) {
+ let res = [];
+ contexts.context.line++;
+ while (++i < lines.length) {
+ if (lines[i] === end)
+ return res.join("\n");
+ res.push(lines[i]);
+ }
+ util.assert(false, "Unexpected end of file waiting for " + end);
+ };
+
+ args = update({}, args || {});
+
+ if (tokens && !callable(string))
+ string = util.compileMacro(string, true);
+ if (callable(string))
+ string = string(tokens || {});
+
+ let lines = string.split(/\r\n|[\r\n]/);
+ let startLine = context.line;
+
+ for (var i = 0; i < lines.length && !context.finished; i++) {
+ // Deal with editors from Silly OSs.
+ let line = lines[i].replace(/\r$/, "");
+
+ context.line = startLine + 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) {
+ e.message = context.file + ":" + context.line + ": " + e.message;
+ dactyl.reportError(e, true);
+ }
+ }
+ }
+ });
+ });
+ },
+
+ /**
+ * Displays a list of user-defined commands.
+ */
+ list: function list() {
+ function completerToString(completer) {
+ if (completer)
+ return [k for ([k, v] in Iterator(config.completers)) if (completer == completion.closure[v])][0] || "custom";
+ return "";
+ }
+
+ if (!this.userHives.some(function (h) h._list.length))
+ dactyl.echomsg("No user-defined commands found");
+ else
+ modules.commandline.commandOutput(
+
+
+ |
+ |
+ Name |
+ Args |
+ Range |
+ Complete |
+ Definition |
+
+
+ {
+ template.map(this.userHives, function (hive) let (i = 0)
+
+
+ template.map(hive, function (cmd)
+ template.map(cmd.names, function (name)
+
+ | {!i++ ? hive.name : ""} |
+ {cmd.bang ? "!" : " "} |
+ {cmd.name} |
+ {cmd.argCount} |
+ {cmd.count ? "0c" : ""} |
+ {completerToString(cmd.completer)} |
+ {cmd.replacementText || "function () { ... }"} |
+
)) +
+
)
+ }
+
);
+ }
+ }),
/**
* @property Indicates that no count was specified for this
@@ -532,7 +674,7 @@ var Commands = Module("commands", {
add: function () this.builtin._add.apply(this.builtin, arguments),
addUserCommand: deprecated("commands.user.add", { get: function addUserCommand() this.user.closure._add }),
- getUserCommands: deprecated("iter(commands.user)", function getUserCommands() iter(commands.user).toArray()),
+ getUserCommands: deprecated("iter(commands.user)", function getUserCommands() iter(this.user).toArray()),
removeUserCommand: deprecated("commands.user.remove", { get: function removeUserCommand() this.user.closure.remove }),
/**
@@ -578,111 +720,6 @@ var Commands = Module("commands", {
get: function (name, full) iter(this.hives).map(function ([i, hive]) hive.get(name, full))
.nth(util.identity, 0),
- /**
- * Displays a list of user-defined commands.
- */
- list: function list() {
- function completerToString(completer) {
- if (completer)
- return [k for ([k, v] in Iterator(config.completers)) if (completer == completion.closure[v])][0] || "custom";
- return "";
- }
-
- if (!commands.userHives.some(function (h) h._list.length))
- dactyl.echomsg("No user-defined commands found");
- else
- commandline.commandOutput(
-
-
- |
- |
- Name |
- Args |
- Range |
- Complete |
- Definition |
-
-
- {
- template.map(commands.userHives, function (hive) let (i = 0)
-
+
- template.map(hive, function (cmd)
- template.map(cmd.names, function (name)
-
- | {!i++ ? hive.name : ""} |
- {cmd.bang ? "!" : " "} |
- {cmd.name} |
- {cmd.argCount} |
- {cmd.count ? "0c" : ""} |
- {completerToString(cmd.completer)} |
- {cmd.replacementText || "function () { ... }"} |
-
)) +
-
)
- }
-
);
- },
-
- /**
- * 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} context 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, context) {
- contexts.withContext(context || this.context || { file: "[Command Line]", line: 1 },
- function (context) {
- io.withSavedValues(["readHeredoc"], function () {
- this.readHeredoc = function (end) {
- let res = [];
- contexts.context.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);
- };
-
- args = update({}, args || {});
-
- if (tokens && !callable(string))
- string = util.compileMacro(string, true);
- if (callable(string))
- string = string(tokens || {});
-
- let lines = string.split(/\r\n|[\r\n]/);
- let startLine = context.line;
-
- for (var i = 0; i < lines.length && !context.finished; i++) {
- // Deal with editors from Silly OSs.
- let line = lines[i].replace(/\r$/, "");
-
- context.line = startLine + 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) {
- e.message = context.file + ":" + context.line + ": " + e.message;
- dactyl.reportError(e, true);
- }
- }
- }
- });
- });
- },
-
/**
* Returns true if a command invocation contains a URL referring to the
* domain *host*.
@@ -698,7 +735,7 @@ var Commands = Module("commands", {
return true;
}
catch (e) {
- dactyl.reportError(e);
+ util.reportError(e);
}
return false;
},
@@ -764,7 +801,9 @@ var Commands = Module("commands", {
* Args object.
* @returns {Args}
*/
- parseArgs: function (str, params) {
+ parseArgs: function parseArgs(str, params) {
+ const self = this;
+
function getNextArg(str, _keepQuotes) {
if (arguments.length < 2)
_keepQuotes = keepQuotes;
@@ -774,7 +813,7 @@ var Commands = Module("commands", {
let count = arg.length + 2;
if (complete)
return [count, "", ""];
- return [count, io.readHeredoc(arg), ""];
+ return [count, self.readHeredoc(arg), ""];
}
let [count, arg, quote] = Commands.parseArg(str, null, _keepQuotes);
@@ -795,7 +834,7 @@ var Commands = Module("commands", {
if (!argCount)
argCount = "*";
- var args = (params.newArgs || Array).call(params); // parsed options
+ var args = params.newArgs ? params.newArgs() : [];
args.string = str; // for access to the unparsed string
// FIXME!
@@ -832,7 +871,7 @@ var Commands = Module("commands", {
if (complete)
complete.message = error;
else
- dactyl.assert(false, error);
+ util.assert(false, error);
};
outer:
@@ -873,7 +912,7 @@ var Commands = Module("commands", {
if (sep == "=" || /\s/.test(sep) && opt.type != CommandOption.NOARG) {
[count, quoted, quote, error] = getNextArg(sub.substr(optname.length + 1), true);
arg = Option.dequote(quoted);
- dactyl.assert(!error, error);
+ util.assert(!error, error);
// if we add the argument to an option after a space, it MUST not be empty
if (sep != "=" && !quote && arg.length == 0)
@@ -974,7 +1013,7 @@ var Commands = Module("commands", {
// if not an option, treat this token as an argument
let [count, arg, quote, error] = getNextArg(sub);
- dactyl.assert(!error, error);
+ util.assert(!error, error);
if (complete) {
args.quote = Commands.complQuote[quote] || Commands.complQuote[""];
@@ -1127,7 +1166,7 @@ var Commands = Module("commands", {
parseCommands: function (str, complete) {
do {
let [count, cmd, bang, args, len] = commands.parseCommand(str);
- let command = commands.get(cmd || "");
+ let command = this.get(cmd || "");
if (command == null) {
yield [null, { commandString: str }];
@@ -1142,7 +1181,8 @@ var Commands = Module("commands", {
if (!complete || /(\w|^)[!\s]/.test(str))
args = command.parseArgs(args, context, { count: count, bang: bang });
else
- args = commands.parseArgs(args, { extra: { count: count, bang: bang } });
+ args = this.parseArgs(args, { extra: { count: count, bang: bang } });
+ args.context = this.context;
args.commandName = cmd;
args.commandString = str.substr(0, len) + args.string;
str = args.trailing;
@@ -1175,23 +1215,6 @@ var Commands = Module("commands", {
get quoteArg() Commands.quoteArg // XXX: better somewhere else?
}, {
- /**
- * Returns a frame object describing the currently executing
- * command, if applicable, otherwise returns the passed frame.
- *
- * @param {nsIStackFrame} frame
- */
- getCaller: function (frame) {
- if (contexts.context)
- return {
- __proto__: frame,
- filename: contexts.context.file[0] == "[" ? contexts.context.file :
- services.io.newFileURI(File(contexts.context.file)).spec,
- lineNumber: contexts.context.line
- };
- return frame;
- },
-
// returns [count, parsed_argument]
parseArg: function parseArg(str, sep, keepQuotes) {
let arg = "";
@@ -1233,15 +1256,19 @@ var Commands = Module("commands", {
/[\s"'\\]|^$|^-/.test(str) ? "'"
: ""](str)
}, {
- completion: function () {
+ completion: function initCompletion(dactyl, modules, window) {
+ const { completion } = modules;
+
completion.command = function command(context) {
context.title = ["Command"];
context.keys = { text: "longNames", description: "description" };
- context.generate = function () commands.hives.map(function (h) h._list).flatten();
+ context.generate = function () modules.commands.hives.map(function (h) h._list).flatten();
};
// provides completions for ex commands, including their arguments
completion.ex = function ex(context) {
+ const { commands } = modules;
+
// if there is no space between the command name and the cursor
// then get completions of the command name
for (var [command, args] in commands.parseCommands(context.filter, context))
@@ -1280,18 +1307,20 @@ var Commands = Module("commands", {
}
}
catch (e) {
- dactyl.reportError(e);
+ util.reportError(e);
}
};
- completion.userCommand = function userCommand(context) {
+ completion.userCommand = function userCommand(context, group) {
context.title = ["User Command", "Definition"];
context.keys = { text: "name", description: "replacementText" };
- context.completions = commands.getUserCommands();
+ context.completions = group || modules.commands.user;
};
},
- commands: function () {
+ commands: function (dactyl, modules, window) {
+ const { commands, contexts } = modules;
+
// TODO: Vim allows commands to be defined without {rep} if there are {attr}s
// specified - useful?
commands.add(["com[mand]"],
@@ -1299,12 +1328,15 @@ var Commands = Module("commands", {
function (args) {
let cmd = args[0];
- dactyl.assert(!cmd || cmd.split(",").every(commands.validName.closure.test),
- "E182: Invalid command name");
+ util.assert(!cmd || cmd.split(",").every(commands.validName.closure.test),
+ "E182: Invalid command name");
if (!args.literalArg)
commands.list();
else {
+ util.assert(args["-group"] !== commands.builtin,
+ "Cannot change commands in the builtin group");
+
let completer = args["-complete"];
let completerFunc = null; // default to no completion for user commands
@@ -1333,7 +1365,7 @@ var Commands = Module("commands", {
};
}
else
- completerFunc = function (context) completion.closure[config.completers[completer]](context);
+ completerFunc = function (context) modules.completion.closure[config.completers[completer]](context);
}
let added = args["-group"].add(cmd.split(","),
@@ -1364,8 +1396,9 @@ var Commands = Module("commands", {
}, {
bang: true,
completer: function (context, args) {
+ const { completion } = modules;
if (args.completeArg == 0)
- completion.userCommand(context);
+ completion.userCommand(context, args["-group"]);
else
args["-javascript"] ? completion.javascript(context) : completion.ex(context);
},
@@ -1442,17 +1475,20 @@ var Commands = Module("commands", {
commands.add(["comc[lear]"],
"Delete all user-defined commands",
- function () {
- commands.getUserCommands().forEach(function (cmd) { commands.removeUserCommand(cmd.name); });
+ function (args) {
+ args["-group"].clear();
},
- { argCount: "0" });
+ {
+ argCount: "0",
+ options: [contexts.GroupFlag("commands")]
+ });
commands.add(["comp[letions]"],
"List the completion results for a given command substring",
- function (args) { completion.listCompleter("ex", args[0]); },
+ function (args) { modules.completion.listCompleter("ex", args[0]); },
{
argCount: "1",
- completer: function (context, args) completion.ex(context),
+ completer: function (context, args) modules.completion.ex(context),
literal: 0
});
@@ -1461,13 +1497,14 @@ var Commands = Module("commands", {
function (args) {
let name = args[0];
- if (commands.get(name))
- commands.removeUserCommand(name);
+ if (args["-group"].get(name))
+ args["-group"].remove(name);
else
dactyl.echoerr("E184: No such user-defined command: " + name);
}, {
argCount: "1",
- completer: function (context) completion.userCommand(context)
+ completer: function (context, args) modules.completion.userCommand(context, args["-group"]),
+ options: [contexts.GroupFlag("commands")]
});
dactyl.addUsageCommand({
@@ -1491,22 +1528,29 @@ var Commands = Module("commands", {
"Yank the output of the given command to the clipboard",
function (args) {
let cmd = /^:/.test(args[0]) ? args[0] : ":echo " + args[0];
- let res = commandline.withOutputToString(commands.execute, commands, cmd);
+
+ let res = modules.commandline.withOutputToString(commands.execute, commands, cmd);
+
dactyl.clipboardWrite(res);
+
let lines = res.split("\n").length;
dactyl.echomsg("Yanked " + lines + " line" + (lines == 1 ? "" : "s"));
},
{
- completer: function (context) completion[/^:/.test(context.filter) ? "ex" : "javascript"](context),
+ completer: function (context) modules.completion[/^:/.test(context.filter) ? "ex" : "javascript"](context),
literal: 0
});
},
- javascript: function () {
+ javascript: function (dactyl, modules, window) {
+ const { JavaScript, commands } = modules;
+
JavaScript.setCompleter([commands.user.get, commands.user.remove],
- [function () ([c.name, c.description] for (c in this))]);
+ [function () [[c.name, c.description] for (c in this)]]);
},
- mappings: function () {
- mappings.add(config.browserModes,
+ mappings: function (dactyl, modules, window) {
+ const { commands, mappings, modes } = modules;
+
+ mappings.add([modes.COMMAND],
["@:"], "Repeat the last Ex command",
function (args) {
if (commands.repeat) {
@@ -1554,4 +1598,8 @@ var Commands = Module("commands", {
};
})();
-// vim: set fdm=marker sw=4 ts=4 et:
+endModule();
+
+} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
+
+// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
diff --git a/common/modules/completion.jsm b/common/modules/completion.jsm
index 9b177586..3374ec2d 100644
--- a/common/modules/completion.jsm
+++ b/common/modules/completion.jsm
@@ -42,9 +42,11 @@ var CompletionContext = Class("CompletionContext", {
let parent = editor;
name = parent.name + "/" + name;
- this.autoComplete = this.options.get("autocomplete").getKey(name);
- this.sortResults = this.options.get("wildsort").getKey(name);
- this.wildcase = this.options.get("wildcase").getKey(name);
+ if (this.options) {
+ this.autoComplete = this.options.get("autocomplete").getKey(name);
+ this.sortResults = this.options.get("wildsort").getKey(name);
+ this.wildcase = this.options.get("wildcase").getKey(name);
+ }
this.contexts = parent.contexts;
if (name in this.contexts)
@@ -448,7 +450,7 @@ var CompletionContext = Class("CompletionContext", {
let self = this;
delete this._substrings;
- if (!this.forceAnchored)
+ if (!this.forceAnchored && this.options)
this.anchored = this.options.get("wildanchor").getKey(this.name, this.anchored);
// Item matchers
diff --git a/common/modules/io.jsm b/common/modules/io.jsm
index ee1f8dbe..bb52400c 100644
--- a/common/modules/io.jsm
+++ b/common/modules/io.jsm
@@ -748,16 +748,17 @@ unlet s:cpo_save
return lines.map(function (l) l.join("")).join("\n").replace(/\s+\n/gm, "\n");
}
+ const { commands, options } = modules;
file.write(template({
name: config.name,
autocommands: wrap("syn keyword " + config.name + "AutoEvent ",
keys(config.autocommands)),
commands: wrap("syn keyword " + config.name + "Command ",
- array(c.specs for (c in commands)).flatten()),
+ array(c.specs for (c in commands.iterator())).flatten()),
options: wrap("syn keyword " + config.name + "Option ",
- array(o.names for (o in modules.options) if (o.type != "boolean")).flatten()),
+ array(o.names for (o in options) if (o.type != "boolean")).flatten()),
toggleoptions: wrap("let s:toggleOptions = [",
- array(o.realNames for (o in modules.options) if (o.type == "boolean"))
+ array(o.realNames for (o in options) if (o.type == "boolean"))
.flatten().map(String.quote),
", ") + "]"
}));
diff --git a/common/content/options.js b/common/modules/options.jsm
similarity index 84%
rename from common/content/options.js
rename to common/modules/options.jsm
index 67a4e3ec..a6d37d1e 100644
--- a/common/content/options.js
+++ b/common/modules/options.jsm
@@ -6,6 +6,16 @@
// given in the LICENSE.txt file included with this file.
"use strict";
+try {
+
+Components.utils.import("resource://dactyl/bootstrap.jsm");
+defineModule("options", {
+ exports: ["Option", "Options", "ValueError", "options"],
+ require: ["storage"],
+ use: ["commands", "completion", "prefs", "services", "template", "util"]
+}, this);
+
+
/** @scope modules */
let ValueError = Class("ValueError", ErrorBase);
@@ -83,7 +93,7 @@ var Option = Class("Option", {
get helpTag() "'" + this.name + "'",
initValue: function () {
- dactyl.trapErrors(function () this.value = this.value, this);
+ util.trapErrors(function () this.value = this.value, this);
},
get isDefault() this.stringValue === this.stringDefaultValue,
@@ -127,13 +137,15 @@ var Option = Class("Option", {
let values;
- if (dactyl.has("tabs") && (scope & Option.SCOPE_LOCAL))
+ /*
+ if (config.has("tabs") && (scope & Option.SCOPE_LOCAL))
values = tabs.options[this.name];
+ */
if ((scope & Option.SCOPE_GLOBAL) && (values == undefined))
values = this.globalValue;
if (this.getter)
- return dactyl.trapErrors(this.getter, this, values);
+ return util.trapErrors(this.getter, this, values);
return values;
},
@@ -151,19 +163,21 @@ var Option = Class("Option", {
return;
if (this.setter)
- newValues = dactyl.trapErrors(this.setter, this, newValues);
+ newValues = this.modules.dactyl.trapErrors(this.setter, this, newValues);
if (newValues === undefined)
return;
- if (dactyl.has("tabs") && (scope & Option.SCOPE_LOCAL))
+ /*
+ if (config.has("tabs") && (scope & Option.SCOPE_LOCAL))
tabs.options[this.name] = newValues;
+ */
if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal)
this.globalValue = newValues;
this.hasChanged = true;
this.setFrom = null;
- dactyl.triggerObserver("options." + this.name, newValues);
+ // dactyl.triggerObserver("options." + this.name, newValues);
},
getValues: deprecated("Option#get", "get"),
@@ -239,7 +253,7 @@ var Option = Class("Option", {
}
catch (e) {
if (!(e instanceof ValueError))
- dactyl.reportError(e);
+ util.reportError(e);
return this.invalidArgument(str || this.stringify(values), operator) + ": " + e.message;
}
@@ -480,6 +494,7 @@ var Option = Class("Option", {
Option._splitAt = 0;
return arg;
},
+
splitList: function (value, keepQuotes) {
let res = [];
Option._splitAt = 0;
@@ -495,6 +510,7 @@ var Option = Class("Option", {
}
return res;
},
+
quote: function quote(str, re)
Commands.quoteArg[/[\s|"'\\,]|^$/.test(str) || re && re.test && re.test(str)
? (/[\b\f\n\r\t]/.test(str) ? '"' : "'")
@@ -514,8 +530,8 @@ var Option = Class("Option", {
values = values[(values.indexOf(String(this.value)) + 1) % values.length];
let value = parseInt(values);
- dactyl.assert(Number(values) % 1 == 0,
- "E521: Number required after =: " + this.name + "=" + values);
+ util.assert(Number(values) % 1 == 0,
+ "E521: Number required after =: " + this.name + "=" + values);
switch (operator) {
case "+":
@@ -627,7 +643,7 @@ var Option = Class("Option", {
},
validateXPath: function (values) {
- let evaluator = XPathEvaluator();
+ let evaluator = services.XPathEvaluator();
return this.testValues(values,
function (value) evaluator.createExpression(value, util.evaluateXPath.resolver));
}
@@ -637,73 +653,124 @@ var Option = Class("Option", {
* @instance options
*/
var Options = Module("options", {
- init: function () {
- this.needInit = [];
- this._options = [];
- this._optionMap = {};
+ Local: function (dactyl, modules, window) let ({ contexts } = modules) ({
+ init: function init() {
+ const self = this;
+ this.needInit = [];
+ this._options = [];
+ this._optionMap = {};
+ this.Option = Class("Option", Option, { modules: modules });
- storage.newMap("options", { store: false });
- storage.addObserver("options", function optionObserver(key, event, option) {
- // Trigger any setters.
- let opt = options.get(option);
- if (event == "change" && opt)
- opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true);
- }, window);
- },
+ storage.newMap("options", { store: false });
+ storage.addObserver("options", function optionObserver(key, event, option) {
+ // Trigger any setters.
+ let opt = self.get(option);
+ if (event == "change" && opt)
+ opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true);
+ }, window);
+ },
- cleanup: function cleanup() {
- for (let opt in this)
- if (opt.cleanupValue != null)
- opt.value = opt.parse(opt.cleanupValue);
- },
+ dactyl: dactyl,
+
+ /**
+ * Lists all options in *scope* or only those with changed values if
+ * *onlyNonDefault* is specified.
+ *
+ * @param {function(Option)} filter Limit the list
+ * @param {number} scope Only list options in this scope (see
+ * {@link Option#scope}).
+ */
+ list: function (filter, scope) {
+ if (!scope)
+ scope = Option.SCOPE_BOTH;
+
+ function opts(opt) {
+ for (let opt in Iterator(options)) {
+ let option = {
+ __proto__: opt,
+ isDefault: opt.isDefault,
+ default: opt.stringDefaultValue,
+ pre: "\u00a0\u00a0", // Unicode nonbreaking space.
+ value: <>>
+ };
+
+ if (filter && !filter(opt))
+ continue;
+ if (!(opt.scope & scope))
+ continue;
+
+ if (opt.type == "boolean") {
+ if (!opt.value)
+ option.pre = "no";
+ option.default = (opt.defaultValue ? "" : "no") + opt.name;
+ }
+ else if (isArray(opt.value))
+ option.value = <>={template.map(opt.value, function (v) template.highlight(String(v)), <>, >)}>;
+ else
+ option.value = <>={template.highlight(opt.stringValue)}>;
+ yield option;
+ }
+ };
+
+ modules.commandline.commandOutput(template.options("Options", opts(), options["verbose"] > 0));
+ },
+
+ cleanup: function cleanup() {
+ for (let opt in this)
+ if (opt.cleanupValue != null)
+ opt.value = opt.parse(opt.cleanupValue);
+ },
+
+ /**
+ * Adds a new option.
+ *
+ * @param {string[]} names All names for the option.
+ * @param {string} description A description of the option.
+ * @param {string} type The option type (see {@link Option#type}).
+ * @param {value} defaultValue The option's default value.
+ * @param {Object} extra An optional extra configuration hash (see
+ * {@link Map#extraInfo}).
+ * @optional
+ */
+ add: function (names, description, type, defaultValue, extraInfo) {
+ const self = this;
+
+ if (!extraInfo)
+ extraInfo = {};
+
+ extraInfo.definedAt = contexts.getCaller(Components.stack.caller);
+
+ let name = names[0];
+ if (name in this._optionMap) {
+ this.dactyl.log("Warning: " + name.quote() + " already exists: replacing existing option.", 1);
+ this.remove(name);
+ }
+
+ let closure = function () self._optionMap[name];
+
+ memoize(this._optionMap, name, function () self.Option(names, description, type, defaultValue, extraInfo));
+ for (let alias in values(names.slice(1)))
+ memoize(this._optionMap, alias, closure);
+
+ if (extraInfo.setter && (!extraInfo.scope || extraInfo.scope & Option.SCOPE_GLOBAL))
+ if (this.dactyl.initialized)
+ closure().initValue();
+ else
+ memoize(this.needInit, this.needInit.length, closure);
+
+ this._floptions = (this._floptions || []).concat(name);
+ memoize(this._options, this._options.length, closure);
+
+ // quickly access options with options["wildmode"]:
+ this.__defineGetter__(name, function () this._optionMap[name].value);
+ this.__defineSetter__(name, function (value) { this._optionMap[name].value = value; });
+ }
+ }),
/** @property {Iterator(Option)} @private */
__iterator__: function ()
values(this._options.sort(function (a, b) String.localeCompare(a.name, b.name))),
- /**
- * Adds a new option.
- *
- * @param {string[]} names All names for the option.
- * @param {string} description A description of the option.
- * @param {string} type The option type (see {@link Option#type}).
- * @param {value} defaultValue The option's default value.
- * @param {Object} extra An optional extra configuration hash (see
- * {@link Map#extraInfo}).
- * @optional
- */
- add: function (names, description, type, defaultValue, extraInfo) {
- if (!extraInfo)
- extraInfo = {};
-
- extraInfo.definedAt = Commands.getCaller(Components.stack.caller);
-
- let name = names[0];
- if (name in this._optionMap) {
- dactyl.log("Warning: " + name.quote() + " already exists: replacing existing option.", 1);
- this.remove(name);
- }
-
- let closure = function () options._optionMap[name];
-
- memoize(this._optionMap, name, function () Option(names, description, type, defaultValue, extraInfo));
- for (let alias in values(names.slice(1)))
- memoize(this._optionMap, alias, closure);
-
- if (extraInfo.setter && (!extraInfo.scope || extraInfo.scope & Option.SCOPE_GLOBAL))
- if (dactyl.initialized)
- closure().initValue();
- else
- memoize(this.needInit, this.needInit.length, closure);
-
- this._floptions = (this._floptions || []).concat(name);
- memoize(this._options, this._options.length, closure);
-
- // quickly access options with options["wildmode"]:
- this.__defineGetter__(name, function () this._optionMap[name].value);
- this.__defineSetter__(name, function (value) { this._optionMap[name].value = value; });
- },
-
allPrefs: deprecated("prefs.getNames", function allPrefs() prefs.getNames.apply(prefs, arguments)),
getPref: deprecated("prefs.get", function getPref() prefs.get.apply(prefs, arguments)),
invertPref: deprecated("prefs.invert", function invertPref() prefs.invert.apply(prefs, arguments)),
@@ -734,49 +801,6 @@ var Options = Module("options", {
return null;
},
- /**
- * Lists all options in *scope* or only those with changed values if
- * *onlyNonDefault* is specified.
- *
- * @param {function(Option)} filter Limit the list
- * @param {number} scope Only list options in this scope (see
- * {@link Option#scope}).
- */
- list: function (filter, scope) {
- if (!scope)
- scope = Option.SCOPE_BOTH;
-
- function opts(opt) {
- for (let opt in Iterator(options)) {
- let option = {
- __proto__: opt,
- isDefault: opt.isDefault,
- default: opt.stringDefaultValue,
- pre: "\u00a0\u00a0", // Unicode nonbreaking space.
- value: <>>
- };
-
- if (filter && !filter(opt))
- continue;
- if (!(opt.scope & scope))
- continue;
-
- if (opt.type == "boolean") {
- if (!opt.value)
- option.pre = "no";
- option.default = (opt.defaultValue ? "" : "no") + opt.name;
- }
- else if (isArray(opt.value))
- option.value = <>={template.map(opt.value, function (v) template.highlight(String(v)), <>, >)}>;
- else
- option.value = <>={template.highlight(opt.stringValue)}>;
- yield option;
- }
- };
-
- commandline.commandOutput(template.options("Options", opts(), options["verbose"] > 0));
- },
-
/**
* Parses a :set command's argument string.
*
@@ -801,7 +825,7 @@ var Options = Module("options", {
}
if (matches) {
- ret.option = options.get(ret.name, ret.scope);
+ ret.option = this.get(ret.name, ret.scope);
if (!ret.option && (ret.option = options.get(prefix + ret.name, ret.scope))) {
ret.name = prefix + ret.name;
prefix = "";
@@ -849,7 +873,9 @@ var Options = Module("options", {
get store() storage.options
}, {
}, {
- commands: function () {
+ commands: function initCommands(dactyl, modules, window) {
+ const { commands, contexts, options } = modules;
+
let args = {
getMode: function (args) findMode(args["-mode"]),
iterate: function (args) {
@@ -916,7 +942,7 @@ var Options = Module("options", {
}
if (name == "all" && reset)
- commandline.input("Warning: Resetting all preferences may make " + config.host + " unusable. Continue (yes/[no]): ",
+ modules.commandline.input("Warning: Resetting all preferences may make " + config.host + " unusable. Continue (yes/[no]): ",
function (resp) {
if (resp == "yes")
for (let pref in values(prefs.getNames()))
@@ -946,22 +972,21 @@ var Options = Module("options", {
prefs.set(name, value);
}
else
- commandline.commandOutput(prefs.list(onlyNonDefault, name));
+ modules.commandline.commandOutput(prefs.list(onlyNonDefault, name));
return;
}
- let opt = options.parseOpt(arg, modifiers);
- dactyl.assert(opt, "Error parsing :set command: " + arg);
+ let opt = modules.options.parseOpt(arg, modifiers);
+ util.assert(opt, "Error parsing :set command: " + arg);
let option = opt.option;
- dactyl.assert(option != null || opt.all,
- "E518: Unknown option: " + opt.name);
+ util.assert(option != null || opt.all, "E518: Unknown option: " + opt.name);
// reset a variable to its default value
if (opt.reset) {
flushList();
if (opt.all) {
- for (let option in options)
+ for (let option in modules.options)
option.reset();
}
else {
@@ -975,7 +1000,7 @@ var Options = Module("options", {
else {
flushList();
if (opt.option.type === "boolean") {
- dactyl.assert(!opt.valueGiven, "E474: Invalid argument: " + arg);
+ util.assert(!opt.valueGiven, "E474: Invalid argument: " + arg);
opt.values = !opt.unsetBoolean;
}
else if (/^(string|number)$/.test(opt.option.type) && opt.invert)
@@ -989,7 +1014,7 @@ var Options = Module("options", {
}
if (res)
dactyl.echoerr(res);
- option.setFrom = Commands.getCaller(null);
+ option.setFrom = contexts.getCaller(null);
}
}
flushList();
@@ -1014,7 +1039,7 @@ var Options = Module("options", {
return completion.preference(context);
}
- let opt = options.parseOpt(filter, modifiers);
+ let opt = modules.options.parseOpt(filter, modifiers);
let prefix = opt.prefix;
context.highlight();
@@ -1055,7 +1080,7 @@ var Options = Module("options", {
}
let optcontext = context.fork("values");
- completion.optionValue(optcontext, opt.name, opt.operator);
+ modules.completion.optionValue(optcontext, opt.name, opt.operator);
// Fill in the current values if we're removing
if (opt.operator == "-" && isArray(opt.values)) {
@@ -1065,7 +1090,7 @@ var Options = Module("options", {
context.maxItems = optcontext.maxItems;
context.filters.push(function (i) !set.has(have, i.text));
- completion.optionValue(context, opt.name, opt.operator, null,
+ modules.completion.optionValue(context, opt.name, opt.operator, null,
function (context) {
context.generate = function () option.value.map(function (o) [o, ""]);
});
@@ -1108,10 +1133,10 @@ var Options = Module("options", {
let [, scope, name, op, expr] = matches;
let fullName = (scope || "") + name;
- dactyl.assert(scope == "g:" || scope == null,
- "E461: Illegal variable name: " + scope + name);
- dactyl.assert(set.has(globalVariables, name) || (expr && !op),
- "E121: Undefined variable: " + fullName);
+ util.assert(scope == "g:" || scope == null,
+ "E461: Illegal variable name: " + scope + name);
+ util.assert(set.has(globalVariables, name) || (expr && !op),
+ "E121: Undefined variable: " + fullName);
if (!expr)
dactyl.echo(fullName + "\t\t" + fmt(globalVariables[name]));
@@ -1120,7 +1145,7 @@ var Options = Module("options", {
var newValue = dactyl.userEval(expr);
}
catch (e) {}
- dactyl.assert(newValue !== undefined,
+ util.assert(newValue !== undefined,
"E15: Invalid expression: " + expr);
let value = newValue;
@@ -1167,7 +1192,7 @@ var Options = Module("options", {
literalArg: [opt.type == "boolean" ? (opt.value ? "" : "no") + opt.name
: opt.name + "=" + opt.stringValue]
}
- for (opt in options)
+ for (opt in modules.options)
if (!opt.getter && !opt.isDefault && (opt.scope & Option.SCOPE_GLOBAL))
]
}
@@ -1182,18 +1207,18 @@ var Options = Module("options", {
completer: setCompleter,
domains: function (args) array.flatten(args.map(function (spec) {
try {
- let opt = options.parseOpt(spec);
+ let opt = modules.options.parseOpt(spec);
if (opt.option && opt.option.domains)
return opt.option.domains(opt.values);
}
catch (e) {
- dactyl.reportError(e);
+ util.reportError(e);
}
return [];
})),
keepQuotes: true,
privateData: function (args) args.some(function (spec) {
- let opt = options.parseOpt(spec);
+ let opt = modules.options.parseOpt(spec);
return opt.option && opt.option.privateData &&
(!callable(opt.option.privateData) ||
opt.option.privateData(opt.values));
@@ -1223,12 +1248,14 @@ var Options = Module("options", {
deprecated: "the options system"
});
},
- completion: function () {
+ completion: function initCompletion(dactyl, modules, window) {
+ const { completion } = modules;
+
completion.option = function option(context, scope, prefix) {
context.title = ["Option"];
context.keys = { text: "names", description: "description" };
context.anchored = false;
- context.completions = options;
+ context.completions = modules.options;
if (prefix == "inv")
context.keys.text = function (opt)
opt.type == "boolean" || isArray(opt.value) ? opt.names.map(function (n) "inv" + n)
@@ -1238,7 +1265,7 @@ var Options = Module("options", {
};
completion.optionValue = function (context, name, op, curValue, completer) {
- let opt = options.get(name);
+ let opt = modules.options.get(name);
completer = completer || opt.completer;
if (!completer || !opt)
return;
@@ -1301,15 +1328,18 @@ var Options = Module("options", {
context.completions = res;
};
},
- javascript: function () {
+ javascript: function initJavascript(dactyl, modules, window) {
+ const { options, JavaScript } = modules;
JavaScript.setCompleter(options.get, [function () ([o.name, o.description] for (o in options))]);
},
- sanitizer: function () {
+ sanitizer: function initSanitizer(dactyl, modules, window) {
+ const { sanitizer } = modules;
+
sanitizer.addItem("options", {
description: "Options containing hostname data",
action: function (timespan, host) {
if (host)
- for (let opt in values(options._options))
+ for (let opt in values(modules.options._options))
if (timespan.contains(opt.lastSet * 1000) && opt.domains)
try {
opt.value = opt.filterDomain(host, opt.value);
@@ -1319,12 +1349,12 @@ var Options = Module("options", {
}
},
privateEnter: function () {
- for (let opt in values(options._options))
+ for (let opt in values(modules.options._options))
if (opt.privateData && (!callable(opt.privateData) || opt.privateData(opt.value)))
opt.oldValue = opt.value;
},
privateLeave: function () {
- for (let opt in values(options._options))
+ for (let opt in values(modules.options._options))
if (opt.oldValue != null) {
opt.value = opt.oldValue;
opt.oldValue = null;
@@ -1334,4 +1364,8 @@ var Options = Module("options", {
}
});
-// vim: set fdm=marker sw=4 ts=4 et:
+endModule();
+
+} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
+
+// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm
index af88c6be..c878c724 100644
--- a/common/modules/overlay.jsm
+++ b/common/modules/overlay.jsm
@@ -154,6 +154,7 @@ var Overlay = Module("Overlay", {
defineModule.time("load", null, function _load() {
["addons",
"base",
+ "commands",
"completion",
"config",
"downloads",
@@ -161,6 +162,7 @@ var Overlay = Module("Overlay", {
"highlight",
"io",
"javascript",
+ "options",
"overlay",
"prefs",
"services",
@@ -177,14 +179,12 @@ var Overlay = Module("Overlay", {
"abbreviations",
"autocommands",
"buffer",
- "commands",
"editor",
"events",
"hints",
"mappings",
"marks",
"mow",
- "options",
"statusline"
].forEach(function (name) defineModule.time("load", name, modules.load, modules, name));
@@ -325,6 +325,8 @@ var Overlay = Module("Overlay", {
}
});
+endModule();
+
} catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
diff --git a/common/modules/services.jsm b/common/modules/services.jsm
index 6c38529b..7b31a422 100644
--- a/common/modules/services.jsm
+++ b/common/modules/services.jsm
@@ -84,6 +84,7 @@ var Services = Module("Services", {
this.addClass("Timer", "@mozilla.org/timer;1", Ci.nsITimer, "initWithCallback");
this.addClass("StreamCopier", "@mozilla.org/network/async-stream-copier;1",Ci.nsIAsyncStreamCopier, "init");
this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", Ci.nsIXMLHttpRequest);
+ this.addClass("XPathEvaluator", "@mozilla.org/dom/xpath-evaluator;1", Ci.nsIDOMXPathEvaluator);
this.addClass("ZipReader", "@mozilla.org/libjar/zip-reader;1", Ci.nsIZipReader, "open");
this.addClass("ZipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter);
},
diff --git a/common/modules/styles.jsm b/common/modules/styles.jsm
index 921e11bc..245feccd 100644
--- a/common/modules/styles.jsm
+++ b/common/modules/styles.jsm
@@ -623,6 +623,9 @@ var Styles = Module("Styles", {
this.hive.addRef(this);
},
+ get names() this.hive.names,
+ get sheets() this.hive.sheets,
+
__noSuchMethod__: function __noSuchMethod__(meth, args) {
return this.hive[meth].apply(this.hive, args);
},
diff --git a/common/modules/util.jsm b/common/modules/util.jsm
index b2d62ad4..18f7e83a 100644
--- a/common/modules/util.jsm
+++ b/common/modules/util.jsm
@@ -13,7 +13,7 @@ Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("util", {
exports: ["frag", "FailedAssertion", "Math", "NS", "Point", "Util", "XBL", "XHTML", "XUL", "util"],
require: ["services"],
- use: ["config", "highlight", "storage", "template"]
+ use: ["commands", "config", "highlight", "storage", "template"]
}, this);
var XBL = Namespace("xbl", "http://www.mozilla.org/xbl");
@@ -22,13 +22,6 @@ var XUL = Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is
var NS = Namespace("dactyl", "http://vimperator.org/namespaces/liberator");
default xml namespace = XHTML;
-memoize(this, "Commands", function () {
- // FIXME
- let obj = { Module: Class };
- JSMLoader.loadSubScript("resource://dactyl-content/commands.js", obj);
- return obj.Commands;
-});
-
var FailedAssertion = Class("FailedAssertion", ErrorBase);
var Point = Struct("x", "y");
diff --git a/common/tests/functional/dactyl.jsm b/common/tests/functional/dactyl.jsm
index b32b7313..f7ce56ab 100644
--- a/common/tests/functional/dactyl.jsm
+++ b/common/tests/functional/dactyl.jsm
@@ -64,7 +64,7 @@ function Controller(controller) {
}
this.errors = [];
this._countError = function countError(message, highlight) {
- if (/\bErrorMsg\b/.test(highlight))
+ if (/\b(Error|Warning)Msg\b/.test(highlight))
self.errors.push(String(message));
}
this.modules.dactyl.registerObserver("beep", this._countBeep);
diff --git a/common/tests/functional/testCommands.js b/common/tests/functional/testCommands.js
index 618b688f..1d439861 100644
--- a/common/tests/functional/testCommands.js
+++ b/common/tests/functional/testCommands.js
@@ -122,7 +122,17 @@ var tests = {
singleOutput: ["", "foobar"],
noOutput: ["foo bar", "-js bar baz"],
multiOutput: [""],
- error: ["foo bar", "-js bar baz"]
+ error: [
+ "foo bar",
+ "-js bar baz",
+ "-group=builtin baz quux",
+ "! -group=builtin baz quux",
+ ],
+ completions: [
+ ["", hasItems],
+ ["-group=", hasItems],
+ ["-group=user ", hasItems]
+ ]
},
comclear: {
noOutput: [""]
@@ -140,6 +150,11 @@ var tests = {
delcommand: [
{
init: ["comclear", "command foo bar"],
+ completions: [
+ ["", hasItems],
+ ["-group=", hasItems],
+ ["-group=user ", hasItems]
+ ],
noOutput: ["foo"]
},
{
@@ -152,7 +167,6 @@ var tests = {
noOutput: ["x"],
completions: ["", "x"]
},
- delmapgroup: {}, // Skip for now
get delmarks() this.delmacros,
get delqmarks() this.delmacros,
delstyle: {
@@ -233,6 +247,25 @@ var tests = {
finish: { noOutput: [""] },
forward: { noOutput: [""] },
frameonly: { noOutput: [""] },
+ delgroup: {
+ error: ["builtin"],
+ completions: [""]
+ },
+ group: {
+ multiOutput: [""],
+ noOutput: [
+ "foo -d='foo group' -nopersist -l 'bar.com','http://bar/*','http://bar','^http:'",
+ "! foo -d='foo group' -nopersist -l 'bar.com','http://bar/*','http://bar','^http:'",
+ "foo",
+ "user"
+ ],
+ error: ["builtin"],
+ completions: [
+ "",
+ "foo "
+ ],
+ cleanup: ["delmapgroup foo"]
+ },
hardcopy: {}, // Skip for now
help: {
noOutput: ["", "intro"],
@@ -318,7 +351,8 @@ var tests = {
],
error: [
"-mode=some-nonexistent-mode ",
- "-gtroup=some-nonexistent-group "
+ "-group=some-nonexistent-group ",
+ "-group=builtin "
],
completions: [
["", hasItems],
@@ -333,25 +367,13 @@ var tests = {
},
mapclear: {
noOutput: [""],
- completions: [""]
- },
- mapgroup: {
- multiOutput: [""],
- noOutput: [
- "foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'",
- "! foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'",
- "foo",
- "user"
- ],
error: [
- "some-nonexistent-group",
- "foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'"
+ "-group=builtin"
],
completions: [
"",
- "foo "
- ],
- cleanup: ["delmapgroup foo"]
+ "-group="
+ ]
},
mark: {
error: ["", "#", "xy"],