]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/content/commandline.js
Import r6923 from upstream hg supporting Firefox up to 22.0a1
[dactyl.git] / common / content / commandline.js
index c318759626db4b8df5accd6c6466c13b0ee1abdf..f01cbb0b33b9f31a84b8d7af8bfd8ca3c5438037 100644 (file)
@@ -1,10 +1,10 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2012 Kris Maglione <maglione.k@gmail.com>
 //
 // 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 */
 
@@ -14,56 +14,47 @@ var CommandWidgets = Class("CommandWidgets", {
     init: function init() {
         let s = "dactyl-statusline-field-";
 
-        XML.ignoreWhitespace = true;
         overlay.overlayWindow(window, {
             objects: {
                 eventTarget: commandline
             },
-            append: <e4x xmlns={XUL} xmlns:dactyl={NS}>
-                <vbox id={config.ids.commandContainer}>
-                    <vbox class="dactyl-container" hidden="false" collapsed="true">
-                        <iframe class="dactyl-completions" id="dactyl-completions-dactyl-commandline" src="dactyl://content/buffer.xhtml"
-                                contextmenu="dactyl-contextmenu"
-                                flex="1" hidden="false" collapsed="false"
-                                highlight="Events" events="mowEvents" />
-                    </vbox>
-
-                    <stack orient="horizontal" align="stretch" class="dactyl-container" id="dactyl-container" highlight="CmdLine CmdCmdLine">
-                        <textbox class="plain" id="dactyl-strut"   flex="1" crop="end" collapsed="true"/>
-                        <textbox class="plain" id="dactyl-mode"    flex="1" crop="end"/>
-                        <textbox class="plain" id="dactyl-message" flex="1" readonly="true"/>
-
-                        <hbox id="dactyl-commandline" hidden="false" class="dactyl-container" highlight="Normal CmdNormal" collapsed="true">
-                            <label   id="dactyl-commandline-prompt"  class="dactyl-commandline-prompt  plain" flex="0" crop="end" value="" collapsed="true"/>
-                            <textbox id="dactyl-commandline-command" class="dactyl-commandline-command plain" flex="1" type="input" timeout="100"
-                                     highlight="Events" />
-                        </hbox>
-                    </stack>
-
-                    <vbox class="dactyl-container" hidden="false" collapsed="false" highlight="CmdLine">
-                        <textbox id="dactyl-multiline-input" class="plain" flex="1" rows="1" hidden="false" collapsed="true" multiline="true"
-                                 highlight="Normal Events" events="multilineInputEvents" />
-                    </vbox>
-                </vbox>
-
-                <stack id="dactyl-statusline-stack">
-                    <hbox id={s + "commandline"} hidden="false" class="dactyl-container" highlight="Normal StatusNormal" collapsed="true">
-                        <label id={s + "commandline-prompt"}    class="dactyl-commandline-prompt  plain" flex="0" crop="end" value="" collapsed="true"/>
-                        <textbox id={s + "commandline-command"} class="dactyl-commandline-command plain" flex="1" type="text" timeout="100"
-                                 highlight="Events" />
-                    </hbox>
-                </stack>
-            </e4x>.elements(),
-
-            before: <e4x xmlns={XUL} xmlns:dactyl={NS}>
-                <toolbar id={statusline.statusBar.id}>
-                    <vbox id={"dactyl-completions-" + s + "commandline-container"} class="dactyl-container" hidden="false" collapsed="true">
-                        <iframe class="dactyl-completions" id={"dactyl-completions-" + s + "commandline"} src="dactyl://content/buffer.xhtml"
-                                contextmenu="dactyl-contextmenu" flex="1" hidden="false" collapsed="false"
-                                highlight="Events" events="mowEvents" />
-                    </vbox>
-                </toolbar>
-            </e4x>.elements()
+            append: [
+                ["vbox", { id: config.ids.commandContainer, xmlns: "xul" },
+                    ["vbox", { class: "dactyl-container", hidden: "false", collapsed: "true" },
+                        ["iframe", { class: "dactyl-completions", id: "dactyl-completions-dactyl-commandline",
+                                     src: "dactyl://content/buffer.xhtml", contextmenu: "dactyl-contextmenu",
+                                     flex: "1", hidden: "false", collapsed: "false",
+                                     highlight: "Events", events: "mowEvents" }]],
+
+                    ["stack", { orient: "horizontal", align: "stretch", class: "dactyl-container",
+                                id: "dactyl-container", highlight: "CmdLine CmdCmdLine" },
+                        ["textbox", { class: "plain", id: "dactyl-strut",   flex: "1", crop: "end", collapsed: "true" }],
+                        ["textbox", { class: "plain", id: "dactyl-mode",    flex: "1", crop: "end" }],
+                        ["hbox", { id: "dactyl-message-box" },
+                            ["label", { class: "plain", id: "dactyl-message-pre", flex: "0", readonly: "true", highlight: "WarningMsg" }],
+                            ["textbox", { class: "plain", id: "dactyl-message", flex: "1", readonly: "true" }]],
+
+                        ["hbox", { id: "dactyl-commandline", hidden: "false", class: "dactyl-container", highlight: "Normal CmdNormal", collapsed: "true" },
+                            ["label", {   id: "dactyl-commandline-prompt",  class: "dactyl-commandline-prompt  plain", flex: "0", crop: "end", value: "", collapsed: "true" }],
+                            ["textbox", { id: "dactyl-commandline-command", class: "dactyl-commandline-command plain", flex: "1", type: "input", timeout: "100",
+                                          highlight: "Events" }]]],
+
+                    ["vbox", { class: "dactyl-container", hidden: "false", collapsed: "false", highlight: "CmdLine" },
+                        ["textbox", { id: "dactyl-multiline-input", class: "plain", flex: "1", rows: "1", hidden: "false", collapsed: "true",
+                                      multiline: "true", highlight: "Normal Events", events: "multilineInputEvents" }]]],
+
+                ["stack", { id: "dactyl-statusline-stack", xmlns: "xul" },
+                    ["hbox", { id: s + "commandline", hidden: "false", class: "dactyl-container", highlight: "Normal StatusNormal", collapsed: "true" },
+                        ["label", { id: s + "commandline-prompt",    class: "dactyl-commandline-prompt  plain", flex: "0", crop: "end", value: "", collapsed: "true" }],
+                        ["textbox", { id: s + "commandline-command", class: "dactyl-commandline-command plain", flex: "1", type: "text", timeout: "100",
+                                      highlight: "Events",  }]]]],
+
+            before: [
+                ["toolbar", { id: statusline.statusBar.id, xmlns: "xul" },
+                    ["vbox", { id: "dactyl-completions-" + s + "commandline-container", class: "dactyl-container", hidden: "false", collapsed: "true" },
+                        ["iframe", { class: "dactyl-completions", id: "dactyl-completions-" + s + "commandline", src: "dactyl://content/buffer.xhtml",
+                                     contextmenu: "dactyl-contextmenu", flex: "1", hidden: "false", collapsed: "false", highlight: "Events",
+                                     events: "mowEvents" }]]]],
         });
 
         this.elements = {};
@@ -135,12 +126,26 @@ var CommandWidgets = Class("CommandWidgets", {
                     return this.statusbar;
 
                 let statusElem = this.statusbar.message;
-                if (value && !value[2] && statusElem.editor && statusElem.editor.rootElement.scrollWidth > statusElem.scrollWidth)
+                // Currently doesn't work as expected with <hbox> parent.
+                if (false && value && !value[2] && statusElem.editor && statusElem.editor.rootElement.scrollWidth > statusElem.scrollWidth)
                     return this.commandbar;
                 return this.activeGroup.mode;
             }
         });
 
+        this.addElement({
+            name: "message-pre",
+            defaultGroup: "WarningMsg",
+            getGroup: function () this.activeGroup.message
+        });
+
+        this.addElement({
+            name: "message-box",
+            defaultGroup: "Normal",
+            getGroup: function () this.activeGroup.message,
+            getValue: function () this.message
+        });
+
         this.addElement({
             name: "mode",
             defaultGroup: "ModeMsg",
@@ -224,6 +229,7 @@ var CommandWidgets = Class("CommandWidgets", {
     },
 
     updateVisibility: function updateVisibility() {
+        let changed = 0;
         for (let elem in values(this.elements))
             if (elem.getGroup) {
                 let value = elem.getValue ? elem.getValue.call(this)
@@ -234,6 +240,7 @@ var CommandWidgets = Class("CommandWidgets", {
                     let meth, node = group[elem.name];
                     let visible = (value && group === activeGroup);
                     if (node && !node.collapsed == !visible) {
+                        changed++;
                         node.collapsed = !visible;
                         if (elem.onVisibility)
                             elem.onVisibility.call(this, node, visible);
@@ -247,12 +254,22 @@ var CommandWidgets = Class("CommandWidgets", {
         function check(node) {
             if (DOM(node).style.display === "-moz-stack") {
                 let nodes = Array.filter(node.children, function (n) !n.collapsed && n.boxObject.height);
-                nodes.forEach(function (node, i) node.style.opacity = (i == nodes.length - 1) ? "" : "0");
+                nodes.forEach(function (node, i) { node.style.opacity = (i == nodes.length - 1) ? "" : "0" });
             }
             Array.forEach(node.children, check);
         }
         [this.commandbar.container, this.statusbar.container].forEach(check);
 
+        // Work around a redrawing bug.
+        if (changed && config.haveGecko("16", "20")) {
+            util.delay(function () {
+                // Urgh.
+                statusline.statusBar.style.paddingRight = "1px";
+                DOM(statusline.statusBar).rect; // Force reflow.
+                statusline.statusBar.style.paddingRight = "";
+            }, 0);
+        }
+
         if (this.initialized && loaded.mow && mow.visible)
             mow.resize(false);
     },
@@ -650,6 +667,7 @@ var CommandLine = Module("commandline", {
         if (!scroll || Date.now() - this._lastEchoTime > 5000)
             this.clearMessage();
         this._lastEchoTime = 0;
+        this.hiddenMessages = 0;
 
         if (!this.commandSession) {
             this.widgets.command = null;
@@ -664,8 +682,10 @@ var CommandLine = Module("commandline", {
     },
 
     clearMessage: function clearMessage() {
-        if (this.widgets.message && this.widgets.message[1] === this._lastClearable)
+        if (this.widgets.message && this.widgets.message[1] === this._lastClearable) {
             this.widgets.message = null;
+            this.hiddenMessages = 0;
+        }
     },
 
     /**
@@ -675,11 +695,11 @@ var CommandLine = Module("commandline", {
      * @param {XML} xml The output as an E4X XML object.
      */
     commandOutput: function commandOutput(xml) {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        if (this.command)
-            this.echo(<><div xmlns={XHTML}>:{this.command}</div>&#x0d;{xml}</>, this.HIGHLIGHT_NORMAL, this.FORCE_MULTILINE);
-        else
+        if (!this.command)
             this.echo(xml, this.HIGHLIGHT_NORMAL, this.FORCE_MULTILINE);
+        else
+            this.echo([["div", { xmlns: "html" }, ":" + this.command], "\n", xml],
+                      this.HIGHLIGHT_NORMAL, this.FORCE_MULTILINE);
         this.command = null;
     },
 
@@ -710,10 +730,20 @@ var CommandLine = Module("commandline", {
         let field = this.widgets.active.message.inputField;
         if (field.value && !forceSingle && field.editor.rootElement.scrollWidth > field.scrollWidth) {
             this.widgets.message = null;
-            mow.echo(<span highlight="Message">{str}</span>, highlightGroup, true);
+            mow.echo(["span", { highlight: "Message" }, str], highlightGroup, true);
         }
     },
 
+    _hiddenMessages: 0,
+    get hiddenMessages() this._hiddenMessages,
+    set hiddenMessages(val) {
+        this._hiddenMessages = val;
+        if (val)
+            this.widgets["message-pre"] = _("commandline.moreMessages", val) + " ";
+        else
+            this.widgets["message-pre"] = null
+    },
+
     _lastEcho: null,
 
     /**
@@ -747,40 +777,68 @@ var CommandLine = Module("commandline", {
 
         highlightGroup = highlightGroup || this.HL_NORMAL;
 
-        if (flags & this.APPEND_TO_MESSAGES) {
-            let message = isObject(data) ? data : { message: data };
+        let self = this;
+        function appendToMessages(data) {
+            let message = isObject(data) && !DOM.isJSONXML(data) ? data : { message: data };
 
             // Make sure the memoized message property is an instance property.
             message.message;
-            this._messageHistory.add(update({ highlight: highlightGroup }, message));
-            data = message.message;
+            self._messageHistory.add(update({ highlight: highlightGroup }, message));
+            return message.message;
         }
 
+        if (flags & this.APPEND_TO_MESSAGES)
+            data = appendToMessages(data);
+
         if ((flags & this.ACTIVE_WINDOW) && window != overlay.activeWindow)
             return;
 
         if ((flags & this.DISALLOW_MULTILINE) && !this.widgets.mowContainer.collapsed)
             return;
 
-        let single = flags & (this.FORCE_SINGLELINE | this.DISALLOW_MULTILINE);
+        let forceSingle = flags & (this.FORCE_SINGLELINE | this.DISALLOW_MULTILINE);
         let action = this._echoLine;
 
         if ((flags & this.FORCE_MULTILINE) || (/\n/.test(data) || !isinstance(data, [_, "String"])) && !(flags & this.FORCE_SINGLELINE))
             action = mow.closure.echo;
 
-        if (single)
+        let single = function () action == self._echoLine;
+
+        if (forceSingle) {
             this._lastEcho = null;
+            this.hiddenMessages = 0;
+        }
         else {
-            if (this.widgets.message && this.widgets.message[1] == this._lastEcho)
-                mow.echo(<span highlight="Message">{this._lastEcho}</span>,
-                         this.widgets.message[0], true);
-
-            if (action === this._echoLine && !(flags & this.FORCE_MULTILINE)
-                && !(dactyl.fullyInitialized && this.widgets.mowContainer.collapsed)) {
+            // So complicated...
+            if (single() && !this.widgets.mowContainer.collapsed) {
                 highlightGroup += " Message";
                 action = mow.closure.echo;
             }
-            this._lastEcho = (action == this._echoLine) && data;
+            else if (!single() && this.widgets.mowContainer.collapsed) {
+                if (this._lastEcho && this.widgets.message && this.widgets.message[1] == this._lastEcho.msg) {
+                    if (!(this._lastEcho.flags & this.APPEND_TO_MESSAGES))
+                        appendToMessages(this._lastEcho.data);
+
+                    mow.echo(
+                        ["span", { highlight: "Message" },
+                            ["span", { highlight: "WarningMsg" },
+                                _("commandline.moreMessages", this.hiddenMessages + 1) + " "],
+                            this._lastEcho.msg],
+                        this.widgets.message[0], true);
+
+                    this.hiddenMessages = 0;
+                }
+            }
+            else if (this._lastEcho && this.widgets.message && this.widgets.message[1] == this._lastEcho.msg) {
+                if (!(this._lastEcho.flags & this.APPEND_TO_MESSAGES))
+                    appendToMessages(this._lastEcho.data);
+                if (single() && !(flags & this.APPEND_TO_MESSAGES))
+                    appendToMessages(data);
+
+                flags |= this.APPEND_TO_MESSAGES;
+                this.hiddenMessages++;
+            }
+            this._lastEcho = single() && { flags: flags, msg: data, data: arguments[0] };
         }
 
         this._lastClearable = action === this._echoLine && String(data);
@@ -944,12 +1002,18 @@ var CommandLine = Module("commandline", {
         save: function save() {
             if (events.feedingKeys)
                 return;
+
             let str = this.input.value;
             if (/^\s*$/.test(str))
                 return;
+
+            let privateData = this.checkPrivate(str);
+            if (privateData == "never-save")
+                return;
+
             this.store = this.store.filter(function (line) (line.value || line) != str);
             dactyl.trapErrors(function () {
-                this.store.push({ value: str, timestamp: Date.now()*1000, privateData: this.checkPrivate(str) });
+                this.store.push({ value: str, timestamp: Date.now()*1000, privateData: privateData });
             }, this);
             this.store = this.store.slice(Math.max(0, this.store.length - options["history"]));
         },
@@ -1404,8 +1468,8 @@ var CommandLine = Module("commandline", {
             substring = substring.substr(value.length);
             this.removeSubstring = substring;
 
-            let node = DOM.fromXML(<span highlight="Preview">{substring}</span>,
-                                   document);
+            let node = DOM.fromJSON(["span", { highlight: "Preview" }, substring],
+                                    document);
 
             this.withSavedValues(["caret"], function () {
                 this.editor.insertNode(node, this.editor.rootElement, 1);
@@ -1593,7 +1657,7 @@ var CommandLine = Module("commandline", {
         return arg;
     }
 }, {
-    commands: function init_commands() {
+    commands: function initCommands() {
         [
             {
                 name: "ec[ho]",
@@ -1631,10 +1695,10 @@ var CommandLine = Module("commandline", {
                     commandline.echo(message.message, message.highlight, commandline.FORCE_SINGLELINE);
                 }
                 else if (commandline._messageHistory.length > 1) {
-                    XML.ignoreWhitespace = false;
                     commandline.commandOutput(
                         template.map(commandline._messageHistory.messages, function (message)
-                            <div highlight={message.highlight + " Message"}>{message.message}</div>));
+                           ["div", { highlight: message.highlight + " Message" },
+                               message.message]));
                 }
             },
             { argCount: "0" });
@@ -1679,7 +1743,7 @@ var CommandLine = Module("commandline", {
             bases: [modes.INSERT]
         });
     },
-    mappings: function init_mappings() {
+    mappings: function initMappings() {
 
         mappings.add([modes.COMMAND],
             [":"], "Enter Command Line mode",
@@ -1809,7 +1873,7 @@ var CommandLine = Module("commandline", {
         bind(["<C-]>", "<C-5>"], "Expand command line abbreviation",
              function () { editor.expandAbbreviation(modes.COMMAND_LINE); });
     },
-    options: function init_options() {
+    options: function initOptions() {
         options.add(["history", "hi"],
             "Number of Ex commands and search patterns to store in the command-line history",
             "number", 500,
@@ -1825,7 +1889,7 @@ var CommandLine = Module("commandline", {
             "number", 100,
             { validator: function (value) value >= 0 });
     },
-    sanitizer: function init_sanitizer() {
+    sanitizer: function initSanitizer() {
         sanitizer.addItem("commandline", {
             description: "Command-line and search history",
             persistent: true,
@@ -1896,21 +1960,21 @@ var ItemList = Class("ItemList", {
         DOM(this.win).resize(this._onResize.closure.tell);
     },
 
-    get rootXML() <e4x>
-        <div highlight="Normal" style="white-space: nowrap" key="root">
-            <div key="wrapper">
-                <div highlight="Completions" key="noCompletions"><span highlight="Title">{_("completion.noCompletions")}</span></div>
-                <div key="completions"/>
-            </div>
+    get rootXML()
+        ["div", { highlight: "Normal", style: "white-space: nowrap", key: "root" },
+            ["div", { key: "wrapper" },
+                ["div", { highlight: "Completions", key: "noCompletions" },
+                    ["span", { highlight: "Title" },
+                        _("completion.noCompletions")]],
+                ["div", { key: "completions" }]],
 
-            <div highlight="Completions">{
-            template.map(util.range(0, options["maxitems"] * 2), function (i)
-                <div highlight="CompItem NonText"><li>~</li></div>)
-            }</div>
-        </div>
-    </e4x>.elements(),
+            ["div", { highlight: "Completions" },
+                template.map(util.range(0, options["maxitems"] * 2), function (i)
+                    ["div", { highlight: "CompItem NonText" },
+                        "~"])]],
 
-    get itemCount() this.context.contextList.reduce(function (acc, ctxt) acc + ctxt.items.length, 0),
+    get itemCount() this.context.contextList
+                        .reduce(function (acc, ctxt) acc + ctxt.items.length, 0),
 
     get visible() !this.container.collapsed,
     set visible(val) this.container.collapsed = !val,
@@ -2180,9 +2244,11 @@ var ItemList = Class("ItemList", {
                 let off = group.getOffset(idx);
 
                 start = Math.constrain(start,
-                                       off + Math.min(this.CONTEXT_LINES, group.itemCount - idx + group.offsets.end)
+                                       off + Math.min(this.CONTEXT_LINES,
+                                                      group.itemCount - idx + group.offsets.end)
                                            - this.maxItems + 1,
-                                       off - Math.min(this.CONTEXT_LINES, idx + group.offsets.start));
+                                       off - Math.min(this.CONTEXT_LINES,
+                                                      idx + group.offsets.start));
             }
 
             let count = this.maxItems;
@@ -2232,21 +2298,19 @@ var ItemList = Class("ItemList", {
         },
 
         get rootXML()
-            <div key="root" highlight="CompGroup">
-                <div highlight="Completions">
-                    { this.context.createRow(this.context.title || [], "CompTitle") }
-                </div>
-                <div highlight="CompTitleSep"/>
-                <div key="contents">
-                    <div key="up" highlight="CompLess"/>
-                    <div key="message" highlight="CompMsg">{this.context.message}</div>
-                    <div key="itemsContainer" class="completion-items-container">
-                        <div key="items" highlight="Completions"/>
-                    </div>
-                    <div key="waiting" highlight="CompMsg">{ItemList.WAITING_MESSAGE}</div>
-                    <div key="down" highlight="CompMore"/>
-                </div>
-            </div>,
+            ["div", { key: "root", highlight: "CompGroup" },
+                ["div", { highlight: "Completions" },
+                    this.context.createRow(this.context.title || [], "CompTitle")],
+                ["div", { highlight: "CompTitleSep" }],
+                ["div", { key: "contents" },
+                    ["div", { key: "up", highlight: "CompLess" }],
+                    ["div", { key: "message", highlight: "CompMsg" },
+                        this.context.message || []],
+                    ["div", { key: "itemsContainer", class: "completion-items-container" },
+                        ["div", { key: "items", highlight: "Completions" }]],
+                    ["div", { key: "waiting", highlight: "CompMsg" },
+                        ItemList.WAITING_MESSAGE],
+                    ["div", { key: "down", highlight: "CompMore" }]]],
 
         get doc() this.parent.doc,
         get win() this.parent.win,
@@ -2288,7 +2352,7 @@ var ItemList = Class("ItemList", {
             this.nodes = {};
             this.generatedRange = ItemList.Range(0, 0);
 
-            DOM.fromXML(this.rootXML, this.doc, this.nodes);
+            DOM.fromJSON(this.rootXML, this.doc, this.nodes);
         },
 
         /**
@@ -2299,9 +2363,10 @@ var ItemList = Class("ItemList", {
             DOM(this.nodes.items).empty();
 
             if (this.context.message)
-                DOM(this.nodes.message).empty().append(<>{this.context.message}</>);
+                DOM(this.nodes.message).empty()
+                    .append(DOM.fromJSON(this.context.message, this.doc));
 
-            if (!this.selectedIdx > this.itemCount)
+            if (this.selectedIdx > this.itemCount)
                 this.selectedIdx = null;
         },