//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
-"use strict";
+/* use strict */
/** @scope modules */
* *action*.
* @param {string} description A short one line description of the key mapping.
* @param {function} action The action invoked by each key sequence.
- * @param {Object} extraInfo An optional extra configuration hash. The
+ * @param {Object} info An optional extra configuration hash. The
* following properties are supported.
* arg - see {@link Map#arg}
* count - see {@link Map#count}
* @private
*/
var Map = Class("Map", {
- init: function (modes, keys, description, action, extraInfo) {
+ init: function (modes, keys, description, action, info) {
this.id = ++Map.id;
this.modes = modes;
this._keys = keys;
Object.freeze(this.modes);
- if (extraInfo)
- this.update(extraInfo);
+ if (info) {
+ if (Set.has(Map.types, info.type))
+ this.update(Map.types[info.type]);
+ this.update(info);
+ }
},
- name: Class.memoize(function () this.names[0]),
+ name: Class.Memoize(function () this.names[0]),
/** @property {[string]} All of this mapping's names (key sequences). */
- names: Class.memoize(function () this._keys.map(function (k) events.canonicalKeys(k))),
+ names: Class.Memoize(function () this._keys.map(function (k) DOM.Event.canonicalKeys(k))),
get toStringParams() [this.modes.map(function (m) m.name), this.names.map(String.quote)],
* as an argument.
*/
motion: false,
+
/** @property {boolean} Whether the RHS of the mapping should expand mappings recursively. */
noremap: false,
+
+ /** @property {function(object)} A function to be executed before this mapping. */
+ preExecute: function preExecute(args) {},
+ /** @property {function(object)} A function to be executed after this mapping. */
+ postExecute: function postExecute(args) {},
+
/** @property {boolean} Whether any output from the mapping should be echoed on the command line. */
silent: false,
+
/** @property {string} The literal RHS expansion of this mapping. */
rhs: null,
+
+ /** @property {string} The type of this mapping. */
+ type: "",
+
/**
* @property {boolean} Specifies whether this is a user mapping. User
* mappings may be created by plugins, or directly by users. Users and
.map(function ([i, prop]) [prop, this[i]], arguments)
.toObject();
- args = update({ context: contexts.context },
- this.hive.argsExtra(args),
- args);
+ args = this.hive.makeArgs(this.hive.group.lastDocument,
+ contexts.context,
+ args);
let self = this;
function repeat() self.action(args)
mappings.repeat = repeat;
if (this.executing)
- util.dumpStack(_("map.recursive", args.command));
- dactyl.assert(!this.executing, _("map.recursive", args.command));
+ util.assert(!args.keypressEvents[0].isMacro,
+ _("map.recursive", args.command),
+ false);
try {
+ dactyl.triggerObserver("mappings.willExecute", this, args);
+ mappings.pushCommand();
+ this.preExecute(args);
this.executing = true;
var res = repeat();
}
}
finally {
this.executing = false;
+ mappings.popCommand();
+ this.postExecute(args);
+ dactyl.triggerObserver("mappings.executed", this, args);
}
return res;
}
}, {
- id: 0
+ id: 0,
+
+ types: {}
});
var MapHive = Class("MapHive", Contexts.Hive, {
delete this.states;
},
- states: Class.memoize(function () {
+ states: Class.Memoize(function () {
var states = {
candidates: {},
mappings: {}
for (let name in values(map.keys)) {
states.mappings[name] = map;
let state = "";
- for (let key in events.iterKeys(name)) {
+ for (let key in DOM.Event.iterKeys(name)) {
state += key;
if (state !== name)
states.candidates[state] = (states.candidates[state] || 0) + 1;
*/
var Mappings = Module("mappings", {
init: function () {
+ this.watches = [];
+ this._watchStack = 0;
+ this._yielders = 0;
+ },
+
+ afterCommands: function afterCommands(count, cmd, self) {
+ this.watches.push([cmd, self, Math.max(this._watchStack - 1, 0), count || 1]);
+ },
+
+ pushCommand: function pushCommand(cmd) {
+ this._watchStack++;
+ this._yielders = util.yielders;
+ },
+ popCommand: function popCommand(cmd) {
+ this._watchStack = Math.max(this._watchStack - 1, 0);
+ if (util.yielders > this._yielders)
+ this._watchStack = 0;
+
+ this.watches = this.watches.filter(function (elem) {
+ if (this._watchStack <= elem[2])
+ elem[3]--;
+ if (elem[3] <= 0)
+ elem[0].call(elem[1] || null);
+ return elem[3] > 0;
+ }, this);
},
repeat: Modes.boundProperty(),
expandLeader: function expandLeader(keyString) keyString.replace(/<Leader>/i, function () options["mapleader"]),
- prefixes: Class.memoize(function () {
+ prefixes: Class.Memoize(function () {
let list = Array.map("CASM", function (s) s + "-");
return iter(util.range(0, 1 << list.length)).map(function (mask)
expand: function expand(keys) {
keys = keys.replace(/<leader>/i, options["mapleader"]);
- if (!/<\*-/.test(keys))
- return keys;
- return util.debrace(events.iterKeys(keys).map(function (key) {
- if (/^<\*-/.test(key))
- return ["<", this.prefixes, key.slice(3)];
- return key;
- }, this).flatten().array).map(function (k) events.canonicalKeys(k));
+ if (!/<\*-/.test(keys))
+ var res = keys;
+ else
+ res = util.debrace(DOM.Event.iterKeys(keys).map(function (key) {
+ if (/^<\*-/.test(key))
+ return ["<", this.prefixes, key.slice(3)];
+ return key;
+ }, this).flatten().array).map(function (k) DOM.Event.canonicalKeys(k));
+
+ if (keys != arguments[0])
+ return [arguments[0]].concat(keys);
+ return keys;
},
iterate: function (mode) {
get: function get(mode, cmd) this.hives.map(function (h) h.get(mode, cmd)).compact()[0] || null,
/**
- * Returns an array of maps with names starting with but not equal to
+ * Returns a count of maps with names starting with but not equal to
* *prefix*.
*
* @param {Modes.Mode} mode The mode to search.
*/
getCandidates: function (mode, prefix)
this.hives.map(function (h) h.getCandidates(mode, prefix))
- .flatten(),
+ .reduce(function (a, b) a + b, 0),
/**
* Lists all user-defined mappings matching *filter* for the specified
.map(function (hive) [
{
command: "map",
- options: array([
- hive.name !== "user" && ["-group", hive.name],
- ["-modes", uniqueModes(map.modes)],
- ["-description", map.description],
- map.silent && ["-silent"]])
-
- .filter(util.identity)
- .toObject(),
+ options: {
+ "-count": map.count ? null : undefined,
+ "-description": map.description,
+ "-group": hive.name == "user" ? undefined : hive.name,
+ "-modes": uniqueModes(map.modes),
+ "-silent": map.silent ? null : undefined
+ },
arguments: [map.names[0]],
literalArg: map.rhs,
ignoreDefaults: true
name: [mode.char + "listk[eys]", mode.char + "lk"],
iterateIndex: function (args)
let (self = this, prefix = /^[bCmn]$/.test(mode.char) ? "" : mode.char + "_",
- tags = services["dactyl:"].HELP_TAGS)
+ haveTag = Set.has(help.tags))
({ helpTag: prefix + map.name, __proto__: map }
for (map in self.iterate(args, true))
- if (map.hive === mappings.builtin || Set.has(tags, prefix + map.name))),
+ if (map.hive === mappings.builtin || haveTag(prefix + map.name))),
description: "List all " + mode.displayName + " mode mappings along with their short descriptions",
index: mode.char + "-map",
getMode: function (args) mode,
};
},
javascript: function initJavascript(dactyl, modules, window) {
- JavaScript.setCompleter([mappings.get, mappings.builtin.get],
+ JavaScript.setCompleter([Mappings.prototype.get, MapHive.prototype.get],
[
null,
function (context, obj, args) [[m.names, m.description] for (m in this.iterate(args[0]))]