]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/content/modes.js
Import 1.0rc1 supporting Firefox up to 11.*
[dactyl.git] / common / content / modes.js
index c498830903670defc855de8d4e1723fe4acc806b..50b2ee5b16f4570025d76636cb7bf1ebc74f70ca 100644 (file)
@@ -4,7 +4,7 @@
 //
 // 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 */
 
@@ -57,24 +57,8 @@ var Modes = Module("modes", {
             description: "Active when nothing is focused",
             bases: [this.COMMAND]
         });
-        this.addMode("VISUAL", {
-            char: "v",
-            description: "Active when text is selected",
-            display: function () "VISUAL" + (this._extended & modes.LINE ? " LINE" : ""),
-            bases: [this.COMMAND],
-            ownsFocus: true
-        }, {
-            leave: function (stack, newMode) {
-                if (newMode.main == modes.CARET) {
-                    let selection = content.getSelection();
-                    if (selection && !selection.isCollapsed)
-                        selection.collapseToStart();
-                }
-                else if (stack.pop)
-                    editor.unselectText();
-            }
-        });
         this.addMode("CARET", {
+            char: "caret",
             description: "Active when the caret is visible in the web content",
             bases: [this.NORMAL]
         }, {
@@ -87,6 +71,8 @@ var Modes = Module("modes", {
                     modes.pop();
                 else if (!stack.pop && !this.pref)
                     this.pref = true;
+                if (!stack.pop)
+                    buffer.resetCaret();
             },
 
             leave: function (stack) {
@@ -94,27 +80,6 @@ var Modes = Module("modes", {
                     this.pref = false;
             }
         });
-        this.addMode("TEXT_EDIT", {
-            char: "t",
-            description: "Vim-like editing of input elements",
-            bases: [this.COMMAND],
-            ownsFocus: true
-        }, {
-            onKeyPress: function (eventList) {
-                const KILL = false, PASS = true;
-
-                // Hack, really.
-                if (eventList[0].charCode || /^<(?:.-)*(?:BS|Del|C-h|C-w|C-u|C-k)>$/.test(events.toString(eventList[0]))) {
-                    dactyl.beep();
-                    return KILL;
-                }
-                return PASS;
-            }
-        });
-        this.addMode("OUTPUT_MULTILINE", {
-            description: "Active when the multi-line output buffer is open",
-            bases: [this.NORMAL]
-        });
 
         this.addMode("INPUT", {
             char: "I",
@@ -122,20 +87,10 @@ var Modes = Module("modes", {
             bases: [this.MAIN],
             insert: true
         });
-        this.addMode("INSERT", {
-            char: "i",
-            description: "Active when an input element is focused",
-            insert: true,
-            ownsFocus: true
-        });
-        this.addMode("AUTOCOMPLETE", {
-            description: "Active when an input autocomplete pop-up is active",
-            display: function () "AUTOCOMPLETE (insert)",
-            bases: [this.INSERT]
-        });
 
         this.addMode("EMBED", {
             description: "Active when an <embed> or <object> element is focused",
+            bases: [modes.MAIN],
             insert: true,
             ownsFocus: true,
             passthrough: true
@@ -207,69 +162,36 @@ var Modes = Module("modes", {
 
             }
         });
-
-        function makeTree() {
-            let list = modes.all.filter(function (m) m.name !== m.description);
-
-            let tree = {};
-
-            for (let mode in values(list))
-                tree[mode.name] = {};
-
-            for (let mode in values(list))
-                for (let base in values(mode.bases))
-                    tree[base.name][mode.name] = tree[mode.name];
-
-            let roots = iter([m.name, tree[m.name]] for (m in values(list)) if (!m.bases.length)).toObject();
-
-            default xml namespace = NS;
-            function rec(obj) {
-                XML.ignoreWhitespace = XML.prettyPrinting = false;
-
-                let res = <ul dactyl:highlight="Dense" xmlns:dactyl={NS}/>;
-                Object.keys(obj).sort().forEach(function (name) {
-                    let mode = modes.getMode(name);
-                    res.* += <li><em>{mode.displayName}</em>: {mode.description}{
-                        rec(obj[name])
-                    }</li>;
-                });
-
-                if (res.*.length())
-                    return res;
-                return <></>;
-            }
-
-            return rec(roots);
-        }
-
-        util.timeout(function () {
-            // Waits for the add-on to become available, if necessary.
-            config.addon;
-            config.version;
-
-            services["dactyl:"].pages["modes.dtd"] = services["dactyl:"].pages["modes.dtd"]();
-        });
-
-        services["dactyl:"].pages["modes.dtd"] = function () [null,
-            util.makeDTD(iter({ "modes.tree": makeTree() },
-                              config.dtd))];
     },
+
     cleanup: function cleanup() {
         modes.reset();
     },
 
+    signals: {
+        "io.source": function ioSource(context, file, modTime) {
+            cache.flushEntry("modes.dtd", modTime);
+        }
+    },
+
     _getModeMessage: function _getModeMessage() {
         // when recording a macro
         let macromode = "";
         if (this.recording)
-            macromode = "recording";
+            macromode = "recording " + this.recording + " ";
         else if (this.replaying)
             macromode = "replaying";
 
-        let val = this._modeMap[this._main].display();
-        if (val)
-            return "-- " + val + " --" + macromode;
-        return macromode;
+        if (!options.get("showmode").getKey(this.main.allBases, false))
+            return macromode;
+
+        let modeName = this._modeMap[this._main].display();
+        if (!modeName)
+            return macromode;
+
+        if (macromode)
+            macromode = " " + macromode;
+        return "-- " + modeName + " --" + macromode;
     },
 
     NONE: 0,
@@ -302,6 +224,18 @@ var Modes = Module("modes", {
         dactyl.triggerObserver("modes.add", mode);
     },
 
+    removeMode: function removeMode(mode) {
+        this.remove(mode);
+        if (this[mode.name] == mode)
+            delete this[mode.name];
+        if (this._modeMap[mode.name] == mode)
+            delete this._modeMap[mode.name];
+        if (this._modeMap[mode.mode] == mode)
+            delete this._modeMap[mode.mode];
+
+        this._mainModes = this._mainModes.filter(function (m) m != mode);
+    },
+
     dumpStack: function dumpStack() {
         util.dump("Mode stack:");
         for (let [i, mode] in array.iterItems(this._modeStack))
@@ -327,9 +261,7 @@ var Modes = Module("modes", {
         if (!loaded.modes)
             return;
 
-        let msg = null;
-        if (options.get("showmode").getKey(this.main.allBases, false))
-            msg = this._getModeMessage();
+        let msg = this._getModeMessage();
 
         if (msg || loaded.commandline)
             commandline.widgets.mode = msg || null;
@@ -354,12 +286,11 @@ var Modes = Module("modes", {
         if (!(id in this.boundProperties))
             for (let elem in array.iterValues(this._modeStack))
                 elem.saved[id] = { obj: obj, prop: prop, value: obj[prop], test: test };
-        this.boundProperties[id] = { obj: Cu.getWeakReference(obj), prop: prop, test: test };
+        this.boundProperties[id] = { obj: util.weakReference(obj), prop: prop, test: test };
     },
 
     inSet: false,
 
-    // helper function to set both modes in one go
     set: function set(mainMode, extendedMode, params, stack) {
         var delayed, oldExtended, oldMain, prev, push;
 
@@ -440,6 +371,9 @@ var Modes = Module("modes", {
     },
 
     push: function push(mainMode, extendedMode, params) {
+        if (this.main == this.IGNORE)
+            this.pop();
+
         this.set(mainMode, extendedMode, params, { push: this.topOfStack });
     },
 
@@ -447,8 +381,7 @@ var Modes = Module("modes", {
         while (this._modeStack.length > 1 && this.main != mode) {
             let a = this._modeStack.pop();
             this.set(this.topOfStack.main, this.topOfStack.extended, this.topOfStack.params,
-                     update({ pop: a },
-                            args || {}));
+                     update({ pop: a }, args));
 
             if (mode == null)
                 return;
@@ -489,7 +422,7 @@ var Modes = Module("modes", {
         init: function init(name, options, params) {
             if (options.bases)
                 util.assert(options.bases.every(function (m) m instanceof this, this.constructor),
-                           _("mode.invalidBases"), true);
+                           _("mode.invalidBases"), false);
 
             this.update({
                 id: 1 << Modes.Mode._id++,
@@ -501,12 +434,12 @@ var Modes = Module("modes", {
 
         description: Messages.Localized(""),
 
-        displayName: Class.memoize(function () this.name.split("_").map(util.capitalize).join(" ")),
+        displayName: Class.Memoize(function () this.name.split("_").map(util.capitalize).join(" ")),
 
         isinstance: function isinstance(obj)
             this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj,
 
-        allBases: Class.memoize(function () {
+        allBases: Class.Memoize(function () {
             let seen = {}, res = [], queue = [this].concat(this.bases);
             for (let mode in array.iterValues(queue))
                 if (!Set.add(seen, mode)) {
@@ -520,7 +453,7 @@ var Modes = Module("modes", {
 
         get count() !this.insert,
 
-        _display: Class.memoize(function _display() this.name.replace("_", " ", "g")),
+        _display: Class.Memoize(function _display() this.name.replace("_", " ", "g")),
 
         display: function display() this._display,
 
@@ -528,15 +461,15 @@ var Modes = Module("modes", {
 
         hidden: false,
 
-        input: Class.memoize(function input() this.insert || this.bases.length && this.bases.some(function (b) b.input)),
+        input: Class.Memoize(function input() this.insert || this.bases.length && this.bases.some(function (b) b.input)),
 
-        insert: Class.memoize(function insert() this.bases.length && this.bases.some(function (b) b.insert)),
+        insert: Class.Memoize(function insert() this.bases.length && this.bases.some(function (b) b.insert)),
 
-        ownsFocus: Class.memoize(function ownsFocus() this.bases.length && this.bases.some(function (b) b.ownsFocus)),
+        ownsFocus: Class.Memoize(function ownsFocus() this.bases.length && this.bases.some(function (b) b.ownsFocus)),
 
         passEvent: function passEvent(event) this.input && event.charCode && !(event.ctrlKey || event.altKey || event.metaKey),
 
-        passUnknown: Class.memoize(function () options.get("passunknown").getKey(this.name)),
+        passUnknown: Class.Memoize(function () options.get("passunknown").getKey(this.name)),
 
         get mask() this,
 
@@ -584,24 +517,67 @@ var Modes = Module("modes", {
         }, desc));
     }
 }, {
+    cache: function initCache() {
+        function makeTree() {
+            let list = modes.all.filter(function (m) m.name !== m.description);
+
+            let tree = {};
+
+            for (let mode in values(list))
+                tree[mode.name] = {};
+
+            for (let mode in values(list))
+                for (let base in values(mode.bases))
+                    tree[base.name][mode.name] = tree[mode.name];
+
+            let roots = iter([m.name, tree[m.name]] for (m in values(list)) if (!m.bases.length)).toObject();
+
+            default xml namespace = NS;
+            function rec(obj) {
+                XML.ignoreWhitespace = XML.prettyPrinting = false;
+
+                let res = <ul dactyl:highlight="Dense" xmlns:dactyl={NS}/>;
+                Object.keys(obj).sort().forEach(function (name) {
+                    let mode = modes.getMode(name);
+                    res.* += <li><em>{mode.displayName}</em>: {mode.description}{
+                        rec(obj[name])
+                    }</li>;
+                });
+
+                if (res.*.length())
+                    return res;
+                return <></>;
+            }
+
+            return rec(roots);
+        }
+
+        cache.register("modes.dtd", function ()
+            util.makeDTD(iter({ "modes.tree": makeTree() },
+                              config.dtd)));
+    },
     mappings: function initMappings() {
         mappings.add([modes.BASE, modes.NORMAL],
             ["<Esc>", "<C-[>"],
             "Return to Normal mode",
             function () { modes.reset(); });
 
-        mappings.add([modes.INPUT, modes.COMMAND, modes.PASS_THROUGH, modes.QUOTE],
+        mappings.add([modes.INPUT, modes.COMMAND, modes.OPERATOR, modes.PASS_THROUGH, modes.QUOTE],
             ["<Esc>", "<C-[>"],
             "Return to the previous mode",
-            function () { modes.pop(); });
+            function () { modes.pop(null, { fromEscape: true }); });
 
-        mappings.add([modes.MENU], ["<C-c>"],
-            "Leave Menu mode",
+        mappings.add([modes.AUTOCOMPLETE, modes.MENU], ["<C-c>"],
+            "Leave Autocomplete or Menu mode",
             function () { modes.pop(); });
 
         mappings.add([modes.MENU], ["<Esc>"],
             "Close the current popup",
-            function () { return Events.PASS_THROUGH; });
+            function () {
+                if (events.popups.active.length)
+                    return Events.PASS_THROUGH;
+                modes.pop();
+            });
 
         mappings.add([modes.MENU], ["<C-[>"],
             "Close the current popup",
@@ -642,16 +618,17 @@ var Modes = Module("modes", {
 
         options.add(["passunknown", "pu"],
             "Pass through unknown keys in these modes",
-            "stringlist", "!text_edit,base",
+            "stringlist", "!text_edit,!visual,base",
             opts);
 
         options.add(["showmode", "smd"],
             "Show the current mode in the command line when it matches this expression",
-            "stringlist", "caret,output_multiline,!normal,base",
+            "stringlist", "caret,output_multiline,!normal,base,operator",
             opts);
     },
     prefs: function initPrefs() {
-        prefs.watch("accessibility.browsewithcaret", function () modes.onCaretChange.apply(modes, arguments));
+        prefs.watch("accessibility.browsewithcaret",
+                    function () { modes.onCaretChange.apply(modes, arguments) });
     }
 });