diff --git a/common/content/buffer.js b/common/content/buffer.js index 5154d7f6..deab343d 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -964,33 +964,31 @@ const Buffer = Module("buffer", { let doc = buffer.focusedFrame.document; if (isArray(url)) { - let chrome = "chrome://global/content/viewSource.xul"; - window.openDialog(chrome, "_blank", "all,dialog=no", - url[0], null, null, url[1]); - /* FIXME - let win = dactyl.open(chrome)[0]; - while (win.document.documentURI != chrome) - util.threadYield(false, true); - win.arguments = [url[0], null, null, url[1]]; - */ - return; + if (options.get("editor").has("l")) + this.viewSourceExternally(url[0] || doc, url[1]); + else { + let chrome = "chrome://global/content/viewSource.xul"; + window.openDialog(chrome, "_blank", "all,dialog=no", + url[0], null, null, url[1]); + } } - - if (useExternalEditor) - this.viewSourceExternally(url || doc); else { - url = url || doc.location.href; - const PREFIX = "view-source:"; - if (url.indexOf(PREFIX) == 0) - url = url.substr(PREFIX.length); - else - url = PREFIX + url; + if (useExternalEditor) + this.viewSourceExternally(url || doc); + else { + url = url || doc.location.href; + const PREFIX = "view-source:"; + if (url.indexOf(PREFIX) == 0) + url = url.substr(PREFIX.length); + else + url = PREFIX + url; - let sh = history.session; - if (sh[sh.index].URI.spec == url) - window.getWebNavigation().gotoIndex(sh.index); - else - dactyl.open(url, { hide: true }); + let sh = history.session; + if (sh[sh.index].URI.spec == url) + window.getWebNavigation().gotoIndex(sh.index); + else + dactyl.open(url, { hide: true }); + } } }, @@ -1011,8 +1009,8 @@ const Buffer = Module("buffer", { viewSourceExternally: Class("viewSourceExternally", XPCOM([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), { init: function (doc, callback) { - this.callback = callback || - function (file) editor.editFileExternally(file.path); + this.callback = callable(callback) ? callback : + function (file) editor.editFileExternally(file.path, callback); let url = isString(doc) ? doc : doc.location.href; let uri = util.newURI(url, charset); diff --git a/common/content/editor.js b/common/content/editor.js index 6e307b14..fbc79505 100644 --- a/common/content/editor.js +++ b/common/content/editor.js @@ -241,13 +241,11 @@ const Editor = Module("editor", { return -1; }, - editFileExternally: function (path) { - // TODO: save return value in v:shell_error - let args = commands.parseArgs(options["editor"], { argCount: "*", allowUnknownOptions: true }); + editFileExternally: function (path, line, column) { + let args = options.get("editor").format({ f: path, l: line, c: column }); dactyl.assert(args.length >= 1, "No editor specified"); - args.push(path); io.run(io.expandPath(args.shift()), args, true); }, @@ -257,6 +255,7 @@ const Editor = Module("editor", { return; let textBox = config.isComposeWindow ? null : dactyl.focus; + 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]): ", @@ -267,8 +266,12 @@ const Editor = Module("editor", { return; } - if (textBox) + if (textBox) { var text = textBox.value; + let pre = text.substr(0, textBox.selectionStart); + line = 1 + pre.replace(/[^\n]/g, "").length; + column = 1 + pre.replace(/[^]*\n/, "").length; + } else { var editor = window.GetCurrentEditor ? GetCurrentEditor() : Editor.getEditor(document.commandDispatcher.focusedWindow); @@ -310,7 +313,7 @@ const Editor = Module("editor", { timer.initWithCallback({ notify: update }, 100, timer.TYPE_REPEATING_SLACK); try { - this.editFileExternally(tmpfile.path); + this.editFileExternally(tmpfile.path, line, column); } finally { timer.cancel(); @@ -770,7 +773,21 @@ const Editor = Module("editor", { options: function () { options.add(["editor"], "Set the external text editor", - "string", "gvim -f"); + "string", "gvim -f +%l %f", { + format: function (obj, value) { + let args = commands.parseArgs(value || this.value, { argCount: "*", allowUnknownOptions: true }) + .map(util.compileFormat).filter(function (fmt) fmt.valid(obj)) + .map(function (fmt) fmt(obj)); + if (obj["f"] && !this.has("f")) + args.push(obj["f"]); + return args; + }, + has: function (key) set.has(util.compileFormat(this.value).seen, key), + validator: function (value) { + this.format({}, value); + return Object.keys(util.compileFormat(value).seen).every(function (k) "cfl".indexOf(k) >= 0) + } + }); options.add(["insertmode", "im"], "Use Insert mode as the default for text areas", diff --git a/common/locale/en-US/options.xml b/common/locale/en-US/options.xml index 215bbd5e..91c1a7b1 100644 --- a/common/locale/en-US/options.xml +++ b/common/locale/en-US/options.xml @@ -69,6 +69,25 @@ +
+ Some options may be given format strings containing %-delimited escape + sequences. Character sequences in the form of %character are + substituted by a specified replacement string. If character is + upper-case, then the string is automatically + quoted. Any substring enclosed by %[ + and %] is automatically elided if any of the contained format + characters aren't currently valid. A literal % character may be + included with the special escape sequence %%. +
+ +
+ For example, given the format string
+ Accepts a