diff --git a/common/content/commands.js b/common/content/commands.js
index cf704727..b9623754 100644
--- a/common/content/commands.js
+++ b/common/content/commands.js
@@ -696,7 +696,7 @@ const Commands = Module("commands", {
if (opt.multiple)
args[opt.names[0]] = (args[opt.names[0]] || []).concat(arg);
else
- args[opt.names[0]] = opt.type == this.OPTION_NOARG || arg;
+ args[opt.names[0]] = opt.type == CommandOption.NOARG || arg;
i += optname.length + count;
if (i == str.length)
@@ -919,7 +919,7 @@ const Commands = Module("commands", {
return;
}
- [prefix] = context.filter.match(/^(?:\w*[\s!]|!)\s*/);
+ [prefix] = context.filter.match(/^(?:\w*[\s!])?\s*/);
let cmdContext = context.fork(command.name, prefix.length);
let argContext = context.fork("args", prefix.length);
args = command.parseArgs(cmdContext.filter, argContext, { count: count, bang: bang });
diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index 892026db..62c44361 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -326,7 +326,7 @@ const Dactyl = Module("dactyl", {
* userContext global.
*/
userfunc: function () {
- return this.userEval(
+ return this.usereval(
"(function (" +
Array.slice(arguments, 0, -1).join(", ") +
") { " + arguments[arguments.length - 1] + " })")
diff --git a/common/content/mappings.js b/common/content/mappings.js
index 74489a17..4dcc3e42 100644
--- a/common/content/mappings.js
+++ b/common/content/mappings.js
@@ -33,6 +33,7 @@ const Map = Class("Map", {
init: function (modes, keys, description, action, extraInfo) {
modes = Array.concat(modes).map(function (m) isobject(m) ? m.mask : m);
+ this.id = ++Map.id;
this.modes = modes;
this.names = keys.map(events.canonicalKeys);
this.name = this.names[0];
@@ -117,6 +118,8 @@ const Map = Class("Map", {
return dactyl.trapErrors(repeat);
}
+}, {
+ id: 0
});
/**
@@ -177,8 +180,8 @@ const Mappings = Module("mappings", {
_mappingsIterator: function (modes, stack) {
modes = modes.slice();
return (map for ([i, map] in Iterator(stack[modes.shift()].sort(function (m1, m2) String.localeCompare(m1.name, m2.name))))
- if (modes.every(function (mode) stack[mode].some(
- function (m) m.rhs == map.rhs && m.name == map.name))))
+ if (modes.every(function (mode) stack[mode].
+ some(function (m) array.equals(m.rhs, map.rhs) && m.name == map.name))))
},
// NOTE: just normal mode for now
@@ -356,7 +359,7 @@ const Mappings = Module("mappings", {
| {modeSign} {name} |
{map.noremap ? "*" : " "} |
- {map.rhs || "function () { ... }"} |
+ {map.rhs ? map.rhs.join(" ") : "function () { ... }"} |
))
}
;
@@ -370,32 +373,49 @@ const Mappings = Module("mappings", {
}, {
}, {
commands: function () {
- function addMapCommands(ch, modes, modeDescription) {
+ const stockDescription = "User defined mapping";
+ function addMapCommands(ch, mapmodes, modeDescription) {
// 0 args -> list all maps
// 1 arg -> list the maps starting with args
// 2 args -> map arg1 to arg*
- function map(args, modes, noremap) {
+ function map(args, mapmodes, noremap) {
+ mapmodes = getModes(args, mapmodes);
if (!args.length) {
- mappings.list(modes);
+ mappings.list(mapmodes);
return;
}
let [lhs, rhs] = args;
if (!rhs) // list the mapping
- mappings.list(modes, mappings._expandLeader(lhs));
+ mappings.list(mapmodes, mappings._expandLeader(lhs));
else {
- // this matches Vim's behaviour
- if (/^$/i.test(rhs))
- noremap = true;
+ if (args["-javascript"]) {
+ rhs = ["-javascript", rhs];
+ var action = dactyl.userfunc("count", rhs);
+ }
+ else if (args["-ex"]) {
+ rhs = ["-ex", rhs];
+ action = function (count) {
+ dactyl.execute(commands.replaceTokens(rhs[1], { count: count }));
+ };
+ }
+ else {
+ rhs = [events.canonicalKeys(rhs)];
+ action = function (count) {
+ events.feedkeys(commands.replaceTokens(rhs[0], { count: count }),
+ this.noremap, this.silent);
+ };
+ }
- mappings.addUserMap(modes, [lhs],
- "User defined mapping",
- function (count) { events.feedkeys((count || "") + this.rhs, this.noremap, this.silent); }, {
- count: true,
- rhs: events.canonicalKeys(rhs),
- noremap: !!noremap,
- silent: "" in args
+ mappings.addUserMap(mapmodes, [lhs],
+ args["-description"] || stockDescription,
+ action, {
+ count: args["-count"],
+ rhs: rhs,
+ noremap: "-builtin" in args || noremap,
+ persist: !args["-nopersist"],
+ silent: "-silent" in args
});
}
}
@@ -409,38 +429,109 @@ const Mappings = Module("mappings", {
&& /^[nv](nore)?map$/.test(cmd);
}
+ function findMode(name) {
+ if (typeof name == "number")
+ return name;
+ for (let mode in modes.mainModes)
+ if (name == mode.char || name == mode.name.toLowerCase())
+ return mode.mask;
+ return null;
+ }
+ function getModes(args, def)
+ array.uniq((args["-modes"] || def || ["n", "v"]).map(findMode));
+ function uniqueModes(modes) {
+ modes = modes.map(modules.modes.closure.getMode);
+ let chars = [k for ([k, v] in Iterator(modules.modes.modeChars))
+ if (v.every(function (mode) modes.indexOf(mode) >= 0))];
+ return array.uniq(modes.filter(function (m) chars.indexOf(m.char) < 0).concat(chars));
+ }
+
const opts = {
- completer: function (context, args) completion.userMapping(context, args, modes),
+ completer: function (context, args) {
+ if (args.length == 1)
+ return completion.userMapping(context, args, mapmodes)
+ if (args["-javascript"])
+ return completion.javascript(context);
+ if (args["-ex"])
+ return completion.ex(context);
+ },
literal: 1,
- options: [{ names: ["", ""] }],
+ options: [
+ {
+ names: ["-builtin", "-b"],
+ description: "Execute this mapping as if there were no user-defined mappings"
+ },
+ {
+ names: ["-descripion", "-d"],
+ type: CommandOption.STRING,
+ description: "A discription of this mapping"
+ },
+ {
+ names: ["-ex", "-e"],
+ description: "Execute this mapping as an Ex command rather than keys"
+ },
+ {
+ names: ["-javascript", "-js", "-j"],
+ description: "Execute this mapping as JavaScript rather than keys"
+ },
+ {
+ names: ["-modes", "-mode", "-m"],
+ type: CommandOption.LIST,
+ description: "Create this mapping in the given modes",
+ validator: function (list) !list || list.every(findMode),
+ completer: function () [[array.compact([mode.name.toLowerCase(), mode.char]), mode.disp]
+ for (mode in modes.mainModes)],
+ },
+ {
+ names: ["-nopersist", "-n"],
+ description: "Do not save this mapping to an auto-generated RC file"
+ },
+ {
+ names: ["-silent", "-s", "", ""],
+ description: "Do not echo any generated keys to the command-line"
+ }
+ ],
serialize: function () {
- let noremap = this.name.indexOf("noremap") > -1;
- return [
+ return this.name == "map" ? [
{
command: this.name,
- options: map.silent ? { "": null } : {},
+ options: array([
+ ["-modes", uniqueModes(map.modes)],
+ map.noremap && ["-builtin"],
+ map.description != stockDescription && ["-description", map.description],
+ map.rhs.length > 1 && [map.rhs[0]],
+ map.silent && ["-silent"]])
+ .filter(util.identity)
+ .toObject(),
arguments: [map.names[0]],
- literalArg: map.rhs
+ literalArg: map.rhs[map.rhs.length - 1]
}
- for (map in mappings._mappingsIterator(modes, mappings._user))
- if (map.rhs && map.noremap == noremap && !isMultiMode(map, this.name))
- ];
+ for (map in userMappings())
+ if (map.persist)
+ ] : [];
}
};
+ function userMappings() {
+ let seen = {};
+ for (let [, stack] in Iterator(mappings._user))
+ for (let map in values(stack))
+ if (!set.add(seen, map.id))
+ yield map;
+ }
commands.add([ch ? ch + "m[ap]" : "map"],
"Map a key sequence" + modeDescription,
- function (args) { map(args, modes, false); },
+ function (args) { map(args, mapmodes, false); },
opts);
commands.add([ch + "no[remap]"],
"Map a key sequence without remapping keys" + modeDescription,
- function (args) { map(args, modes, true); },
+ function (args) { map(args, mapmodes, true); },
opts);
commands.add([ch + "mapc[lear]"],
"Remove all mappings" + modeDescription,
- function () { modes.forEach(function (mode) { mappings.removeAll(mode); }); },
+ function () { mapmodes.forEach(function (mode) { mappings.removeAll(mode); }); },
{ argCount: "0" });
commands.add([ch + "unm[ap]"],
@@ -449,7 +540,7 @@ const Mappings = Module("mappings", {
args = args[0];
let found = false;
- for (let [, mode] in Iterator(modes)) {
+ for (let [, mode] in Iterator(mapmodes)) {
if (mappings.hasMap(mode, args)) {
mappings.remove(mode, args);
found = true;
@@ -460,7 +551,7 @@ const Mappings = Module("mappings", {
},
{
argCount: "1",
- completer: function (context, args) completion.userMapping(context, args, modes)
+ completer: function (context, args) completion.userMapping(context, args, mapmodes)
});
}
diff --git a/common/content/modes.js b/common/content/modes.js
index 52fe6911..922238b8 100644
--- a/common/content/modes.js
+++ b/common/content/modes.js
@@ -10,6 +10,7 @@
const Modes = Module("modes", {
init: function () {
+ this.modeChars = {};
this._main = 1; // NORMAL
this._extended = 0; // NONE
@@ -143,6 +144,11 @@ const Modes = Module("modes", {
name: name,
disp: disp
}, options);
+ if (mode.char) {
+ this.modeChars[mode.char] = this.modeChars[mode.char] || [];
+ this.modeChars[mode.char].push(mode);
+ }
+
mode.display = mode.display || function () disp;
this._modeMap[name] = mode;
this._modeMap[this[name]] = mode;
diff --git a/common/locale/en-US/map.xml b/common/locale/en-US/map.xml
index 93053adc..13754b8b 100644
--- a/common/locale/en-US/map.xml
+++ b/common/locale/en-US/map.xml
@@ -62,8 +62,6 @@
prefixed with one of the above letters. For instance,
:imap creates a new key mapping in insert mode, while
:cunmap removes a key mapping from command-line mode.
- Although other modes do exist, their mappings may currently only
- be altered via JavaScript.
@@ -103,6 +101,24 @@
+Map options
+
+ Any of the map commands may be given the following options:
+
+
+
+
+
+ - -builtin
- Execute this mapping as if there were no user-defined mappings (short name -b)
+ - -descripion
- A discription of this mapping (short name -d)
+ - -ex
- Execute rhs as an Ex command rather than keys (short name -e)
+ - -javascript
- Execute rhs as JavaScript rather than keys (short names -js, -j)
+ - -modes
- Create this mapping in the given modes (short names -mode, -m)
+ - -nopersist
- Do not save this mapping to an auto-generated rc file (short name -n)
+ - -silent
- Do not echo any generated keys to the command-line (short name -s, also <silent> for Vim compatibility)
+
+
+
-
:no :noremap
:noremap lhs rhs
diff --git a/common/modules/base.jsm b/common/modules/base.jsm
index dd701aaf..aa0a3e2f 100644
--- a/common/modules/base.jsm
+++ b/common/modules/base.jsm
@@ -937,6 +937,17 @@ const array = Class("array", Array, {
*/
compact: function compact(ary) ary.filter(function (item) item != null),
+ /**
+ * Returns true if each element of ary1 is equal to the
+ * corresponding element in ary2.
+ *
+ * @param {Array} ary1
+ * @param {Array} ary2
+ * @returns {boolean}
+ */
+ equals: function (ary1, ary2)
+ ary1.length == ary2.length && Array.every(ary1, function (e, i) e == ary2[i]),
+
/**
* Flattens an array, such that all elements of the array are
* joined into a single array:
diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS
index 35d27921..6ccbdbab 100644
--- a/pentadactyl/NEWS
+++ b/pentadactyl/NEWS
@@ -28,6 +28,9 @@
* Added ‘transliterated’ option to 'hintmatching'
* Added 'autocomplete' option for specifying which completion
contexts should be autocompleted
+ * Added several new options to :map
+ * Removed the :source line at the end of files generated by
+ :mkpentadactylrc
* gf now toggles between source and content view.
The | key binding has been removed.
* :extadd now supports remote URLs as well as local files on