1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-21 18:57:59 +01:00

Experimentally move commands.js and options.js to modules. Fix some bugs.

--HG--
branch : groups
rename : common/content/commands.js => common/modules/commands.jsm
rename : common/content/options.js => common/modules/options.jsm
This commit is contained in:
Kris Maglione
2011-02-10 15:40:09 -05:00
parent 1299fddfd3
commit 60063a8f91
12 changed files with 595 additions and 463 deletions

View File

@@ -148,6 +148,23 @@ var Contexts = Module("contexts", {
context: null, 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: Class.memoize(function () Object.create(Group.groupsProto, {
groups: { value: this.activeGroups().filter(function (g) g.filter(buffer.uri)) } groups: { value: this.activeGroups().filter(function (g) g.filter(buffer.uri)) }
})), })),
@@ -216,8 +233,8 @@ var Contexts = Module("contexts", {
getGroup: function getGroup(name, hive) { getGroup: function getGroup(name, hive) {
if (name === "default") if (name === "default")
var group = this.context && this.context.context && this.context.context.GROUP; var group = this.context && this.context.context && this.context.context.GROUP;
else else if (set.has(this.groupMap, name))
group = set.has(this.groupMap, name) && this.groupMap[name]; group = this.groupMap[name];
if (group && hive) if (group && hive)
return group[hive]; return group[hive];

View File

@@ -170,7 +170,7 @@ var MapHive = Class("MapHive", Group.Hive, {
extra = extra || {}; extra = extra || {};
let map = Map(modes, keys, description, action, 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; map.hive = this;
if (this.name !== "builtin") if (this.name !== "builtin")
@@ -341,7 +341,7 @@ var Mappings = Module("mappings", {
*/ */
add: function () { add: function () {
let map = this.builtin.add.apply(this.builtin, arguments); 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; return map;
}, },
@@ -358,7 +358,7 @@ var Mappings = Module("mappings", {
*/ */
addUserMap: function () { addUserMap: function () {
let map = this.user.add.apply(this.user, arguments); 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; return map;
}, },
@@ -454,6 +454,9 @@ var Mappings = Module("mappings", {
if (!rhs) // list the mapping if (!rhs) // list the mapping
mappings.list(mapmodes, mappings.expandLeader(lhs), hives); mappings.list(mapmodes, mappings.expandLeader(lhs), hives);
else { else {
util.assert(args["-group"] !== mappings.builtin,
"Cannot change mappings in the builtin group");
args["-group"].add(mapmodes, [lhs], args["-group"].add(mapmodes, [lhs],
args["-description"], args["-description"],
contexts.bindMacro(args, "-keys", function (params) params), contexts.bindMacro(args, "-keys", function (params) params),
@@ -572,6 +575,9 @@ var Mappings = Module("mappings", {
commands.add([ch + "mapc[lear]"], commands.add([ch + "mapc[lear]"],
"Remove all mappings" + modeDescription, "Remove all mappings" + modeDescription,
function (args) { function (args) {
util.assert(args["-group"] !== mappings.builtin,
"Cannot change mappings in the builtin group");
let mapmodes = array.uniq(args["-modes"].map(findMode)); let mapmodes = array.uniq(args["-modes"].map(findMode));
mapmodes.forEach(function (mode) { mapmodes.forEach(function (mode) {
args["-group"].clear(mode); args["-group"].clear(mode);
@@ -593,6 +599,9 @@ var Mappings = Module("mappings", {
commands.add([ch + "unm[ap]"], commands.add([ch + "unm[ap]"],
"Remove a mapping" + modeDescription, "Remove a mapping" + modeDescription,
function (args) { function (args) {
util.assert(args["-group"] !== mappings.builtin,
"Cannot change mappings in the builtin group");
let mapmodes = array.uniq(args["-modes"].map(findMode)); let mapmodes = array.uniq(args["-modes"].map(findMode));
let found = false; let found = false;

View File

@@ -6,6 +6,15 @@
// given in the LICENSE.txt file included with this file. // given in the LICENSE.txt file included with this file.
"use strict"; "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 */ /** @scope modules */
/** /**
@@ -141,6 +150,8 @@ var Command = Class("Command", {
* @param {Object} modifiers Any modifiers to be passed to {@link #action}. * @param {Object} modifiers Any modifiers to be passed to {@link #action}.
*/ */
execute: function (args, modifiers) { execute: function (args, modifiers) {
const { dactyl } = this.modules;
let context = args.context; let context = args.context;
if (this.deprecated && !set.add(this.complained, context ? context.file : "[Command Line]")) { if (this.deprecated && !set.add(this.complained, context ? context.file : "[Command Line]")) {
let loc = contexts.context ? context.file + ":" + context.line + ": " : ""; let loc = contexts.context ? context.file + ":" + context.line + ": " : "";
@@ -190,7 +201,7 @@ var Command = Class("Command", {
* @returns {Args} * @returns {Args}
* @see Commands#parseArgs * @see Commands#parseArgs
*/ */
parseArgs: function (args, complete, extra) commands.parseArgs(args, { parseArgs: function parseArgs(args, complete, extra) this.modules.commands.parseArgs(args, {
__proto__: this, __proto__: this,
complete: complete, complete: complete,
extra: extra extra: extra
@@ -265,8 +276,9 @@ var Command = Class("Command", {
optionMap: Class.memoize(function () array(this.options) optionMap: Class.memoize(function () array(this.options)
.map(function (opt) opt.names.map(function (name) [name, opt])) .map(function (opt) opt.names.map(function (name) [name, opt]))
.flatten().toObject()), .flatten().toObject()),
newArgs: function () { newArgs: function (base) {
let res = []; let res = [];
update(res, base);
res.__proto__ = this.argsPrototype; res.__proto__ = this.argsPrototype;
return res; return res;
}, },
@@ -279,8 +291,6 @@ var Command = Class("Command", {
command: this, command: this,
get context() contexts.context,
explicitOpts: Class.memoize(function () ({})), explicitOpts: Class.memoize(function () ({})),
has: function (opt) set.has(this.explicitOpts, opt) || typeof opt === "number" && set.has(this, opt), has: function (opt) set.has(this.explicitOpts, opt) || typeof opt === "number" && set.has(this, opt),
@@ -290,11 +300,11 @@ var Command = Class("Command", {
// TODO: string: Class.memoize(function () { ... }), // TODO: string: Class.memoize(function () { ... }),
verify: function verify() { verify: function verify() {
if (this.command.argCount) { if (this.command.argCount) {
dactyl.assert((this.length > 0 || !/^[1+]$/.test(this.command.argCount)) && util.assert((this.length > 0 || !/^[1+]$/.test(this.command.argCount)) &&
(this.literal == null || !/[1+]/.test(this.command.argCount) || /\S/.test(this.literalArg || "")), (this.literal == null || !/[1+]/.test(this.command.argCount) || /\S/.test(this.literalArg || "")),
"E471: Argument required"); "E471: Argument required");
dactyl.assert((this.length == 0 || this.command.argCount !== "0") && util.assert((this.length == 0 || this.command.argCount !== "0") &&
(this.length <= 1 || !/^[01?]$/.test(this.command.argCount)), (this.length <= 1 || !/^[01?]$/.test(this.command.argCount)),
"E488: Trailing characters"); "E488: Trailing characters");
} }
@@ -352,11 +362,16 @@ var Command = Class("Command", {
}); });
// Prototype. // 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: function (cmd, args) {
args = Array.slice(args); args = Array.slice(args);
let res = cmd.newArgs(); let res = cmd.newArgs({ context: this.context });
if (isObject(args[0])) if (isObject(args[0]))
for (let [k, v] in Iterator(args.shift())) for (let [k, v] in Iterator(args.shift()))
if (k == "!") if (k == "!")
@@ -366,7 +381,7 @@ var ex = {
else { else {
let opt = cmd.optionMap["-" + k]; let opt = cmd.optionMap["-" + k];
let val = opt.type && opt.type.parse(v); let val = opt.type && opt.type.parse(v);
dactyl.assert(val != null && (typeof val !== "number" || !isNaN(val)), util.assert(val != null && (typeof val !== "number" || !isNaN(val)),
"No such option: " + k); "No such option: " + k);
Class.replaceProperty(args, opt.names[0], val); Class.replaceProperty(args, opt.names[0], val);
args.explicitOpts[opt.names[0]] = val; args.explicitOpts[opt.names[0]] = val;
@@ -376,31 +391,49 @@ var ex = {
return res; return res;
}, },
_complete: function (cmd) _complete: function (cmd) let (self = this)
function _complete(context, func, obj, args) { function _complete(context, func, obj, args) {
args = ex._args(cmd, args); args = self._args(cmd, args);
args.completeArg = args.length - 1; args.completeArg = args.length - 1;
if (cmd.completer && args.length) if (cmd.completer && args.length)
return cmd.completer(context, args); return cmd.completer(context, args);
}, },
_run: function (name) { _run: function (name) {
let cmd = commands.get(name); const self = this;
dactyl.assert(cmd, "No such command"); let cmd = this.commands.get(name);
util.assert(cmd, "No such command");
return update(function exCommand(options) { return update(function exCommand(options) {
let args = ex._args(cmd, arguments); let args = self._args(cmd, arguments);
args.verify(); args.verify();
return cmd.execute(args); return cmd.execute(args);
}, { }, {
dactylCompleter: ex._complete(cmd) dactylCompleter: self._complete(cmd)
}); });
}, },
__noSuchMethod__: function (meth, args) this._run(meth).apply(this, args) __noSuchMethod__: function (meth, args) this._run(meth).apply(this, args)
}; });
var CommandHive = Class("CommandHive", Group.Hive, { /**
* @instance commands
*/
var Commands = Module("commands", {
lazyInit: true,
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 context() contexts.context,
get readHeredoc() modules.io.readHeredoc,
hives: Group.Hives("commands", Class("CommandHive", Group.Hive, {
init: function init(group) { init: function init(group) {
init.supercall(this, group); init.supercall(this, group);
this._map = {}; this._map = {};
@@ -425,9 +458,11 @@ var CommandHive = Class("CommandHive", Group.Hive, {
* @optional * @optional
*/ */
add: function (names, description, action, extra, replace) { add: function (names, description, action, extra, replace) {
const { commands } = modules;
extra = extra || {}; extra = extra || {};
if (!extra.definedAt) if (!extra.definedAt)
extra.definedAt = Commands.getCaller(Components.stack.caller); extra.definedAt = contexts.getCaller(Components.stack.caller);
extra.hive = this; extra.hive = this;
extra.parsedSpecs = Command.parseSpecs(names); extra.parsedSpecs = Command.parseSpecs(names);
@@ -435,10 +470,10 @@ var CommandHive = Class("CommandHive", Group.Hive, {
let names = array.flatten(extra.parsedSpecs); let names = array.flatten(extra.parsedSpecs);
let name = names[0]; let name = names[0];
dactyl.assert(!names.some(function (name) name in commands.builtin._map), util.assert(!names.some(function (name) name in commands.builtin._map),
"E182: Can't replace non-user command: " + name); "E182: Can't replace non-user command: " + name);
dactyl.assert(replace || names.every(function (name) !(name in this._map), this), util.assert(replace || names.every(function (name) !(name in this._map), this),
"Not replacing command " + name); "Not replacing command " + name);
for (let name in values(names)) { for (let name in values(names)) {
@@ -450,19 +485,31 @@ var CommandHive = Class("CommandHive", Group.Hive, {
let self = this; let self = this;
let closure = function () self._map[name]; let closure = function () self._map[name];
memoize(this._map, name, function () Command(names, description, action, extra)); memoize(this._map, name, function () commands.Command(names, description, action, extra));
memoize(this._list, this._list.length, closure); memoize(this._list, this._list.length, closure);
for (let alias in values(names.slice(1))) for (let alias in values(names.slice(1)))
memoize(this._map, alias, closure); memoize(this._map, alias, closure);
return name; return name;
}, },
_add: function (names, description, action, extra, replace) { _add: function (names, description, action, extra, replace) {
extra = extra || {}; extra = extra || {};
extra.definedAt = Commands.getCaller(Components.stack.caller.caller); extra.definedAt = contexts.getCaller(Components.stack.caller.caller);
return this.add.apply(this, arguments); 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*. * Returns the command with matching *name*.
* *
@@ -472,7 +519,7 @@ var CommandHive = Class("CommandHive", Group.Hive, {
* its names matches *name* exactly. * its names matches *name* exactly.
* @returns {Command} * @returns {Command}
*/ */
get: function (name, full) this._map[name] get: function get(name, full) this._map[name]
|| !full && array.nth(this._list, function (cmd) cmd.hasName(name), 0) || !full && array.nth(this._list, function (cmd) cmd.hasName(name), 0)
|| null, || null,
@@ -482,8 +529,8 @@ var CommandHive = Class("CommandHive", Group.Hive, {
* @param {string} name The name of the command to remove. This can be * @param {string} name The name of the command to remove. This can be
* any of the command's names. * any of the command's names.
*/ */
remove: function (name) { remove: function remove(name) {
dactyl.assert(this.group !== contexts.default, util.assert(this.group !== contexts.default,
"Cannot delete non-user commands"); "Cannot delete non-user commands");
let cmd = this.get(name); let cmd = this.get(name);
@@ -491,23 +538,118 @@ var CommandHive = Class("CommandHive", Group.Hive, {
for (let name in values(cmd.names)) for (let name in values(cmd.names))
delete this._map[name]; delete this._map[name];
} }
}); })),
/**
* @instance commands
*/
var Commands = Module("commands", {
init: function () {
this.user = contexts.hives.commands.user;
this.builtin = contexts.hives.commands.builtin;
},
hives: Group.Hives("commands", CommandHive),
get allHives() contexts.allGroups.commands, get allHives() contexts.allGroups.commands,
get userHives() this.allHives.filter(function (h) h !== this.builtin, this), 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(
<table>
<tr highlight="Title">
<td/>
<td style="padding-right: 1em;"></td>
<td style="padding-right: 1ex;">Name</td>
<td style="padding-right: 1ex;">Args</td>
<td style="padding-right: 1ex;">Range</td>
<td style="padding-right: 1ex;">Complete</td>
<td style="padding-right: 1ex;">Definition</td>
</tr>
<col style="min-width: 6em; padding-right: 1em;"/>
{
template.map(this.userHives, function (hive) let (i = 0)
<tr style="height: .5ex;"/> +
template.map(hive, function (cmd)
template.map(cmd.names, function (name)
<tr>
<td highlight="Title">{!i++ ? hive.name : ""}</td>
<td>{cmd.bang ? "!" : " "}</td>
<td>{cmd.name}</td>
<td>{cmd.argCount}</td>
<td>{cmd.count ? "0c" : ""}</td>
<td>{completerToString(cmd.completer)}</td>
<td>{cmd.replacementText || "function () { ... }"}</td>
</tr>)) +
<tr style="height: .5ex;"/>)
}
</table>);
}
}),
/** /**
* @property Indicates that no count was specified for this * @property Indicates that no count was specified for this
* command invocation. * command invocation.
@@ -532,7 +674,7 @@ var Commands = Module("commands", {
add: function () this.builtin._add.apply(this.builtin, arguments), add: function () this.builtin._add.apply(this.builtin, arguments),
addUserCommand: deprecated("commands.user.add", { get: function addUserCommand() this.user.closure._add }), 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 }), 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)) get: function (name, full) iter(this.hives).map(function ([i, hive]) hive.get(name, full))
.nth(util.identity, 0), .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(
<table>
<tr highlight="Title">
<td/>
<td style="padding-right: 1em;"></td>
<td style="padding-right: 1ex;">Name</td>
<td style="padding-right: 1ex;">Args</td>
<td style="padding-right: 1ex;">Range</td>
<td style="padding-right: 1ex;">Complete</td>
<td style="padding-right: 1ex;">Definition</td>
</tr>
<col style="min-width: 6em; padding-right: 1em;"/>
{
template.map(commands.userHives, function (hive) let (i = 0)
<tr style="height: .5ex;"/> +
template.map(hive, function (cmd)
template.map(cmd.names, function (name)
<tr>
<td highlight="Title">{!i++ ? hive.name : ""}</td>
<td>{cmd.bang ? "!" : " "}</td>
<td>{cmd.name}</td>
<td>{cmd.argCount}</td>
<td>{cmd.count ? "0c" : ""}</td>
<td>{completerToString(cmd.completer)}</td>
<td>{cmd.replacementText || "function () { ... }"}</td>
</tr>)) +
<tr style="height: .5ex;"/>)
}
</table>);
},
/**
* 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 * Returns true if a command invocation contains a URL referring to the
* domain *host*. * domain *host*.
@@ -698,7 +735,7 @@ var Commands = Module("commands", {
return true; return true;
} }
catch (e) { catch (e) {
dactyl.reportError(e); util.reportError(e);
} }
return false; return false;
}, },
@@ -764,7 +801,9 @@ var Commands = Module("commands", {
* Args object. * Args object.
* @returns {Args} * @returns {Args}
*/ */
parseArgs: function (str, params) { parseArgs: function parseArgs(str, params) {
const self = this;
function getNextArg(str, _keepQuotes) { function getNextArg(str, _keepQuotes) {
if (arguments.length < 2) if (arguments.length < 2)
_keepQuotes = keepQuotes; _keepQuotes = keepQuotes;
@@ -774,7 +813,7 @@ var Commands = Module("commands", {
let count = arg.length + 2; let count = arg.length + 2;
if (complete) if (complete)
return [count, "", ""]; return [count, "", ""];
return [count, io.readHeredoc(arg), ""]; return [count, self.readHeredoc(arg), ""];
} }
let [count, arg, quote] = Commands.parseArg(str, null, _keepQuotes); let [count, arg, quote] = Commands.parseArg(str, null, _keepQuotes);
@@ -795,7 +834,7 @@ var Commands = Module("commands", {
if (!argCount) if (!argCount)
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 args.string = str; // for access to the unparsed string
// FIXME! // FIXME!
@@ -832,7 +871,7 @@ var Commands = Module("commands", {
if (complete) if (complete)
complete.message = error; complete.message = error;
else else
dactyl.assert(false, error); util.assert(false, error);
}; };
outer: outer:
@@ -873,7 +912,7 @@ var Commands = Module("commands", {
if (sep == "=" || /\s/.test(sep) && opt.type != CommandOption.NOARG) { if (sep == "=" || /\s/.test(sep) && opt.type != CommandOption.NOARG) {
[count, quoted, quote, error] = getNextArg(sub.substr(optname.length + 1), true); [count, quoted, quote, error] = getNextArg(sub.substr(optname.length + 1), true);
arg = Option.dequote(quoted); 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 we add the argument to an option after a space, it MUST not be empty
if (sep != "=" && !quote && arg.length == 0) if (sep != "=" && !quote && arg.length == 0)
@@ -974,7 +1013,7 @@ var Commands = Module("commands", {
// if not an option, treat this token as an argument // if not an option, treat this token as an argument
let [count, arg, quote, error] = getNextArg(sub); let [count, arg, quote, error] = getNextArg(sub);
dactyl.assert(!error, error); util.assert(!error, error);
if (complete) { if (complete) {
args.quote = Commands.complQuote[quote] || Commands.complQuote[""]; args.quote = Commands.complQuote[quote] || Commands.complQuote[""];
@@ -1127,7 +1166,7 @@ var Commands = Module("commands", {
parseCommands: function (str, complete) { parseCommands: function (str, complete) {
do { do {
let [count, cmd, bang, args, len] = commands.parseCommand(str); let [count, cmd, bang, args, len] = commands.parseCommand(str);
let command = commands.get(cmd || ""); let command = this.get(cmd || "");
if (command == null) { if (command == null) {
yield [null, { commandString: str }]; yield [null, { commandString: str }];
@@ -1142,7 +1181,8 @@ var Commands = Module("commands", {
if (!complete || /(\w|^)[!\s]/.test(str)) if (!complete || /(\w|^)[!\s]/.test(str))
args = command.parseArgs(args, context, { count: count, bang: bang }); args = command.parseArgs(args, context, { count: count, bang: bang });
else 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.commandName = cmd;
args.commandString = str.substr(0, len) + args.string; args.commandString = str.substr(0, len) + args.string;
str = args.trailing; str = args.trailing;
@@ -1175,23 +1215,6 @@ var Commands = Module("commands", {
get quoteArg() Commands.quoteArg // XXX: better somewhere else? 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] // returns [count, parsed_argument]
parseArg: function parseArg(str, sep, keepQuotes) { parseArg: function parseArg(str, sep, keepQuotes) {
let arg = ""; let arg = "";
@@ -1233,15 +1256,19 @@ var Commands = Module("commands", {
/[\s"'\\]|^$|^-/.test(str) ? "'" /[\s"'\\]|^$|^-/.test(str) ? "'"
: ""](str) : ""](str)
}, { }, {
completion: function () { completion: function initCompletion(dactyl, modules, window) {
const { completion } = modules;
completion.command = function command(context) { completion.command = function command(context) {
context.title = ["Command"]; context.title = ["Command"];
context.keys = { text: "longNames", description: "description" }; 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 // provides completions for ex commands, including their arguments
completion.ex = function ex(context) { completion.ex = function ex(context) {
const { commands } = modules;
// if there is no space between the command name and the cursor // if there is no space between the command name and the cursor
// then get completions of the command name // then get completions of the command name
for (var [command, args] in commands.parseCommands(context.filter, context)) for (var [command, args] in commands.parseCommands(context.filter, context))
@@ -1280,18 +1307,20 @@ var Commands = Module("commands", {
} }
} }
catch (e) { 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.title = ["User Command", "Definition"];
context.keys = { text: "name", description: "replacementText" }; 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 // TODO: Vim allows commands to be defined without {rep} if there are {attr}s
// specified - useful? // specified - useful?
commands.add(["com[mand]"], commands.add(["com[mand]"],
@@ -1299,12 +1328,15 @@ var Commands = Module("commands", {
function (args) { function (args) {
let cmd = args[0]; let cmd = args[0];
dactyl.assert(!cmd || cmd.split(",").every(commands.validName.closure.test), util.assert(!cmd || cmd.split(",").every(commands.validName.closure.test),
"E182: Invalid command name"); "E182: Invalid command name");
if (!args.literalArg) if (!args.literalArg)
commands.list(); commands.list();
else { else {
util.assert(args["-group"] !== commands.builtin,
"Cannot change commands in the builtin group");
let completer = args["-complete"]; let completer = args["-complete"];
let completerFunc = null; // default to no completion for user commands let completerFunc = null; // default to no completion for user commands
@@ -1333,7 +1365,7 @@ var Commands = Module("commands", {
}; };
} }
else 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(","), let added = args["-group"].add(cmd.split(","),
@@ -1364,8 +1396,9 @@ var Commands = Module("commands", {
}, { }, {
bang: true, bang: true,
completer: function (context, args) { completer: function (context, args) {
const { completion } = modules;
if (args.completeArg == 0) if (args.completeArg == 0)
completion.userCommand(context); completion.userCommand(context, args["-group"]);
else else
args["-javascript"] ? completion.javascript(context) : completion.ex(context); args["-javascript"] ? completion.javascript(context) : completion.ex(context);
}, },
@@ -1442,17 +1475,20 @@ var Commands = Module("commands", {
commands.add(["comc[lear]"], commands.add(["comc[lear]"],
"Delete all user-defined commands", "Delete all user-defined commands",
function () { function (args) {
commands.getUserCommands().forEach(function (cmd) { commands.removeUserCommand(cmd.name); }); args["-group"].clear();
}, },
{ argCount: "0" }); {
argCount: "0",
options: [contexts.GroupFlag("commands")]
});
commands.add(["comp[letions]"], commands.add(["comp[letions]"],
"List the completion results for a given command substring", "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", argCount: "1",
completer: function (context, args) completion.ex(context), completer: function (context, args) modules.completion.ex(context),
literal: 0 literal: 0
}); });
@@ -1461,13 +1497,14 @@ var Commands = Module("commands", {
function (args) { function (args) {
let name = args[0]; let name = args[0];
if (commands.get(name)) if (args["-group"].get(name))
commands.removeUserCommand(name); args["-group"].remove(name);
else else
dactyl.echoerr("E184: No such user-defined command: " + name); dactyl.echoerr("E184: No such user-defined command: " + name);
}, { }, {
argCount: "1", argCount: "1",
completer: function (context) completion.userCommand(context) completer: function (context, args) modules.completion.userCommand(context, args["-group"]),
options: [contexts.GroupFlag("commands")]
}); });
dactyl.addUsageCommand({ dactyl.addUsageCommand({
@@ -1491,22 +1528,29 @@ var Commands = Module("commands", {
"Yank the output of the given command to the clipboard", "Yank the output of the given command to the clipboard",
function (args) { function (args) {
let cmd = /^:/.test(args[0]) ? args[0] : ":echo " + args[0]; 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); dactyl.clipboardWrite(res);
let lines = res.split("\n").length; let lines = res.split("\n").length;
dactyl.echomsg("Yanked " + lines + " line" + (lines == 1 ? "" : "s")); 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 literal: 0
}); });
}, },
javascript: function () { javascript: function (dactyl, modules, window) {
const { JavaScript, commands } = modules;
JavaScript.setCompleter([commands.user.get, commands.user.remove], 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: function (dactyl, modules, window) {
mappings.add(config.browserModes, const { commands, mappings, modes } = modules;
mappings.add([modes.COMMAND],
["@:"], "Repeat the last Ex command", ["@:"], "Repeat the last Ex command",
function (args) { function (args) {
if (commands.repeat) { 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:

View File

@@ -42,9 +42,11 @@ var CompletionContext = Class("CompletionContext", {
let parent = editor; let parent = editor;
name = parent.name + "/" + name; name = parent.name + "/" + name;
if (this.options) {
this.autoComplete = this.options.get("autocomplete").getKey(name); this.autoComplete = this.options.get("autocomplete").getKey(name);
this.sortResults = this.options.get("wildsort").getKey(name); this.sortResults = this.options.get("wildsort").getKey(name);
this.wildcase = this.options.get("wildcase").getKey(name); this.wildcase = this.options.get("wildcase").getKey(name);
}
this.contexts = parent.contexts; this.contexts = parent.contexts;
if (name in this.contexts) if (name in this.contexts)
@@ -448,7 +450,7 @@ var CompletionContext = Class("CompletionContext", {
let self = this; let self = this;
delete this._substrings; delete this._substrings;
if (!this.forceAnchored) if (!this.forceAnchored && this.options)
this.anchored = this.options.get("wildanchor").getKey(this.name, this.anchored); this.anchored = this.options.get("wildanchor").getKey(this.name, this.anchored);
// Item matchers // Item matchers

View File

@@ -748,16 +748,17 @@ unlet s:cpo_save
return lines.map(function (l) l.join("")).join("\n").replace(/\s+\n/gm, "\n"); return lines.map(function (l) l.join("")).join("\n").replace(/\s+\n/gm, "\n");
} }
const { commands, options } = modules;
file.write(template({ file.write(template({
name: config.name, name: config.name,
autocommands: wrap("syn keyword " + config.name + "AutoEvent ", autocommands: wrap("syn keyword " + config.name + "AutoEvent ",
keys(config.autocommands)), keys(config.autocommands)),
commands: wrap("syn keyword " + config.name + "Command ", 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 ", 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 = [", 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), .flatten().map(String.quote),
", ") + "]" ", ") + "]"
})); }));

View File

@@ -6,6 +6,16 @@
// given in the LICENSE.txt file included with this file. // given in the LICENSE.txt file included with this file.
"use strict"; "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 */ /** @scope modules */
let ValueError = Class("ValueError", ErrorBase); let ValueError = Class("ValueError", ErrorBase);
@@ -83,7 +93,7 @@ var Option = Class("Option", {
get helpTag() "'" + this.name + "'", get helpTag() "'" + this.name + "'",
initValue: function () { initValue: function () {
dactyl.trapErrors(function () this.value = this.value, this); util.trapErrors(function () this.value = this.value, this);
}, },
get isDefault() this.stringValue === this.stringDefaultValue, get isDefault() this.stringValue === this.stringDefaultValue,
@@ -127,13 +137,15 @@ var Option = Class("Option", {
let values; let values;
if (dactyl.has("tabs") && (scope & Option.SCOPE_LOCAL)) /*
if (config.has("tabs") && (scope & Option.SCOPE_LOCAL))
values = tabs.options[this.name]; values = tabs.options[this.name];
*/
if ((scope & Option.SCOPE_GLOBAL) && (values == undefined)) if ((scope & Option.SCOPE_GLOBAL) && (values == undefined))
values = this.globalValue; values = this.globalValue;
if (this.getter) if (this.getter)
return dactyl.trapErrors(this.getter, this, values); return util.trapErrors(this.getter, this, values);
return values; return values;
}, },
@@ -151,19 +163,21 @@ var Option = Class("Option", {
return; return;
if (this.setter) if (this.setter)
newValues = dactyl.trapErrors(this.setter, this, newValues); newValues = this.modules.dactyl.trapErrors(this.setter, this, newValues);
if (newValues === undefined) if (newValues === undefined)
return; return;
if (dactyl.has("tabs") && (scope & Option.SCOPE_LOCAL)) /*
if (config.has("tabs") && (scope & Option.SCOPE_LOCAL))
tabs.options[this.name] = newValues; tabs.options[this.name] = newValues;
*/
if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal) if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal)
this.globalValue = newValues; this.globalValue = newValues;
this.hasChanged = true; this.hasChanged = true;
this.setFrom = null; this.setFrom = null;
dactyl.triggerObserver("options." + this.name, newValues); // dactyl.triggerObserver("options." + this.name, newValues);
}, },
getValues: deprecated("Option#get", "get"), getValues: deprecated("Option#get", "get"),
@@ -239,7 +253,7 @@ var Option = Class("Option", {
} }
catch (e) { catch (e) {
if (!(e instanceof ValueError)) if (!(e instanceof ValueError))
dactyl.reportError(e); util.reportError(e);
return this.invalidArgument(str || this.stringify(values), operator) + ": " + e.message; return this.invalidArgument(str || this.stringify(values), operator) + ": " + e.message;
} }
@@ -480,6 +494,7 @@ var Option = Class("Option", {
Option._splitAt = 0; Option._splitAt = 0;
return arg; return arg;
}, },
splitList: function (value, keepQuotes) { splitList: function (value, keepQuotes) {
let res = []; let res = [];
Option._splitAt = 0; Option._splitAt = 0;
@@ -495,6 +510,7 @@ var Option = Class("Option", {
} }
return res; return res;
}, },
quote: function quote(str, re) quote: function quote(str, re)
Commands.quoteArg[/[\s|"'\\,]|^$/.test(str) || re && re.test && re.test(str) Commands.quoteArg[/[\s|"'\\,]|^$/.test(str) || re && re.test && re.test(str)
? (/[\b\f\n\r\t]/.test(str) ? '"' : "'") ? (/[\b\f\n\r\t]/.test(str) ? '"' : "'")
@@ -514,7 +530,7 @@ var Option = Class("Option", {
values = values[(values.indexOf(String(this.value)) + 1) % values.length]; values = values[(values.indexOf(String(this.value)) + 1) % values.length];
let value = parseInt(values); let value = parseInt(values);
dactyl.assert(Number(values) % 1 == 0, util.assert(Number(values) % 1 == 0,
"E521: Number required after =: " + this.name + "=" + values); "E521: Number required after =: " + this.name + "=" + values);
switch (operator) { switch (operator) {
@@ -627,7 +643,7 @@ var Option = Class("Option", {
}, },
validateXPath: function (values) { validateXPath: function (values) {
let evaluator = XPathEvaluator(); let evaluator = services.XPathEvaluator();
return this.testValues(values, return this.testValues(values,
function (value) evaluator.createExpression(value, util.evaluateXPath.resolver)); function (value) evaluator.createExpression(value, util.evaluateXPath.resolver));
} }
@@ -637,102 +653,24 @@ var Option = Class("Option", {
* @instance options * @instance options
*/ */
var Options = Module("options", { var Options = Module("options", {
init: function () { Local: function (dactyl, modules, window) let ({ contexts } = modules) ({
init: function init() {
const self = this;
this.needInit = []; this.needInit = [];
this._options = []; this._options = [];
this._optionMap = {}; this._optionMap = {};
this.Option = Class("Option", Option, { modules: modules });
storage.newMap("options", { store: false }); storage.newMap("options", { store: false });
storage.addObserver("options", function optionObserver(key, event, option) { storage.addObserver("options", function optionObserver(key, event, option) {
// Trigger any setters. // Trigger any setters.
let opt = options.get(option); let opt = self.get(option);
if (event == "change" && opt) if (event == "change" && opt)
opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true); opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true);
}, window); }, window);
}, },
cleanup: function cleanup() { dactyl: dactyl,
for (let opt in this)
if (opt.cleanupValue != null)
opt.value = opt.parse(opt.cleanupValue);
},
/** @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)),
listPrefs: deprecated("prefs.list", function listPrefs() { commandline.commandOutput(prefs.list.apply(prefs, arguments)); }),
observePref: deprecated("prefs.observe", function observePref() prefs.observe.apply(prefs, arguments)),
popContext: deprecated("prefs.popContext", function popContext() prefs.popContext.apply(prefs, arguments)),
pushContext: deprecated("prefs.pushContext", function pushContext() prefs.pushContext.apply(prefs, arguments)),
resetPref: deprecated("prefs.reset", function resetPref() prefs.reset.apply(prefs, arguments)),
safeResetPref: deprecated("prefs.safeReset", function safeResetPref() prefs.safeReset.apply(prefs, arguments)),
safeSetPref: deprecated("prefs.safeSet", function safeSetPref() prefs.safeSet.apply(prefs, arguments)),
setPref: deprecated("prefs.set", function setPref() prefs.set.apply(prefs, arguments)),
withContext: deprecated("prefs.withContext", function withContext() prefs.withContext.apply(prefs, arguments)),
/**
* Returns the option with *name* in the specified *scope*.
*
* @param {string} name The option's name.
* @param {number} scope The option's scope (see {@link Option#scope}).
* @optional
* @returns {Option} The matching option.
*/
get: function (name, scope) {
if (!scope)
scope = Option.SCOPE_BOTH;
if (this._optionMap[name] && (this._optionMap[name].scope & scope))
return this._optionMap[name];
return null;
},
/** /**
* Lists all options in *scope* or only those with changed values if * Lists all options in *scope* or only those with changed values if
@@ -774,7 +712,93 @@ var Options = Module("options", {
} }
}; };
commandline.commandOutput(template.options("Options", opts(), options["verbose"] > 0)); 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))),
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)),
listPrefs: deprecated("prefs.list", function listPrefs() { commandline.commandOutput(prefs.list.apply(prefs, arguments)); }),
observePref: deprecated("prefs.observe", function observePref() prefs.observe.apply(prefs, arguments)),
popContext: deprecated("prefs.popContext", function popContext() prefs.popContext.apply(prefs, arguments)),
pushContext: deprecated("prefs.pushContext", function pushContext() prefs.pushContext.apply(prefs, arguments)),
resetPref: deprecated("prefs.reset", function resetPref() prefs.reset.apply(prefs, arguments)),
safeResetPref: deprecated("prefs.safeReset", function safeResetPref() prefs.safeReset.apply(prefs, arguments)),
safeSetPref: deprecated("prefs.safeSet", function safeSetPref() prefs.safeSet.apply(prefs, arguments)),
setPref: deprecated("prefs.set", function setPref() prefs.set.apply(prefs, arguments)),
withContext: deprecated("prefs.withContext", function withContext() prefs.withContext.apply(prefs, arguments)),
/**
* Returns the option with *name* in the specified *scope*.
*
* @param {string} name The option's name.
* @param {number} scope The option's scope (see {@link Option#scope}).
* @optional
* @returns {Option} The matching option.
*/
get: function (name, scope) {
if (!scope)
scope = Option.SCOPE_BOTH;
if (this._optionMap[name] && (this._optionMap[name].scope & scope))
return this._optionMap[name];
return null;
}, },
/** /**
@@ -801,7 +825,7 @@ var Options = Module("options", {
} }
if (matches) { 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))) { if (!ret.option && (ret.option = options.get(prefix + ret.name, ret.scope))) {
ret.name = prefix + ret.name; ret.name = prefix + ret.name;
prefix = ""; prefix = "";
@@ -849,7 +873,9 @@ var Options = Module("options", {
get store() storage.options get store() storage.options
}, { }, {
}, { }, {
commands: function () { commands: function initCommands(dactyl, modules, window) {
const { commands, contexts, options } = modules;
let args = { let args = {
getMode: function (args) findMode(args["-mode"]), getMode: function (args) findMode(args["-mode"]),
iterate: function (args) { iterate: function (args) {
@@ -916,7 +942,7 @@ var Options = Module("options", {
} }
if (name == "all" && reset) 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) { function (resp) {
if (resp == "yes") if (resp == "yes")
for (let pref in values(prefs.getNames())) for (let pref in values(prefs.getNames()))
@@ -946,22 +972,21 @@ var Options = Module("options", {
prefs.set(name, value); prefs.set(name, value);
} }
else else
commandline.commandOutput(prefs.list(onlyNonDefault, name)); modules.commandline.commandOutput(prefs.list(onlyNonDefault, name));
return; return;
} }
let opt = options.parseOpt(arg, modifiers); let opt = modules.options.parseOpt(arg, modifiers);
dactyl.assert(opt, "Error parsing :set command: " + arg); util.assert(opt, "Error parsing :set command: " + arg);
let option = opt.option; let option = opt.option;
dactyl.assert(option != null || opt.all, util.assert(option != null || opt.all, "E518: Unknown option: " + opt.name);
"E518: Unknown option: " + opt.name);
// reset a variable to its default value // reset a variable to its default value
if (opt.reset) { if (opt.reset) {
flushList(); flushList();
if (opt.all) { if (opt.all) {
for (let option in options) for (let option in modules.options)
option.reset(); option.reset();
} }
else { else {
@@ -975,7 +1000,7 @@ var Options = Module("options", {
else { else {
flushList(); flushList();
if (opt.option.type === "boolean") { 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; opt.values = !opt.unsetBoolean;
} }
else if (/^(string|number)$/.test(opt.option.type) && opt.invert) else if (/^(string|number)$/.test(opt.option.type) && opt.invert)
@@ -989,7 +1014,7 @@ var Options = Module("options", {
} }
if (res) if (res)
dactyl.echoerr(res); dactyl.echoerr(res);
option.setFrom = Commands.getCaller(null); option.setFrom = contexts.getCaller(null);
} }
} }
flushList(); flushList();
@@ -1014,7 +1039,7 @@ var Options = Module("options", {
return completion.preference(context); return completion.preference(context);
} }
let opt = options.parseOpt(filter, modifiers); let opt = modules.options.parseOpt(filter, modifiers);
let prefix = opt.prefix; let prefix = opt.prefix;
context.highlight(); context.highlight();
@@ -1055,7 +1080,7 @@ var Options = Module("options", {
} }
let optcontext = context.fork("values"); 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 // Fill in the current values if we're removing
if (opt.operator == "-" && isArray(opt.values)) { if (opt.operator == "-" && isArray(opt.values)) {
@@ -1065,7 +1090,7 @@ var Options = Module("options", {
context.maxItems = optcontext.maxItems; context.maxItems = optcontext.maxItems;
context.filters.push(function (i) !set.has(have, i.text)); 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) { function (context) {
context.generate = function () option.value.map(function (o) [o, ""]); context.generate = function () option.value.map(function (o) [o, ""]);
}); });
@@ -1108,9 +1133,9 @@ var Options = Module("options", {
let [, scope, name, op, expr] = matches; let [, scope, name, op, expr] = matches;
let fullName = (scope || "") + name; let fullName = (scope || "") + name;
dactyl.assert(scope == "g:" || scope == null, util.assert(scope == "g:" || scope == null,
"E461: Illegal variable name: " + scope + name); "E461: Illegal variable name: " + scope + name);
dactyl.assert(set.has(globalVariables, name) || (expr && !op), util.assert(set.has(globalVariables, name) || (expr && !op),
"E121: Undefined variable: " + fullName); "E121: Undefined variable: " + fullName);
if (!expr) if (!expr)
@@ -1120,7 +1145,7 @@ var Options = Module("options", {
var newValue = dactyl.userEval(expr); var newValue = dactyl.userEval(expr);
} }
catch (e) {} catch (e) {}
dactyl.assert(newValue !== undefined, util.assert(newValue !== undefined,
"E15: Invalid expression: " + expr); "E15: Invalid expression: " + expr);
let value = newValue; let value = newValue;
@@ -1167,7 +1192,7 @@ var Options = Module("options", {
literalArg: [opt.type == "boolean" ? (opt.value ? "" : "no") + opt.name literalArg: [opt.type == "boolean" ? (opt.value ? "" : "no") + opt.name
: opt.name + "=" + opt.stringValue] : opt.name + "=" + opt.stringValue]
} }
for (opt in options) for (opt in modules.options)
if (!opt.getter && !opt.isDefault && (opt.scope & Option.SCOPE_GLOBAL)) if (!opt.getter && !opt.isDefault && (opt.scope & Option.SCOPE_GLOBAL))
] ]
} }
@@ -1182,18 +1207,18 @@ var Options = Module("options", {
completer: setCompleter, completer: setCompleter,
domains: function (args) array.flatten(args.map(function (spec) { domains: function (args) array.flatten(args.map(function (spec) {
try { try {
let opt = options.parseOpt(spec); let opt = modules.options.parseOpt(spec);
if (opt.option && opt.option.domains) if (opt.option && opt.option.domains)
return opt.option.domains(opt.values); return opt.option.domains(opt.values);
} }
catch (e) { catch (e) {
dactyl.reportError(e); util.reportError(e);
} }
return []; return [];
})), })),
keepQuotes: true, keepQuotes: true,
privateData: function (args) args.some(function (spec) { privateData: function (args) args.some(function (spec) {
let opt = options.parseOpt(spec); let opt = modules.options.parseOpt(spec);
return opt.option && opt.option.privateData && return opt.option && opt.option.privateData &&
(!callable(opt.option.privateData) || (!callable(opt.option.privateData) ||
opt.option.privateData(opt.values)); opt.option.privateData(opt.values));
@@ -1223,12 +1248,14 @@ var Options = Module("options", {
deprecated: "the options system" deprecated: "the options system"
}); });
}, },
completion: function () { completion: function initCompletion(dactyl, modules, window) {
const { completion } = modules;
completion.option = function option(context, scope, prefix) { completion.option = function option(context, scope, prefix) {
context.title = ["Option"]; context.title = ["Option"];
context.keys = { text: "names", description: "description" }; context.keys = { text: "names", description: "description" };
context.anchored = false; context.anchored = false;
context.completions = options; context.completions = modules.options;
if (prefix == "inv") if (prefix == "inv")
context.keys.text = function (opt) context.keys.text = function (opt)
opt.type == "boolean" || isArray(opt.value) ? opt.names.map(function (n) "inv" + n) 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) { completion.optionValue = function (context, name, op, curValue, completer) {
let opt = options.get(name); let opt = modules.options.get(name);
completer = completer || opt.completer; completer = completer || opt.completer;
if (!completer || !opt) if (!completer || !opt)
return; return;
@@ -1301,15 +1328,18 @@ var Options = Module("options", {
context.completions = res; 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))]); 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", { sanitizer.addItem("options", {
description: "Options containing hostname data", description: "Options containing hostname data",
action: function (timespan, host) { action: function (timespan, host) {
if (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) if (timespan.contains(opt.lastSet * 1000) && opt.domains)
try { try {
opt.value = opt.filterDomain(host, opt.value); opt.value = opt.filterDomain(host, opt.value);
@@ -1319,12 +1349,12 @@ var Options = Module("options", {
} }
}, },
privateEnter: function () { 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))) if (opt.privateData && (!callable(opt.privateData) || opt.privateData(opt.value)))
opt.oldValue = opt.value; opt.oldValue = opt.value;
}, },
privateLeave: function () { privateLeave: function () {
for (let opt in values(options._options)) for (let opt in values(modules.options._options))
if (opt.oldValue != null) { if (opt.oldValue != null) {
opt.value = opt.oldValue; opt.value = opt.oldValue;
opt.oldValue = null; 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:

View File

@@ -154,6 +154,7 @@ var Overlay = Module("Overlay", {
defineModule.time("load", null, function _load() { defineModule.time("load", null, function _load() {
["addons", ["addons",
"base", "base",
"commands",
"completion", "completion",
"config", "config",
"downloads", "downloads",
@@ -161,6 +162,7 @@ var Overlay = Module("Overlay", {
"highlight", "highlight",
"io", "io",
"javascript", "javascript",
"options",
"overlay", "overlay",
"prefs", "prefs",
"services", "services",
@@ -177,14 +179,12 @@ var Overlay = Module("Overlay", {
"abbreviations", "abbreviations",
"autocommands", "autocommands",
"buffer", "buffer",
"commands",
"editor", "editor",
"events", "events",
"hints", "hints",
"mappings", "mappings",
"marks", "marks",
"mow", "mow",
"options",
"statusline" "statusline"
].forEach(function (name) defineModule.time("load", name, modules.load, modules, name)); ].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); } } 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: // vim: set fdm=marker sw=4 ts=4 et ft=javascript:

View File

@@ -84,6 +84,7 @@ var Services = Module("Services", {
this.addClass("Timer", "@mozilla.org/timer;1", Ci.nsITimer, "initWithCallback"); 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("StreamCopier", "@mozilla.org/network/async-stream-copier;1",Ci.nsIAsyncStreamCopier, "init");
this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", Ci.nsIXMLHttpRequest); 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("ZipReader", "@mozilla.org/libjar/zip-reader;1", Ci.nsIZipReader, "open");
this.addClass("ZipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter); this.addClass("ZipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter);
}, },

View File

@@ -623,6 +623,9 @@ var Styles = Module("Styles", {
this.hive.addRef(this); this.hive.addRef(this);
}, },
get names() this.hive.names,
get sheets() this.hive.sheets,
__noSuchMethod__: function __noSuchMethod__(meth, args) { __noSuchMethod__: function __noSuchMethod__(meth, args) {
return this.hive[meth].apply(this.hive, args); return this.hive[meth].apply(this.hive, args);
}, },

View File

@@ -13,7 +13,7 @@ Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("util", { defineModule("util", {
exports: ["frag", "FailedAssertion", "Math", "NS", "Point", "Util", "XBL", "XHTML", "XUL", "util"], exports: ["frag", "FailedAssertion", "Math", "NS", "Point", "Util", "XBL", "XHTML", "XUL", "util"],
require: ["services"], require: ["services"],
use: ["config", "highlight", "storage", "template"] use: ["commands", "config", "highlight", "storage", "template"]
}, this); }, this);
var XBL = Namespace("xbl", "http://www.mozilla.org/xbl"); 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"); var NS = Namespace("dactyl", "http://vimperator.org/namespaces/liberator");
default xml namespace = XHTML; 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 FailedAssertion = Class("FailedAssertion", ErrorBase);
var Point = Struct("x", "y"); var Point = Struct("x", "y");

View File

@@ -64,7 +64,7 @@ function Controller(controller) {
} }
this.errors = []; this.errors = [];
this._countError = function countError(message, highlight) { this._countError = function countError(message, highlight) {
if (/\bErrorMsg\b/.test(highlight)) if (/\b(Error|Warning)Msg\b/.test(highlight))
self.errors.push(String(message)); self.errors.push(String(message));
} }
this.modules.dactyl.registerObserver("beep", this._countBeep); this.modules.dactyl.registerObserver("beep", this._countBeep);

View File

@@ -122,7 +122,17 @@ var tests = {
singleOutput: ["", "foobar"], singleOutput: ["", "foobar"],
noOutput: ["foo bar", "-js bar baz"], noOutput: ["foo bar", "-js bar baz"],
multiOutput: [""], 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: { comclear: {
noOutput: [""] noOutput: [""]
@@ -140,6 +150,11 @@ var tests = {
delcommand: [ delcommand: [
{ {
init: ["comclear", "command foo bar"], init: ["comclear", "command foo bar"],
completions: [
["", hasItems],
["-group=", hasItems],
["-group=user ", hasItems]
],
noOutput: ["foo"] noOutput: ["foo"]
}, },
{ {
@@ -152,7 +167,6 @@ var tests = {
noOutput: ["x"], noOutput: ["x"],
completions: ["", "x"] completions: ["", "x"]
}, },
delmapgroup: {}, // Skip for now
get delmarks() this.delmacros, get delmarks() this.delmacros,
get delqmarks() this.delmacros, get delqmarks() this.delmacros,
delstyle: { delstyle: {
@@ -233,6 +247,25 @@ var tests = {
finish: { noOutput: [""] }, finish: { noOutput: [""] },
forward: { noOutput: [""] }, forward: { noOutput: [""] },
frameonly: { 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 hardcopy: {}, // Skip for now
help: { help: {
noOutput: ["", "intro"], noOutput: ["", "intro"],
@@ -318,7 +351,8 @@ var tests = {
], ],
error: [ error: [
"-mode=some-nonexistent-mode <C-a> <C-a>", "-mode=some-nonexistent-mode <C-a> <C-a>",
"-gtroup=some-nonexistent-group <C-a> <C-a>" "-group=some-nonexistent-group <C-a> <C-a>",
"-group=builtin <C-a> <C-a>"
], ],
completions: [ completions: [
["", hasItems], ["", hasItems],
@@ -333,25 +367,13 @@ var tests = {
}, },
mapclear: { mapclear: {
noOutput: [""], 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: [ error: [
"some-nonexistent-group", "-group=builtin"
"foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'"
], ],
completions: [ completions: [
"", "",
"foo " "-group="
], ]
cleanup: ["delmapgroup foo"]
}, },
mark: { mark: {
error: ["", "#", "xy"], error: ["", "#", "xy"],