diff --git a/NEWS b/NEWS index 733dd040..78d23c0e 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ 2007-xx-xx: * version 0.6 * THIS VERSION ONLY WORKS WITH FIREFOX 3.0 + * you can edit textfields with Ctrl-i now using an external editor (thanks to Joseph Xu) * :open,:bmarks,etc. filter on space seperated tokens now, so you can search with :open linux windows <tab> all your bookmarks/history which contain linux AND windows in the url or title diff --git a/content/editor.js b/content/editor.js index 17c1dd9e..8901c540 100644 --- a/content/editor.js +++ b/content/editor.js @@ -300,7 +300,70 @@ function Editor() //{{{ vimperator.beep(); return -1; } - + + this.editWithExternalEditor = function() + { + function err(msg) + { + vimperator.callFunctionInThread(null, vimperator.echoerr, [msg]); + } + + var textBox = document.commandDispatcher.focusedElement; + var editor = vimperator.options["editor"]; + var args = []; + args = editor.split(" "); + if (args.length < 1) + { + err("no editor specified"); + return; + } + + try + { + var tmpfile = vimperator.io.createTempFile(); + } + catch (e) + { + err("Could not create temporary file: " + e.message); + return; + } + try + { + vimperator.io.writeFile(tmpfile, textBox.value); + } + catch (e) + { + err("Could not write to temporary file " + tmpfile.path + ": " + e.message); + return; + } + + var prog = args.shift(); + args.push(tmpfile.path) + + textBox.setAttribute("readonly", "true"); + var newThread = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0); + // TODO: save return value in v:shell_error + vimperator.callFunctionInThread(newThread, vimperator.run, [prog, args, true]); + textBox.removeAttribute("readonly"); + +// if (retcode != 0) +// { +// err("External editor returned with exit code " + retcode); +// } +// else + { + try + { + var val = vimperator.io.readFile(tmpfile); + textBox.value = val; + } + catch (e) + { + err("Could not read from temporary file " + tmpfile.path + ": " + e.message); + } + } + tmpfile.remove(false); + } } //}}} // vim: set fdm=marker sw=4 ts=4 et: diff --git a/content/mappings.js b/content/mappings.js index 3d9b044d..07cbd319 100644 --- a/content/mappings.js +++ b/content/mappings.js @@ -1920,6 +1920,10 @@ function Mappings() //{{{ function() { vimperator.editor.pasteClipboard(); }, { } )); + addDefaultMap(new Map([vimperator.modes.INSERT, vimperator.modes.TEXTAREA], [""], + function() { vimperator.editor.editWithExternalEditor(); }, + { } + )); //}}} diff --git a/content/options.js b/content/options.js index 9b133955..67645d0c 100644 --- a/content/options.js +++ b/content/options.js @@ -421,6 +421,16 @@ function Options() //{{{ default_value: "google" } )); + addOption(new Option(["editor"], "string", + { + short_help: "Set the external text editor", + help: "Sets the editor to run when <C-i> " + + "is pressed in INSERT and TEXTAREA modes. Note that vimperator will " + + "not behave correctly if the editor forks its own process, such as with "+ + "gvim without the -f argument.", + default_value: "gvim -f" + } + )); addOption(new Option(["extendedhinttags", "eht"], "string", { short_help: "XPath string of hintable elements activated by ';'", diff --git a/content/vimperator.js b/content/vimperator.js index 26b74766..14b3c418 100644 --- a/content/vimperator.js +++ b/content/vimperator.js @@ -697,13 +697,46 @@ const vimperator = (function() //{{{ get windows() { - var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator); + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); var wa = []; var enumerator = wm.getEnumerator("navigator:browser"); while (enumerator.hasMoreElements()) wa.push(enumerator.getNext()); return wa; + }, + + // be sure to call GUI related methods like alert() or dump() ONLY in the main thread + callFunctionInThread: function(thread, func, args) + { + function CallbackEvent (func, args) + { + if (!(args instanceof Array)) + args = []; + + return { + QueryInterface: function(iid) + { + if (iid.equals(Components.interfaces.nsIRunnable) || + iid.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + run: function() + { + func.apply(window, args); + } + } + } + + if (!thread) + thread = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0); + + // DISPATCH_SYNC is necessary, otherwise strange things will happen + thread.dispatch(new CallbackEvent(func, args), thread.DISPATCH_SYNC); } + } //}}} })(); //}}}