1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-20 04:48:08 +01:00

Implement (with an e) an experimental ‘ex’ namespace.

This commit is contained in:
Kris Maglione
2010-10-17 20:00:57 -04:00
parent a2f6b13a10
commit e6b6d80b10
3 changed files with 62 additions and 29 deletions

View File

@@ -133,7 +133,7 @@ const Browser = Module("browser", {
mappings.add([modes.NORMAL], ["<C-l>"], mappings.add([modes.NORMAL], ["<C-l>"],
"Redraw the screen", "Redraw the screen",
function () { commands.get("redraw").action(); }); function () { ex.redraw(); });
}, },
commands: function () { commands: function () {

View File

@@ -35,13 +35,16 @@
* @property {boolean} multiple Whether this option can be specified multiple times * @property {boolean} multiple Whether this option can be specified multiple times
* @property {string} description A description of the option * @property {string} description A description of the option
*/ */
const CommandOption = Struct("names", "type", "validator", "completer", "multiple", "description"); const CommandOption = Struct("names", "type", "validator", "completer", "multiple", "description");
CommandOption.defaultValue("description", function () ""); CommandOption.defaultValue("description", function () "");
CommandOption.defaultValue("type", function () CommandOption.NOARG); CommandOption.defaultValue("type", function () CommandOption.NOARG);
CommandOption.defaultValue("multiple", function () false); CommandOption.defaultValue("multiple", function () false);
const ArgType = Struct("description", "parse");
update(CommandOption, { update(CommandOption, {
/** /**
* @property {number} The option argument is unspecified. Any argument * @property {object} The option argument is unspecified. Any argument
* is accepted and caller is responsible for parsing the return * is accepted and caller is responsible for parsing the return
* value. * value.
* @final * @final
@@ -49,36 +52,36 @@ update(CommandOption, {
ANY: 0, ANY: 0,
/** /**
* @property {number} The option doesn't accept an argument. * @property {object} The option doesn't accept an argument.
* @final * @final
*/ */
NOARG: 1, NOARG: ArgType("no arg", function (arg) !arg || null),
/** /**
* @property {number} The option accepts a boolean argument. * @property {object} The option accepts a boolean argument.
* @final * @final
*/ */
BOOL: 2, BOOL: ArgType("boolean", function (val) Commands.parseBool(val)),
/** /**
* @property {number} The option accepts a string argument. * @property {object} The option accepts a string argument.
* @final * @final
*/ */
STRING: 3, STRING: ArgType("string", function (val) val),
/** /**
* @property {number} The option accepts an integer argument. * @property {object} The option accepts an integer argument.
* @final * @final
*/ */
INT: 4, INT: ArgType("int", parseInt),
/** /**
* @property {number} The option accepts a float argument. * @property {object} The option accepts a float argument.
* @final * @final
*/ */
FLOAT: 5, FLOAT: ArgType("float", parseFloat),
/** /**
* @property {number} The option accepts a string list argument. * @property {object} The option accepts a string list argument.
* E.g. "foo,bar" * E.g. "foo,bar"
* @final * @final
*/ */
LIST: 6 LIST: ArgType("list", function (arg) arg && arg.split(/\s*,\s*/))
}); });
/** /**
@@ -252,6 +255,9 @@ const Command = Class("Command", {
* @see Commands@parseArguments * @see Commands@parseArguments
*/ */
options: [], options: [],
optionMap: Class.memoize(function () array(this.options)
.map(function (opt) opt.names.map(function (name) [name, opt]))
.flatten.toObject()),
/** /**
* @property {boolean|function(args)} When true, invocations of this * @property {boolean|function(args)} When true, invocations of this
* command may contain private data which should be purged from * command may contain private data which should be purged from
@@ -343,10 +349,47 @@ const Command = Class("Command", {
} }
}); });
// Prototype.
const ex = {
__noSuchMethod__: function (meth, args) {
let cmd = commands.get(meth);
dactyl.assert(cmd, "No such command");
if (isObject(args[0]))
for (let [k, v] in Iterator(args.shift()))
if (k == "!")
args.bang = v;
else if (k == "#")
args.count = v;
else {
let opt = cmd.optionMap["-" + k];
let val = opt.type && opt.type.parse(v);
dactyl.assert(val != null && (typeof val !== "number" || !isNaN(val)),
"No such option: " + k);
args[opt.names[0]] = val;
}
if (cmd.literal != null)
args.literalArg = args[cmd.literal];
// FIXME: Duplicated in parseArgs.
dactyl.assert((args.length > 0 || !/^[1+]$/.test(this.argCount)) &&
(this.literal == null || !/[1+]/.test(this.argCount) || /\S/.test(args.literalArg || "")),
"E471: Argument required");
// This logic eludes me... --Kris
dactyl.assert((args.length == 0 || this.argCount !== "0") &&
(args.length <= 1 || !/^[01?]$/.test(this.argCount)),
"E488: Trailing characters");
// TODO: memoize(args, "string", function () { ... });
return cmd.execute(args);
}
};
/** /**
* @instance commands * @instance commands
*/ */
const ArgType = Struct("description", "parse");
const Commands = Module("commands", { const Commands = Module("commands", {
init: function () { init: function () {
this._exCommands = []; this._exCommands = [];
@@ -771,13 +814,12 @@ const Commands = Module("commands", {
args.quote = Commands.complQuote[quote] || Commands.complQuote[""]; args.quote = Commands.complQuote[quote] || Commands.complQuote[""];
} }
if (!complete || arg != null) { if (!complete || arg != null) {
let type = Commands.argTypes[opt.type]; if (opt.type) {
if (type) {
let orig = arg; let orig = arg;
arg = type.parse(arg); arg = opt.type.parse(arg);
if (arg == null || (typeof arg == "number" && isNaN(arg))) { if (arg == null || (typeof arg == "number" && isNaN(arg))) {
if (!complete || orig != "" || args.completeStart != str.length) if (!complete || orig != "" || args.completeStart != str.length)
fail("Invalid argument for " + type.description + " option: " + optname); fail("Invalid argument for " + opt.type.description + " option: " + optname);
if (complete) if (complete)
complete.highlight(args.completeStart, count - 1, "SPELLCHECK"); complete.highlight(args.completeStart, count - 1, "SPELLCHECK");
} }
@@ -1420,15 +1462,6 @@ const Commands = Module("commands", {
return false; return false;
return NaN; return NaN;
}; };
Commands.argTypes = [
null,
ArgType("no arg", function (arg) !arg || null),
ArgType("boolean", Commands.parseBool),
ArgType("string", function (val) val),
ArgType("int", parseInt),
ArgType("float", parseFloat),
ArgType("list", function (arg) arg && arg.split(/\s*,\s*/))
];
})(); })();
// vim: set fdm=marker sw=4 ts=4 et: // vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -956,7 +956,7 @@ const Tabs = Module("tabs", {
if (dactyl.has("tabs_undo")) { if (dactyl.has("tabs_undo")) {
mappings.add([modes.NORMAL], ["u"], mappings.add([modes.NORMAL], ["u"],
"Undo closing of a tab", "Undo closing of a tab",
function (count) { commands.get("undo").action({ count: count }); }, function (count) { ex.undo({ "#": count }); },
{ count: true }); { count: true });
} }