]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/content/editor.js
Import 1.0b7.1 supporting Firefox up to 8.*
[dactyl.git] / common / content / editor.js
index e1df65370adea22d02da9706ae7bccabcc88d41c..d2fe3224b9f063b010fe42eae926f1418a41df07 100644 (file)
@@ -1,3 +1,4 @@
+// Copyright (c) 2008-2011 Kris Maglione <maglione.k at Gmail>
 // Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
 //
 // This work is licensed for reuse under an MIT license. Details are
@@ -24,12 +25,6 @@ var Editor = Module("editor", {
     selectedText: function () String(Editor.getEditor(null).selection),
 
     pasteClipboard: function (clipboard, toStart) {
-        // TODO: I don't think this is needed anymore? --djk
-        if (util.OS.isWindows) {
-            this.executeCommand("cmd_paste");
-            return;
-        }
-
         let elem = dactyl.focusedElement;
         if (elem.inputField)
             elem = elem.inputField;
@@ -189,61 +184,52 @@ var Editor = Module("editor", {
         }
     },
 
-    // returns the position of char
-    findCharForward: function (ch, count) {
-        if (!Editor.getEditor())
+    findChar: function (key, count, backward) {
+
+        let editor = Editor.getEditor();
+        if (!editor)
             return -1;
 
-        let text = Editor.getEditor().value;
         // XXX
         if (count == null)
             count = 1;
 
-        for (let i = Editor.getEditor().selectionEnd + 1; i < text.length; i++) {
-            if (text[i] == "\n")
-                break;
-            if (text[i] == ch)
-                count--;
-            if (count == 0)
-                return i + 1; // always position the cursor after the char
-        }
+        let code = events.fromString(key)[0].charCode;
+        util.assert(code);
+        let char = String.fromCharCode(code);
 
-        dactyl.beep();
-        return -1;
-    },
-
-    // returns the position of char
-    findCharBackward: function (ch, count) {
-        if (!Editor.getEditor())
-            return -1;
-
-        let text = Editor.getEditor().value;
-        // XXX
-        if (count == null)
-            count = 1;
+        let text = editor.value;
+        let caret = editor.selectionEnd;
+        if (backward) {
+            let end = text.lastIndexOf("\n", caret);
+            while (caret > end && caret >= 0 && count--)
+                caret = text.lastIndexOf(char, caret - 1);
+        }
+        else {
+            let end = text.indexOf("\n", caret);
+            if (end == -1)
+                end = text.length;
 
-        for (let i = Editor.getEditor().selectionStart - 1; i >= 0; i--) {
-            if (text[i] == "\n")
-                break;
-            if (text[i] == ch)
-                count--;
-            if (count == 0)
-                return i;
+            while (caret < end && caret >= 0 && count--)
+                caret = text.indexOf(char, caret + 1);
         }
 
-        dactyl.beep();
-        return -1;
+        if (count > 0)
+            caret = -1;
+        if (caret == -1)
+            dactyl.beep();
+        return caret;
     },
 
     /**
      * Edits the given file in the external editor as specified by the
      * 'editor' option.
      *
-     * @param {object|File|string} args An object specifying the file,
-     *  line, and column to edit. If a non-object is specified, it is
-     *  treated as the file parameter of the object.
+     * @param {object|File|string} args An object specifying the file, line,
+     *     and column to edit. If a non-object is specified, it is treated as
+     *     the file parameter of the object.
      * @param {boolean} blocking If true, this function does not return
-     *  until the editor exits.
+     *     until the editor exits.
      */
     editFileExternally: function (args, blocking) {
         if (!isObject(args) || args instanceof File)
@@ -252,7 +238,7 @@ var Editor = Module("editor", {
 
         let args = options.get("editor").format(args);
 
-        dactyl.assert(args.length >= 1, _("editor.noEditor"));
+        dactyl.assert(args.length >= 1, _("option.notSet", "editor"));
 
         io.run(args.shift(), args, blocking);
     },
@@ -266,7 +252,7 @@ var Editor = Module("editor", {
         let line, column;
 
         if (!forceEditing && textBox && textBox.type == "password") {
-            commandline.input("Editing a password field externally will reveal the password. Would you like to continue? (yes/[no]): ",
+            commandline.input(_("editor.prompt.editPassword") + " ",
                 function (resp) {
                     if (resp && resp.match(/^y(es)?$/i))
                         editor.editFieldExternally(true);
@@ -281,10 +267,10 @@ var Editor = Module("editor", {
             column = 1 + pre.replace(/[^]*\n/, "").length;
         }
         else {
-            var editor = window.GetCurrentEditor ? GetCurrentEditor()
-                                                 : Editor.getEditor(document.commandDispatcher.focusedWindow);
-            dactyl.assert(editor);
-            text = Array.map(editor.rootElement.childNodes, function (e) util.domToString(e, true)).join("");
+            var editor_ = window.GetCurrentEditor ? GetCurrentEditor()
+                                                  : Editor.getEditor(document.commandDispatcher.focusedWindow);
+            dactyl.assert(editor_);
+            text = Array.map(editor_.rootElement.childNodes, function (e) util.domToString(e, true)).join("");
         }
 
         let origGroup = textBox && textBox.getAttributeNS(NS, "highlight") || "";
@@ -318,19 +304,25 @@ var Editor = Module("editor", {
             lastUpdate = Date.now();
 
             let val = tmpfile.read();
-            if (textBox)
+            if (textBox) {
                 textBox.value = val;
+
+                textBox.setAttributeNS(NS, "modifiable", true);
+                util.computedStyle(textBox).MozUserInput;
+                events.dispatch(textBox, events.create(textBox.ownerDocument, "input", {}));
+                textBox.removeAttributeNS(NS, "modifiable");
+            }
             else {
-                while (editor.rootElement.firstChild)
-                    editor.rootElement.removeChild(editor.rootElement.firstChild);
-                editor.rootElement.innerHTML = val;
+                while (editor_.rootElement.firstChild)
+                    editor_.rootElement.removeChild(editor_.rootElement.firstChild);
+                editor_.rootElement.innerHTML = val;
             }
         }
 
         try {
             var tmpfile = io.createTempFile();
             if (!tmpfile)
-                throw Error("Couldn't create temporary file");
+                throw Error(_("io.cantCreateTempFile"));
 
             if (textBox) {
                 highlight.highlightNode(textBox, origGroup + " EditorEditing");
@@ -338,8 +330,7 @@ var Editor = Module("editor", {
             }
 
             if (!tmpfile.write(text))
-                throw Error("Input contains characters not valid in the current " +
-                            "file encoding");
+                throw Error(_("io.cantEncode"));
 
             var lastUpdate = Date.now();
 
@@ -414,9 +405,9 @@ var Editor = Module("editor", {
             elem = dactyl.focusedElement || document.commandDispatcher.focusedWindow;
         dactyl.assert(elem);
 
-        if (elem instanceof Element)
-            return elem.QueryInterface(Ci.nsIDOMNSEditableElement).editor;
         try {
+            if (elem instanceof Element)
+                return elem.QueryInterface(Ci.nsIDOMNSEditableElement).editor;
             return elem.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
                        .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIEditingSession)
                        .getEditorForWindow(elem);
@@ -658,19 +649,28 @@ var Editor = Module("editor", {
         mappings.add([modes.INPUT],
             ["<C-t>"], "Edit text field in Vi mode",
             function () {
+                dactyl.assert(dactyl.focusedElement);
                 dactyl.assert(!editor.isTextEdit);
                 modes.push(modes.TEXT_EDIT);
             });
 
+        // Ugh.
+        mappings.add([modes.INPUT, modes.CARET],
+            ["<*-CR>", "<*-BS>", "<*-Del>", "<*-Left>", "<*-Right>", "<*-Up>", "<*-Down>",
+             "<*-Home>", "<*-End>", "<*-PageUp>", "<*-PageDown>",
+             "<M-c>", "<M-v>", "<*-Tab>"],
+            "Handled by " + config.host,
+            function () Events.PASS_THROUGH);
+
         mappings.add([modes.INSERT],
-            ["<Space>", "<Return>"], "Expand insert mode abbreviation",
+            ["<Space>", "<Return>"], "Expand Insert mode abbreviation",
             function () {
                 editor.expandAbbreviation(modes.INSERT);
-                return Events.PASS;
+                return Events.PASS_THROUGH;
             });
 
         mappings.add([modes.INSERT],
-            ["<C-]>", "<C-5>"], "Expand insert mode abbreviation",
+            ["<C-]>", "<C-5>"], "Expand Insert mode abbreviation",
             function () { editor.expandAbbreviation(modes.INSERT); });
 
         // text edit mode
@@ -723,15 +723,15 @@ var Editor = Module("editor", {
 
         // visual mode
         mappings.add([modes.CARET, modes.TEXT_EDIT],
-            ["v"], "Start visual mode",
+            ["v"], "Start Visual mode",
             function () { modes.push(modes.VISUAL); });
 
         mappings.add([modes.VISUAL],
-            ["v", "V"], "End visual mode",
+            ["v", "V"], "End Visual mode",
             function () { modes.pop(); });
 
         mappings.add([modes.TEXT_EDIT],
-            ["V"], "Start visual line mode",
+            ["V"], "Start Visual Line mode",
             function () {
                 modes.push(modes.VISUAL, modes.LINE);
                 editor.executeCommand("cmd_beginLine", 1);
@@ -777,7 +777,7 @@ var Editor = Module("editor", {
         mappings.add([modes.TEXT_EDIT, modes.VISUAL],
             ["f"], "Move to a character on the current line after the cursor",
             function ({ arg, count }) {
-                let pos = editor.findCharForward(arg, Math.max(count, 1));
+                let pos = editor.findChar(arg, Math.max(count, 1));
                 if (pos >= 0)
                     editor.moveToPosition(pos, true, modes.main == modes.VISUAL);
             },
@@ -786,7 +786,7 @@ var Editor = Module("editor", {
         mappings.add([modes.TEXT_EDIT, modes.VISUAL],
             ["F"], "Move to a character on the current line before the cursor",
             function ({ arg, count }) {
-                let pos = editor.findCharBackward(arg, Math.max(count, 1));
+                let pos = editor.findChar(arg, Math.max(count, 1), true);
                 if (pos >= 0)
                     editor.moveToPosition(pos, false, modes.main == modes.VISUAL);
             },
@@ -795,7 +795,7 @@ var Editor = Module("editor", {
         mappings.add([modes.TEXT_EDIT, modes.VISUAL],
             ["t"], "Move before a character on the current line",
             function ({ arg, count }) {
-                let pos = editor.findCharForward(arg, Math.max(count, 1));
+                let pos = editor.findChar(arg, Math.max(count, 1));
                 if (pos >= 0)
                     editor.moveToPosition(pos - 1, true, modes.main == modes.VISUAL);
             },
@@ -804,7 +804,7 @@ var Editor = Module("editor", {
         mappings.add([modes.TEXT_EDIT, modes.VISUAL],
             ["T"], "Move before a character on the current line, backwards",
             function ({ arg, count }) {
-                let pos = editor.findCharBackward(arg, Math.max(count, 1));
+                let pos = editor.findChar(arg, Math.max(count, 1), true);
                 if (pos >= 0)
                     editor.moveToPosition(pos + 1, false, modes.main == modes.VISUAL);
             },
@@ -837,10 +837,10 @@ var Editor = Module("editor", {
         function bind() mappings.add.apply(mappings,
                                            [[modes.AUTOCOMPLETE]].concat(Array.slice(arguments)))
 
-        bind(["<Esc>"], "Return to INSERT mode",
+        bind(["<Esc>"], "Return to Insert mode",
              function () Events.PASS_THROUGH);
 
-        bind(["<C-[>"], "Return to INSERT mode",
+        bind(["<C-[>"], "Return to Insert mode",
              function () { events.feedkeys("<Esc>", { skipmap: true }); });
 
         bind(["<Up>"], "Select the previous autocomplete result",
@@ -859,7 +859,7 @@ var Editor = Module("editor", {
     options: function () {
         options.add(["editor"],
             "The external text editor",
-            "string", "gvim -f +<line> <file>", {
+            "string", 'gvim -f +<line> +"sil! call cursor(0, <column>)" <file>', {
                 format: function (obj, value) {
                     let args = commands.parseArgs(value || this.value, { argCount: "*", allowUnknownOptions: true })
                                        .map(util.compileMacro).filter(function (fmt) fmt.valid(obj))
@@ -868,7 +868,7 @@ var Editor = Module("editor", {
                         args.push(obj["file"]);
                     return args;
                 },
-                has: function (key) set.has(util.compileMacro(this.value).seen, key),
+                has: function (key) Set.has(util.compileMacro(this.value).seen, key),
                 validator: function (value) {
                     this.format({}, value);
                     return Object.keys(util.compileMacro(value).seen).every(function (k) ["column", "file", "line"].indexOf(k) >= 0);