1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-20 16:17:59 +01:00

Add :command -javascript and CommandOption#default.

This commit is contained in:
Kris Maglione
2010-10-17 19:33:39 -04:00
parent 699b487ab2
commit a2f6b13a10
4 changed files with 105 additions and 75 deletions

View File

@@ -232,11 +232,8 @@ const Abbreviations = Module("abbreviations", {
dactyl.assert(lhs != null, "E474: Invalid argument"); dactyl.assert(lhs != null, "E474: Invalid argument");
if (rhs) { if (rhs) {
if (args["-javascript"]) { if (args["-javascript"])
let expr = rhs; rhs = Command.bindMacro({ literalArg: rhs }, "-javascript", ["editor"]);
rhs = dactyl.userFunc("editor", expr);
rhs.toString = function () expr;
}
abbreviations.add(modes, lhs, rhs); abbreviations.add(modes, lhs, rhs);
} }
else { else {
@@ -258,7 +255,8 @@ const Abbreviations = Module("abbreviations", {
return completion.javascript(context); return completion.javascript(context);
}, },
literal: 0, literal: 0,
serial: function () [ { serialize: function () [
{
command: this.name, command: this.name,
arguments: [abbr.lhs], arguments: [abbr.lhs],
literalArg: abbr.rhs, literalArg: abbr.rhs,

View File

@@ -25,6 +25,7 @@
* (@link CommandOption.FLOAT), * (@link CommandOption.FLOAT),
* (@link CommandOption.LIST), * (@link CommandOption.LIST),
* (@link CommandOption.ANY) * (@link CommandOption.ANY)
* @property {object} default The option's default value
* @property {function} validator A validator function * @property {function} validator A validator function
* @property {function (CompletionContext, object)} completer A list of * @property {function (CompletionContext, object)} completer A list of
* completions, or a completion function which will be passed a * completions, or a completion function which will be passed a
@@ -285,7 +286,44 @@ const Command = Class("Command", {
*/ */
replacementText: null replacementText: null
}, { }, {
bindMacro: function (args, default_) { bindMacro: function (args, default_, params) {
let makeParams = function makeParams()
let (args = arguments)
params.map(function (name, i) [name, args[i]]).toObject();
if (callable(params))
makeParams = params;
else
params = array(params);
let rhs = args.literalArg;
let type = ["-builtin", "-ex", "-javascript", "-keys"].reduce(function (a, b) args[b] ? b : a, default_);
switch (type) {
case "-builtin":
let noremap = true;
/* fallthrough */
case "-keys":
let silent = args["-silent"];
rhs = events.canonicalKeys(rhs);
var action = function action(count)
events.feedkeys(commands.replaceTokens(rhs, { count: count }),
noremap, silent);
break;
case "-ex":
action = function action() commands.execute(rhs, makeParams.apply(this, arguments),
false, null, action.sourcing);
action.sourcing = io.sourcing && update({}, io.sourcing);
break;
case "-javascript":
if (callable(params))
action = dactyl.userEval("(function action() { with (action.makeParams.apply(this, arguments)) {" + args.literalArg + "} })")
else
action = dactyl.userFunc.apply(dactyl, params.concat(args.literalArg).array);
action.makeParams = makeParams;
break;
}
action.toString = function toString() (type === default_ ? "" : type + " ") + rhs;
args = null;
return action;
}, },
// TODO: do we really need more than longNames as a convenience anyway? // TODO: do we really need more than longNames as a convenience anyway?
@@ -389,7 +427,6 @@ const Commands = Module("commands", {
addUserCommand: function (names, description, action, extra, replace) { addUserCommand: function (names, description, action, extra, replace) {
extra = extra || {}; extra = extra || {};
extra.user = true; extra.user = true;
description = description || "User defined command";
return this._addCommand([names, description, action, extra], replace); return this._addCommand([names, description, action, extra], replace);
}, },
@@ -404,7 +441,13 @@ const Commands = Module("commands", {
commandToString: function (args) { commandToString: function (args) {
let res = [args.command + (args.bang ? "!" : "")]; let res = [args.command + (args.bang ? "!" : "")];
let defaults = {};
if (args.ignoreDefaults)
defaults = array(this.options).map(function (opt) [opt.names[0], opt.default]).toObject();
for (let [opt, val] in Iterator(args.options || {})) { for (let [opt, val] in Iterator(args.options || {})) {
if (val != null && defaults[opt] === val)
continue;
let chr = /^-.$/.test(opt) ? " " : "="; let chr = /^-.$/.test(opt) ? " " : "=";
if (val != null) if (val != null)
opt += chr + Commands.quote(val); opt += chr + Commands.quote(val);
@@ -416,8 +459,8 @@ const Commands = Module("commands", {
let str = args.literalArg; let str = args.literalArg;
if (str) if (str)
res.push(!/\n/.test(str) ? str : res.push(!/\n/.test(str) ? str :
this.hereDoc ? "<<EOF\n" + str.replace(/\n$/, "") + "\nEOF" this.hereDoc ? "<<EOF\n" + String.replace(str, /\n$/, "") + "\nEOF"
: str.replace(/\n/, "\n" + res[0].replace(/./g, " ").replace(/.$/, "\\"))); : String.replace(str, /\n/, "\n" + res[0].replace(/./g, " ").replace(/.$/, "\\")));
return res.join(" "); return res.join(" ");
}, },
@@ -848,6 +891,10 @@ const Commands = Module("commands", {
args.length > 1 && /^[01?]$/.test(argCount)) args.length > 1 && /^[01?]$/.test(argCount))
fail("E488: Trailing characters"); fail("E488: Trailing characters");
for (let opt in values(options))
if (set.has(opt, "default") && args[opt.names[0]] === undefined)
args[opt.names[0]] = opt.default;
return args; return args;
}, },
@@ -970,8 +1017,10 @@ const Commands = Module("commands", {
if (token == "lt") // Don't quote, as in Vim (but, why so in Vim? You'd think people wouldn't say <q-lt> if they didn't want it) if (token == "lt") // Don't quote, as in Vim (but, why so in Vim? You'd think people wouldn't say <q-lt> if they didn't want it)
return "<"; return "<";
let res = tokens[token]; let res = tokens[token];
if (res == undefined) // Ignore anything undefined if (res === undefined) // Ignore anything undefined
res = "<" + token + ">"; res = "<" + token + ">";
if (res === null)
res = "";
if (quote && typeof res != "number") if (quote && typeof res != "number")
return Commands.quoteArg['"'](res); return Commands.quoteArg['"'](res);
return res; return res;
@@ -1074,16 +1123,6 @@ const Commands = Module("commands", {
}, },
commands: function () { commands: function () {
function userCommand(args, modifiers) {
let tokens = {
args: this.argCount && args.string,
bang: this.bang && args.bang ? "!" : "",
count: this.count && args.count
};
commands.execute(this.replacementText, tokens, false, null, this.sourcing);
}
// TODO: offer completion.ex? // TODO: offer completion.ex?
// : make this config specific // : make this config specific
var completeOptionMap = { var completeOptionMap = {
@@ -1108,12 +1147,7 @@ const Commands = Module("commands", {
dactyl.assert(!/\W/.test(cmd || ''), "E182: Invalid command name"); dactyl.assert(!/\W/.test(cmd || ''), "E182: Invalid command name");
if (args.literalArg) { if (args.literalArg) {
let nargsOpt = args["-nargs"] || "0"; let completeOpt = args["-complete"];
let bangOpt = "-bang" in args;
let countOpt = "-count" in args;
let descriptionOpt = args["-description"] || "User-defined command";
let completeOpt = args["-complete"];
let completeFunc = null; // default to no completion for user commands let completeFunc = null; // default to no completion for user commands
if (completeOpt) { if (completeOpt) {
@@ -1140,12 +1174,19 @@ const Commands = Module("commands", {
} }
let added = commands.addUserCommand([cmd], let added = commands.addUserCommand([cmd],
descriptionOpt, args["-description"],
userCommand, { Command.bindMacro(args, "-ex",
argCount: nargsOpt, function makeParams(args, modifiers) ({
bang: bangOpt, args: this.argCount && args.string,
count: countOpt, bang: this.bang && args.bang ? "!" : "",
count: this.count && args.count
})),
{
argCount: args["-nargs"],
bang: args["-bang"],
count: args["-count"],
completer: completeFunc, completer: completeFunc,
persist: !args["-nopersist"],
replacementText: args.literalArg, replacementText: args.literalArg,
sourcing: io.sourcing && update({}, io.sourcing) sourcing: io.sourcing && update({}, io.sourcing)
}, args.bang); }, args.bang);
@@ -1190,16 +1231,20 @@ const Commands = Module("commands", {
{ names: ["-bang", "-b"], description: "Command may be proceeded by a !" }, { names: ["-bang", "-b"], description: "Command may be proceeded by a !" },
{ names: ["-count", "-c"], description: "Command may be preceeded by a count" }, { names: ["-count", "-c"], description: "Command may be preceeded by a count" },
{ {
names: ["-description", "-desc", "-d"],
description: "A user-visible description of the command",
type: CommandOption.STRING
}, {
// TODO: "E180: invalid complete value: " + arg // TODO: "E180: invalid complete value: " + arg
names: ["-complete", "-C"], names: ["-complete", "-C"],
description: "The argument completion function", description: "The argument completion function",
completer: function (context) [[k, ""] for ([k, v] in Iterator(completeOptionMap))], completer: function (context) [[k, ""] for ([k, v] in Iterator(completeOptionMap))],
type: CommandOption.STRING, type: CommandOption.STRING,
validator: function (arg) arg in completeOptionMap || /custom,\w+/.test(arg), validator: function (arg) arg in completeOptionMap || /custom,\w+/.test(arg),
}, {
names: ["-description", "-desc", "-d"],
description: "A user-visible description of the command",
default: "User-defined command",
type: CommandOption.STRING
}, {
names: ["-javascript", "-js", "-j"],
description: "Execute this mapping as JavaScript rather than keys"
}, { }, {
names: ["-nargs", "-a"], names: ["-nargs", "-a"],
description: "The allowed number of arguments", description: "The allowed number of arguments",
@@ -1208,9 +1253,13 @@ const Commands = Module("commands", {
["*", "Zero or more arguments are allowed"], ["*", "Zero or more arguments are allowed"],
["?", "Zero or one argument is allowed"], ["?", "Zero or one argument is allowed"],
["+", "One or more arguments are allowed"]], ["+", "One or more arguments are allowed"]],
default: "0",
type: CommandOption.STRING, type: CommandOption.STRING,
validator: function (arg) /^[01*?+]$/.test(arg) validator: function (arg) /^[01*?+]$/.test(arg)
}, }, {
names: ["-nopersist", "-n"],
description: "Do not save this command to an auto-generated RC file"
}
], ],
literal: 1, literal: 1,
serialize: function () [ { serialize: function () [ {
@@ -1220,13 +1269,13 @@ const Commands = Module("commands", {
[[v, typeof cmd[k] == "boolean" ? null : cmd[k]] [[v, typeof cmd[k] == "boolean" ? null : cmd[k]]
// FIXME: this map is expressed multiple times // FIXME: this map is expressed multiple times
for ([k, v] in Iterator({ argCount: "-nargs", bang: "-bang", count: "-count", description: "-description" })) for ([k, v] in Iterator({ argCount: "-nargs", bang: "-bang", count: "-count", description: "-description" }))
// FIXME: add support for default values to parseArgs if (cmd[k])]),
if (k in cmd && cmd[k] != "0" && cmd[k] != "User-defined command")]),
arguments: [cmd.name], arguments: [cmd.name],
literalArg: cmd.replacementText literalArg: cmd.action,
ignoreDefaults: true
} }
for ([k, cmd] in Iterator(commands._exCommands)) for ([k, cmd] in Iterator(commands._exCommands))
if (cmd.user && cmd.replacementText) if (cmd.user && cmd.persist)
] ]
}); });

View File

@@ -307,9 +307,8 @@ const Dactyl = Module("dactyl", {
*/ */
userFunc: function () { userFunc: function () {
return this.userEval( return this.userEval(
"(function userFunction(" + "(function userFunction(" + Array.slice(arguments, 0, -1).join(", ") + ")" +
Array.slice(arguments, 0, -1).join(", ") + " { " + arguments[arguments.length - 1] + " })");
") { " + arguments[arguments.length - 1] + " })");
}, },
/** /**

View File

@@ -181,7 +181,7 @@ const Mappings = Module("mappings", {
modes = modes.slice(); modes = modes.slice();
return (map for ([i, map] in Iterator(stack[modes.shift()].sort(function (m1, m2) String.localeCompare(m1.name, m2.name)))) return (map for ([i, map] in Iterator(stack[modes.shift()].sort(function (m1, m2) String.localeCompare(m1.name, m2.name))))
if (map.rhs && modes.every(function (mode) stack[mode]. if (map.rhs && modes.every(function (mode) stack[mode].
some(function (m) m.rhs && array.equals(m.rhs, map.rhs) && m.name == map.name)))) some(function (m) m.rhs && m.rhs === map.rhs && m.name === map.name))))
}, },
// NOTE: just normal mode for now // NOTE: just normal mode for now
@@ -347,7 +347,7 @@ const Mappings = Module("mappings", {
<tr> <tr>
<td>{modeSign} {name}</td> <td>{modeSign} {name}</td>
<td>{map.noremap ? "*" : " "}</td> <td>{map.noremap ? "*" : " "}</td>
<td>{map.rhs ? map.rhs.join(" ") : "function () { ... }"}</td> <td>{map.rhs || map.action.toSource()}</td>
</tr>)) </tr>))
} }
</table>; </table>;
@@ -361,7 +361,6 @@ const Mappings = Module("mappings", {
}, { }, {
}, { }, {
commands: function () { commands: function () {
const stockDescription = "User defined mapping";
function addMapCommands(ch, mapmodes, modeDescription) { function addMapCommands(ch, mapmodes, modeDescription) {
// 0 args -> list all maps // 0 args -> list all maps
// 1 arg -> list the maps starting with args // 1 arg -> list the maps starting with args
@@ -374,35 +373,20 @@ const Mappings = Module("mappings", {
} }
let [lhs, rhs] = args; let [lhs, rhs] = args;
if (noremap)
args["-builtin"] = true;
if (!rhs) // list the mapping if (!rhs) // list the mapping
mappings.list(mapmodes, mappings._expandLeader(lhs)); mappings.list(mapmodes, mappings._expandLeader(lhs));
else { else {
if (args["-javascript"]) {
rhs = ["-javascript", rhs];
var action = dactyl.userFunc("count", rhs);
}
else if (args["-ex"]) {
rhs = ["-ex", rhs];
action = function action(count) commands.execute(rhs[1], { count: count },
false, null, action.sourcing);
action.sourcing = io.sourcing && update({}, io.sourcing);
}
else {
rhs = [events.canonicalKeys(rhs)];
action = function (count) {
events.feedkeys(commands.replaceTokens(rhs[0], { count: count }),
this.noremap, this.silent);
};
}
mappings.addUserMap(mapmodes, [lhs], mappings.addUserMap(mapmodes, [lhs],
args["-description"] || stockDescription, args["-description"],
action, { Command.bindMacro(args, "-keys", ["count"]),
{
count: args["-count"], count: args["-count"],
rhs: rhs, noremap: "-builtin" in args,
noremap: "-builtin" in args || noremap,
persist: !args["-nopersist"], persist: !args["-nopersist"],
get rhs() String(this.action),
silent: "-silent" in args silent: "-silent" in args
}); });
} }
@@ -451,8 +435,9 @@ const Mappings = Module("mappings", {
}, },
{ {
names: ["-description", "-d"], names: ["-description", "-d"],
type: CommandOption.STRING, description: "A description of this mapping",
description: "A description of this mapping" default: "User defined mapping",
type: CommandOption.STRING
}, },
{ {
names: ["-ex", "-e"], names: ["-ex", "-e"],
@@ -485,14 +470,13 @@ const Mappings = Module("mappings", {
command: this.name, command: this.name,
options: array([ options: array([
["-modes", uniqueModes(map.modes)], ["-modes", uniqueModes(map.modes)],
map.noremap && ["-builtin"], ["-description", map.description],
map.description != stockDescription && ["-description", map.description],
map.rhs.length > 1 && [map.rhs[0]],
map.silent && ["-silent"]]) map.silent && ["-silent"]])
.filter(util.identity) .filter(util.identity)
.toObject(), .toObject(),
arguments: [map.names[0]], arguments: [map.names[0]],
literalArg: map.rhs[map.rhs.length - 1] literalArg: map.rhs,
ignoreDefaults: true
} }
for (map in userMappings()) for (map in userMappings())
if (map.persist) if (map.persist)