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( + + + + + + + + + + + { + template.map(this.userHives, function (hive) let (i = 0) + + + template.map(hive, function (cmd) + template.map(cmd.names, function (name) + + + + + + + + + )) + + ) + } +
+ NameArgsRangeCompleteDefinition
{!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( - - - - - - - - - - - { - template.map(commands.userHives, function (hive) let (i = 0) - + - template.map(hive, function (cmd) - template.map(cmd.names, function (name) - - - - - - - - - )) + - ) - } -
- NameArgsRangeCompleteDefinition
{!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"],