mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-20 15:57:57 +01:00
Add :command -javascript and CommandOption#default.
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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 bangOpt = "-bang" in args;
|
|
||||||
let countOpt = "-count" in args;
|
|
||||||
let descriptionOpt = args["-description"] || "User-defined command";
|
|
||||||
let completeOpt = args["-complete"];
|
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)
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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] + " })");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user