mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-21 17:17:58 +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:
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,11 +300,11 @@ 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)) &&
|
||||
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") &&
|
||||
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,7 +381,7 @@ 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)),
|
||||
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,31 +391,49 @@ 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, {
|
||||
/**
|
||||
* @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.supercall(this, group);
|
||||
this._map = {};
|
||||
@@ -425,9 +458,11 @@ var CommandHive = Class("CommandHive", Group.Hive, {
|
||||
* @optional
|
||||
*/
|
||||
add: function (names, description, action, extra, replace) {
|
||||
const { commands } = modules;
|
||||
|
||||
extra = extra || {};
|
||||
if (!extra.definedAt)
|
||||
extra.definedAt = Commands.getCaller(Components.stack.caller);
|
||||
extra.definedAt = contexts.getCaller(Components.stack.caller);
|
||||
|
||||
extra.hive = this;
|
||||
extra.parsedSpecs = Command.parseSpecs(names);
|
||||
@@ -435,10 +470,10 @@ var CommandHive = Class("CommandHive", Group.Hive, {
|
||||
let names = array.flatten(extra.parsedSpecs);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
for (let name in values(names)) {
|
||||
@@ -450,19 +485,31 @@ var CommandHive = Class("CommandHive", Group.Hive, {
|
||||
let self = this;
|
||||
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);
|
||||
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);
|
||||
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*.
|
||||
*
|
||||
@@ -472,7 +519,7 @@ var CommandHive = Class("CommandHive", Group.Hive, {
|
||||
* its names matches *name* exactly.
|
||||
* @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)
|
||||
|| null,
|
||||
|
||||
@@ -482,8 +529,8 @@ var CommandHive = Class("CommandHive", Group.Hive, {
|
||||
* @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,
|
||||
remove: function remove(name) {
|
||||
util.assert(this.group !== contexts.default,
|
||||
"Cannot delete non-user commands");
|
||||
|
||||
let cmd = this.get(name);
|
||||
@@ -491,23 +538,118 @@ var CommandHive = Class("CommandHive", Group.Hive, {
|
||||
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;
|
||||
},
|
||||
|
||||
hives: Group.Hives("commands", CommandHive),
|
||||
})),
|
||||
|
||||
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(
|
||||
<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
|
||||
* command invocation.
|
||||
@@ -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(
|
||||
<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
|
||||
* 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),
|
||||
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:
|
||||
@@ -42,9 +42,11 @@ var CompletionContext = Class("CompletionContext", {
|
||||
let parent = editor;
|
||||
name = parent.name + "/" + 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
|
||||
|
||||
@@ -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),
|
||||
", ") + "]"
|
||||
}));
|
||||
|
||||
@@ -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,7 +530,7 @@ var Option = Class("Option", {
|
||||
values = values[(values.indexOf(String(this.value)) + 1) % values.length];
|
||||
|
||||
let value = parseInt(values);
|
||||
dactyl.assert(Number(values) % 1 == 0,
|
||||
util.assert(Number(values) % 1 == 0,
|
||||
"E521: Number required after =: " + this.name + "=" + values);
|
||||
|
||||
switch (operator) {
|
||||
@@ -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,102 +653,24 @@ var Option = Class("Option", {
|
||||
* @instance options
|
||||
*/
|
||||
var Options = Module("options", {
|
||||
init: function () {
|
||||
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);
|
||||
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);
|
||||
},
|
||||
|
||||
/** @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;
|
||||
},
|
||||
dactyl: dactyl,
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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,9 +1133,9 @@ var Options = Module("options", {
|
||||
let [, scope, name, op, expr] = matches;
|
||||
let fullName = (scope || "") + name;
|
||||
|
||||
dactyl.assert(scope == "g:" || scope == null,
|
||||
util.assert(scope == "g:" || scope == null,
|
||||
"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);
|
||||
|
||||
if (!expr)
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <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: [
|
||||
["", 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"],
|
||||
|
||||
Reference in New Issue
Block a user