]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/content/mappings.js
Import 1.0b7.1 supporting Firefox up to 8.*
[dactyl.git] / common / content / mappings.js
index 668d8ce106a02fffd666ae93fb797ee425a6621b..155849e46fa73a6fb848beef87b9be44f2172129 100644 (file)
@@ -12,8 +12,8 @@
  * A class representing key mappings. Instances are created by the
  * {@link Mappings} class.
  *
- * @param {number[]} modes The modes in which this mapping is active.
- * @param {string[]} keys The key sequences which are bound to
+ * @param {[Modes.Mode]} modes The modes in which this mapping is active.
+ * @param {[string]} keys The key sequences which are bound to
  *     *action*.
  * @param {string} description A short one line description of the key mapping.
  * @param {function} action The action invoked by each key sequence.
  */
 var Map = Class("Map", {
     init: function (modes, keys, description, action, extraInfo) {
-        modes = Array.concat(modes);
-        if (!modes.every(util.identity))
-            throw TypeError("Invalid modes: " + modes);
-
         this.id = ++Map.id;
         this.modes = modes;
         this._keys = keys;
         this.action = action;
         this.description = description;
 
-        if (Object.freeze)
-            Object.freeze(this.modes);
+        Object.freeze(this.modes);
 
         if (extraInfo)
-            update(this, extraInfo);
+            this.update(extraInfo);
     },
 
     name: Class.memoize(function () this.names[0]),
 
-    /** @property {string[]} All of this mapping's names (key sequences). */
+    /** @property {[string]} All of this mapping's names (key sequences). */
     names: Class.memoize(function () this._keys.map(function (k) events.canonicalKeys(k))),
 
     get toStringParams() [this.modes.map(function (m) m.name), this.names.map(String.quote)],
@@ -58,7 +53,7 @@ var Map = Class("Map", {
 
     /** @property {number} A unique ID for this mapping. */
     id: null,
-    /** @property {number[]} All of the modes for which this mapping applies. */
+    /** @property {[Modes.Mode]} All of the modes for which this mapping applies. */
     modes: null,
     /** @property {function (number)} The function called to execute this mapping. */
     action: null,
@@ -96,7 +91,7 @@ var Map = Class("Map", {
      */
     hasName: function (name) this.keys.indexOf(name) >= 0,
 
-    get keys() this.names.map(mappings.expandLeader),
+    get keys() array.flatten(this.names.map(mappings.closure.expand)),
 
     /**
      * Execute the action for this mapping.
@@ -119,7 +114,7 @@ var Map = Class("Map", {
             mappings.repeat = repeat;
 
         if (this.executing)
-            util.dumpStack("Attempt to execute mapping recursively: " + args.command);
+            util.dumpStack(_("map.recursive", args.command));
         dactyl.assert(!this.executing, _("map.recursive", args.command));
 
         try {
@@ -159,10 +154,10 @@ var MapHive = Class("MapHive", Contexts.Hive, {
     },
 
     /**
-     * Adds a new default key mapping.
+     * Adds a new key mapping.
      *
-     * @param {number[]} modes The modes that this mapping applies to.
-     * @param {string[]} keys The key sequences which are bound to *action*.
+     * @param {[Modes.Mode]} modes The modes that this mapping applies to.
+     * @param {[string]} keys The key sequences which are bound to *action*.
      * @param {string} description A description of the key mapping.
      * @param {function} action The action invoked by each key sequence.
      * @param {Object} extra An optional extra configuration hash.
@@ -171,6 +166,10 @@ var MapHive = Class("MapHive", Contexts.Hive, {
     add: function (modes, keys, description, action, extra) {
         extra = extra || {};
 
+        modes = Array.concat(modes);
+        if (!modes.every(util.identity))
+            throw TypeError(/*L*/"Invalid modes: " + modes);
+
         let map = Map(modes, keys, description, action, extra);
         map.definedAt = contexts.getCaller(Components.stack.caller);
         map.hive = this;
@@ -307,13 +306,32 @@ var Mappings = Module("mappings", {
 
     get userHives() this.allHives.filter(function (h) h !== this.builtin, this),
 
-    expandLeader: function (keyString) keyString.replace(/<Leader>/i, options["mapleader"]),
+    expandLeader: function expandLeader(keyString) keyString.replace(/<Leader>/i, function () options["mapleader"]),
+
+    prefixes: Class.memoize(function () {
+        let list = Array.map("CASM", function (s) s + "-");
+
+        return iter(util.range(0, 1 << list.length)).map(function (mask)
+            list.filter(function (p, i) mask & (1 << i)).join("")).toArray().concat("*-");
+    }),
+
+    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));
+    },
 
     iterate: function (mode) {
         let seen = {};
         for (let hive in this.hives.iterValues())
             for (let map in array(hive.getStack(mode)).iterValues())
-                if (!set.add(seen, map.name))
+                if (!Set.add(seen, map.name))
                     yield map;
     },
 
@@ -330,8 +348,8 @@ var Mappings = Module("mappings", {
     /**
      * Adds a new default key mapping.
      *
-     * @param {number[]} modes The modes that this mapping applies to.
-     * @param {string[]} keys The key sequences which are bound to *action*.
+     * @param {[Modes.Mode]} modes The modes that this mapping applies to.
+     * @param {[string]} keys The key sequences which are bound to *action*.
      * @param {string} description A description of the key mapping.
      * @param {function} action The action invoked by each key sequence.
      * @param {Object} extra An optional extra configuration hash.
@@ -352,8 +370,8 @@ var Mappings = Module("mappings", {
     /**
      * Adds a new user-defined key mapping.
      *
-     * @param {number[]} modes The modes that this mapping applies to.
-     * @param {string[]} keys The key sequences which are bound to *action*.
+     * @param {[Modes.Mode]} modes The modes that this mapping applies to.
+     * @param {[string]} keys The key sequences which are bound to *action*.
      * @param {string} description A description of the key mapping.
      * @param {function} action The action invoked by each key sequence.
      * @param {Object} extra An optional extra configuration hash (see
@@ -369,7 +387,7 @@ var Mappings = Module("mappings", {
     /**
      * Returns the map from *mode* named *cmd*.
      *
-     * @param {number} mode The mode to search.
+     * @param {Modes.Mode} mode The mode to search.
      * @param {string} cmd The map name to match.
      * @returns {Map}
      */
@@ -379,9 +397,9 @@ var Mappings = Module("mappings", {
      * Returns an array of maps with names starting with but not equal to
      * *prefix*.
      *
-     * @param {number} mode The mode to search.
+     * @param {Modes.Mode} mode The mode to search.
      * @param {string} prefix The map prefix string to match.
-     * @returns {Map[]}
+     * @returns {[Map]}
      */
     getCandidates: function (mode, prefix)
         this.hives.map(function (h) h.getCandidates(mode, prefix))
@@ -389,15 +407,15 @@ var Mappings = Module("mappings", {
 
     /**
      * Lists all user-defined mappings matching *filter* for the specified
-     * *modes*.
+     * *modes* in the specified *hives*.
      *
-     * @param {number[]} modes An array of modes to search.
-     * @param {string} filter The filter string to match.
+     * @param {[Modes.Mode]} modes An array of modes to search.
+     * @param {string} filter The filter string to match. @optional
+     * @param {[MapHive]} hives The map hives to list. @optional
      */
     list: function (modes, filter, hives) {
-        let modeSign = "";
-        modes.filter(function (m)  m.char).forEach(function (m) { modeSign += m.char; });
-        modes.filter(function (m) !m.char).forEach(function (m) { modeSign += " " + m.name; });
+        let modeSign = modes.map(function (m) m.char || "").join("")
+                     + modes.map(function (m) !m.char ? " " + m.name : "").join("");
         modeSign = modeSign.replace(/^ /, "");
 
         hives = (hives || mappings.userHives).map(function (h) [h, maps(h)])
@@ -413,9 +431,9 @@ var Mappings = Module("mappings", {
         let list = <table>
                 <tr highlight="Title">
                     <td/>
-                    <td style="padding-right: 1em;">Mode</td>
-                    <td style="padding-right: 1em;">Command</td>
-                    <td style="padding-right: 1em;">Action</td>
+                    <td style="padding-right: 1em;">{_("title.Mode")}</td>
+                    <td style="padding-right: 1em;">{_("title.Command")}</td>
+                    <td style="padding-right: 1em;">{_("title.Action")}</td>
                 </tr>
                 <col style="min-width: 6em; padding-right: 1em;"/>
                 {
@@ -459,6 +477,11 @@ var Mappings = Module("mappings", {
                     return;
                 }
 
+                if (args[1] && !/^<nop>$/i.test(args[1])
+                    && !args["-count"] && !args["-ex"] && !args["-javascript"]
+                    && mapmodes.every(function (m) m.count))
+                    args[1] = "<count>" + args[1];
+
                 let [lhs, rhs] = args;
                 if (noremap)
                     args["-builtin"] = true;
@@ -474,7 +497,7 @@ var Mappings = Module("mappings", {
                         contexts.bindMacro(args, "-keys", function (params) params),
                         {
                             arg: args["-arg"],
-                            count: args["-count"],
+                            count: args["-count"] || !(args["-ex"] || args["-javascript"]),
                             noremap: args["-builtin"],
                             persist: !args["-nopersist"],
                             get rhs() String(this.action),
@@ -484,92 +507,93 @@ var Mappings = Module("mappings", {
             }
 
             const opts = {
-                    completer: function (context, args) {
-                        let mapmodes = array.uniq(args["-modes"].map(findMode));
-                        if (args.length == 1)
-                            return completion.userMapping(context, mapmodes, args["-group"]);
-                        if (args.length == 2) {
-                            if (args["-javascript"])
-                                return completion.javascript(context);
-                            if (args["-ex"])
-                                return completion.ex(context);
-                        }
+                identifier: "map",
+                completer: function (context, args) {
+                    let mapmodes = array.uniq(args["-modes"].map(findMode));
+                    if (args.length == 1)
+                        return completion.userMapping(context, mapmodes, args["-group"]);
+                    if (args.length == 2) {
+                        if (args["-javascript"])
+                            return completion.javascript(context);
+                        if (args["-ex"])
+                            return completion.ex(context);
+                    }
+                },
+                hereDoc: true,
+                literal: 1,
+                options: [
+                    {
+                        names: ["-arg", "-a"],
+                        description: "Accept an argument after the requisite key press",
                     },
-                    hereDoc: true,
-                    literal: 1,
-                    options: [
-                        {
-                            names: ["-arg", "-a"],
-                            description: "Accept an argument after the requisite key press",
-                        },
-                        {
-                            names: ["-builtin", "-b"],
-                            description: "Execute this mapping as if there were no user-defined mappings"
-                        },
-                        {
-                            names: ["-count", "-c"],
-                            description: "Accept a count before the requisite key press"
-                        },
-                        {
-                            names: ["-description", "-desc", "-d"],
-                            description: "A description of this mapping",
-                            default: "User-defined mapping",
-                            type: CommandOption.STRING
-                        },
-                        {
-                            names: ["-ex", "-e"],
-                            description: "Execute this mapping as an Ex command rather than keys"
-                        },
-                        contexts.GroupFlag("mappings"),
-                        {
-                            names: ["-javascript", "-js", "-j"],
-                            description: "Execute this mapping as JavaScript rather than keys"
-                        },
-                        update({}, modeFlag, {
-                            names: ["-modes", "-mode", "-m"],
-                            type: CommandOption.LIST,
-                            description: "Create this mapping in the given modes",
-                            default: mapmodes || ["n", "v"]
-                        }),
-                        {
-                            names: ["-nopersist", "-n"],
-                            description: "Do not save this mapping to an auto-generated RC file"
-                        },
-                        {
-                            names: ["-silent", "-s", "<silent>", "<Silent>"],
-                            description: "Do not echo any generated keys to the command line"
-                        }
-                    ],
-                    serialize: function () {
-                        return this.name != "map" ? [] :
-                            array(mappings.userHives)
-                                .filter(function (h) h.persist)
-                                .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(),
-                                        arguments: [map.names[0]],
-                                        literalArg: map.rhs,
-                                        ignoreDefaults: true
-                                    }
-                                    for (map in userMappings(hive))
-                                    if (map.persist)
-                                ])
-                                .flatten().array;
+                    {
+                        names: ["-builtin", "-b"],
+                        description: "Execute this mapping as if there were no user-defined mappings"
+                    },
+                    {
+                        names: ["-count", "-c"],
+                        description: "Accept a count before the requisite key press"
+                    },
+                    {
+                        names: ["-description", "-desc", "-d"],
+                        description: "A description of this mapping",
+                        default: /*L*/"User-defined mapping",
+                        type: CommandOption.STRING
+                    },
+                    {
+                        names: ["-ex", "-e"],
+                        description: "Execute this mapping as an Ex command rather than keys"
+                    },
+                    contexts.GroupFlag("mappings"),
+                    {
+                        names: ["-javascript", "-js", "-j"],
+                        description: "Execute this mapping as JavaScript rather than keys"
+                    },
+                    update({}, modeFlag, {
+                        names: ["-modes", "-mode", "-m"],
+                        type: CommandOption.LIST,
+                        description: "Create this mapping in the given modes",
+                        default: mapmodes || ["n", "v"]
+                    }),
+                    {
+                        names: ["-nopersist", "-n"],
+                        description: "Do not save this mapping to an auto-generated RC file"
+                    },
+                    {
+                        names: ["-silent", "-s", "<silent>", "<Silent>"],
+                        description: "Do not echo any generated keys to the command line"
                     }
+                ],
+                serialize: function () {
+                    return this.name != "map" ? [] :
+                        array(mappings.userHives)
+                            .filter(function (h) h.persist)
+                            .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(),
+                                    arguments: [map.names[0]],
+                                    literalArg: map.rhs,
+                                    ignoreDefaults: true
+                                }
+                                for (map in userMappings(hive))
+                                if (map.persist)
+                            ])
+                            .flatten().array;
+                }
             };
             function userMappings(hive) {
                 let seen = {};
                 for (let stack in values(hive.stacks))
                     for (let map in array.iterValues(stack))
-                        if (!set.add(seen, map.id))
+                        if (!Set.add(seen, map.id))
                             yield map;
             }
 
@@ -606,6 +630,7 @@ var Mappings = Module("mappings", {
                         dactyl.echoerr(_("map.noSuch", args[0]));
                 },
                 {
+                    identifier: "unmap",
                     argCount: "?",
                     bang: true,
                     completer: opts.completer,
@@ -627,7 +652,7 @@ var Mappings = Module("mappings", {
             validator: function (value) Array.concat(value).every(findMode),
             completer: function () [[array.compact([mode.name.toLowerCase().replace(/_/g, "-"), mode.char]), mode.description]
                                     for (mode in values(modes.all))
-                                    if (!mode.hidden)],
+                                    if (!mode.hidden)]
         };
 
         function findMode(name) {
@@ -641,7 +666,9 @@ var Mappings = Module("mappings", {
         function uniqueModes(modes) {
             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));
+            return array.uniq(modes.filter(function (m) chars.indexOf(m.char) < 0)
+                                   .map(function (m) m.name.toLowerCase())
+                                   .concat(chars));
         }
 
         commands.add(["feedkeys", "fk"],
@@ -664,14 +691,14 @@ var Mappings = Module("mappings", {
             if (mode.char && !commands.get(mode.char + "map", true))
                 addMapCommands(mode.char,
                                [m.mask for (m in modes.mainModes) if (m.char == mode.char)],
-                               [mode.name.toLowerCase()]);
+                               mode.displayName);
 
         let args = {
             getMode: function (args) findMode(args["-mode"]),
             iterate: function (args, mainOnly) {
                 let modes = [this.getMode(args)];
                 if (!mainOnly)
-                    modes = modes.concat(modes[0].bases);
+                    modes = modes[0].allBases;
 
                 let seen = {};
                 // Bloody hell. --Kris
@@ -679,7 +706,7 @@ var Mappings = Module("mappings", {
                     for (let hive in mappings.hives.iterValues())
                         for (let map in array.iterValues(hive.getStack(mode)))
                             for (let name in values(map.names))
-                                if (!set.add(seen, name)) {
+                                if (!Set.add(seen, name)) {
                                     yield {
                                         name: name,
                                         columns: [
@@ -725,8 +752,8 @@ var Mappings = Module("mappings", {
                                  tags = services["dactyl:"].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))),
-                    description: "List all " + mode.name + " mode mappings along with their short descriptions",
+                                     if (map.hive === mappings.builtin || Set.has(tags, prefix + map.name))),
+                    description: "List all " + mode.displayName + " mode mappings along with their short descriptions",
                     index: mode.char + "-map",
                     getMode: function (args) mode,
                     options: []