From da34b3dc816920b2840b87a27e04f59b35224a7c Mon Sep 17 00:00:00 2001 From: Martin Stubenschrott Date: Mon, 19 Nov 2007 22:46:55 +0000 Subject: [PATCH] * io.getFile() works for relative filenames now * completion functions must return the start point now from which position of the "filter" string they return the results --- content/buffers.js | 6 +- content/commands.js | 24 ++--- content/completion.js | 222 +++++++++++++++++++++++------------------- content/io.js | 22 +++-- 4 files changed, 149 insertions(+), 125 deletions(-) diff --git a/content/buffers.js b/content/buffers.js index 28de7a59..4a17bc36 100644 --- a/content/buffers.js +++ b/content/buffers.js @@ -260,7 +260,7 @@ vimperator.Buffer = function () //{{{ } else { - var items = vimperator.completion.buffer(""); + var items = vimperator.completion.buffers("")[1]; vimperator.bufferwindow.show(items); vimperator.bufferwindow.selectItem(getBrowser().mTabContainer.selectedIndex); } @@ -268,7 +268,7 @@ vimperator.Buffer = function () //{{{ else { // TODO: move this to vimperator.buffers.get() - var items = vimperator.completion.buffer(""); + var items = vimperator.completion.buffers("")[1]; var number, indicator, title, url; var list = ":" + vimperator.util.escapeHTML(vimperator.commandline.getCommand()) + "
" + ""; @@ -451,7 +451,7 @@ vimperator.Buffer = function () //{{{ if (!vimperator.bufferwindow.visible()) return false; - var items = vimperator.completion.buffer(""); + var items = vimperator.completion.buffers("")[1]; vimperator.bufferwindow.show(items); vimperator.bufferwindow.selectItem(getBrowser().mTabContainer.selectedIndex); }, diff --git a/content/commands.js b/content/commands.js index 5773c4d2..7f38269b 100644 --- a/content/commands.js +++ b/content/commands.js @@ -565,7 +565,7 @@ vimperator.Commands = function () //{{{ if (vimperator.completion.match([url, title], filter, false)) completions.push([url, title]); } - return completions; + return [0, completions]; } } )); @@ -639,7 +639,7 @@ vimperator.Commands = function () //{{{ "The special version :bmarks! opens the default Firefox bookmarks window.
" + "Filter can also contain the following options:
" + "-tags=comma,separated,tag,list
", - completer: function (filter) { return vimperator.bookmarks.get(filter); }, + completer: function (filter) { return [0, vimperator.bookmarks.get(filter)]; }, args: [[["-tags", "-T"], OPTION_LIST]] } )); @@ -653,7 +653,7 @@ vimperator.Commands = function () //{{{ "it is selected. With [!] the next buffer matching the argument " + "is selected, even if it cannot be identified uniquely.
" + "Use b as a shortcut to open this prompt.", - completer: function (filter) { return vimperator.completion.buffer(filter); } + completer: function (filter) { return vimperator.completion.buffers(filter); } } )); commandManager.add(new vimperator.Command(["dia[log]"], @@ -684,8 +684,8 @@ vimperator.Commands = function () //{{{ case "savepage": saveDocument(window.content.document); break; case "searchengines": openDialog("chrome://browser/content/search/engineManager.xul", "_blank", "chrome,dialog,modal,centerscreen"); break; // TODO add viewPartialSource('selection'); ... - case "": vimperator.echoerr("E474: Invalid Argument"); break; - default: vimperator.echoerr("Dialog: '" + args + "' is not available"); + case "": vimperator.echoerr("E474: Invalid argument"); break; + default: vimperator.echoerr("Dialog '" + args + "' is not available"); } }, { @@ -731,7 +731,7 @@ vimperator.Commands = function () //{{{ "Use <Tab> key on a string to complete the URL which you want to delete.
" + "The following options WILL be interpreted in the future:
" + " [!] a special version to delete ALL bookmarks
", - completer: function (filter) { return vimperator.bookmarks.get(filter); } + completer: function (filter) { return [0, vimperator.bookmarks.get(filter)]; } } )); commandManager.add(new vimperator.Command(["com[mand]"], @@ -964,7 +964,7 @@ vimperator.Commands = function () //{{{ if (vimperator.completion.match([url, title], filter, false)) completions.push([url, title]); } - return completions; + return [0, completions]; } } )); @@ -998,7 +998,7 @@ vimperator.Commands = function () //{{{ shortHelp: "Show recently visited URLs", help: "Open the message window at the bottom of the screen with all history items which match [filter] either in the title or URL.
" + "The special version :history! opens the default Firefox history window.", - completer: function (filter) { return vimperator.history.get(filter); } + completer: function (filter) { return [0, vimperator.history.get(filter)]; } } )); commandManager.add(new vimperator.Command(["javas[cript]", "js"], @@ -1856,7 +1856,7 @@ vimperator.Commands = function () //{{{ ":set option+={value}, :set option^={value} and :set option-={value} " + "adds/multiplies/subtracts {value} from a number option and appends/prepends/removes {value} from a string option.
" + ":set all shows the current value of all options and :set all& resets all options to their default values.
", - completer: function (filter) { return vimperator.completion.option(filter); } + completer: function (filter) { return vimperator.completion.options(filter); } } )); // TODO: sclose instead? @@ -1937,7 +1937,7 @@ vimperator.Commands = function () //{{{ "The .vimperatorrc file in your home directory and any files in ~/.vimperator/plugin/ are always sourced at startup.
" + "~ is supported as a shortcut for the $HOME directory.
" + "If ! is specified, errors are not printed.", - completer: function (filter) { return vimperator.completion.file(filter); } + completer: function (filter) { return vimperator.completion.file(filter, true); } } )); commandManager.add(new vimperator.Command(["st[op]"], @@ -1955,7 +1955,7 @@ vimperator.Commands = function () //{{{ help: "Works only for commands that support it, currently:" + "", - completer: function (filter) { return vimperator.completion.command(filter); } + completer: function (filter) { return vimperator.completion.commands(filter); } } )); commandManager.add(new vimperator.Command(["tabl[ast]"], @@ -2181,7 +2181,7 @@ vimperator.Commands = function () //{{{ if (vimperator.completion.match([url, title], filter, false)) completions.push([url, title]); } - return completions; + return [0, completions]; } } )); diff --git a/content/completion.js b/content/completion.js index 3b485019..12dcb6e2 100644 --- a/content/completion.js +++ b/content/completion.js @@ -134,40 +134,41 @@ vimperator.Completion = function () //{{{ { substrings = []; var nodes = [ - ["about", "About Firefox"], - ["addbookmark", "Add bookmarks for the current page"], - ["addons", "Manage Add-ons"], - ["bookmarks", "List your bookmarks"], - ["console", "JavaScript console"], - ["customizetoolbar", "Customize the Toolbar"], - ["downloads", "Manage Downloads"], - ["history", "List your history"], - ["import", "Import Preferences, Bookmarks, History, etc. from other browsers"], - ["openfile", "Open the file selector dialog"], - ["pageinfo", "Show information about the current page"], - ["pagesource", "View page source"], - ["places", "Places Organizer: Manage your bookmarks and history"], - ["preferences", "Show Firefox preferences dialog"], - ["printpreview", "Preview the page before printing"], - ["printsetup", "Setup the page size and orientation before printing"], - ["print", "Show print dialog"], - ["saveframe", "Save frame to disk"], - ["savepage", "Save page to disk"], - ["searchengines", "Manage installed search engines"]] + ["about", "About Firefox"], + ["addbookmark", "Add bookmarks for the current page"], + ["addons", "Manage Add-ons"], + ["bookmarks", "List your bookmarks"], + ["console", "JavaScript console"], + ["customizetoolbar", "Customize the Toolbar"], + ["downloads", "Manage Downloads"], + ["history", "List your history"], + ["import", "Import Preferences, Bookmarks, History, etc. from other browsers"], + ["openfile", "Open the file selector dialog"], + ["pageinfo", "Show information about the current page"], + ["pagesource", "View page source"], + ["places", "Places Organizer: Manage your bookmarks and history"], + ["preferences", "Show Firefox preferences dialog"], + ["printpreview", "Preview the page before printing"], + ["printsetup", "Setup the page size and orientation before printing"], + ["print", "Show print dialog"], + ["saveframe", "Save frame to disk"], + ["savepage", "Save page to disk"], + ["searchengines", "Manage installed search engines"] + ]; if (!filter) - return nodes; + return [0, nodes]; var mapped = nodes.map(function (node) { return [[node[0]], node[1]]; }); - return buildLongestCommonSubstring(mapped, filter); + return [0, buildLongestCommonSubstring(mapped, filter)]; }, // filter a list of urls // - // may consist of searchengines, filenames, bookmarks and history, + // may consist of search engines, filenames, bookmarks and history, // depending on the 'complete' option // if the 'complete' argument is passed like "h", it temporarily overrides the complete option url: function (filter, complete) @@ -175,58 +176,68 @@ vimperator.Completion = function () //{{{ var completions = []; substrings = []; + var start = 0; + var skip = filter.match(/^(.*,\s+)(.*)/); // start after the last ", " + if (skip) + { + start += skip[1].length; + filter = skip[2]; + } + var cpt = complete || vimperator.options["complete"]; // join all completion arrays together for (var i = 0; i < cpt.length; i++) { if (cpt[i] == "s") - completions = completions.concat(this.search(filter)); + completions = completions.concat(this.search(filter)[1]); else if (cpt[i] == "b") completions = completions.concat(vimperator.bookmarks.get(filter)); else if (cpt[i] == "h") completions = completions.concat(vimperator.history.get(filter)); else if (cpt[i] == "f") - completions = completions.concat(this.file(filter, true)); + completions = completions.concat(this.file(filter, false)[1]); } - return completions; + return [start, completions]; }, search: function (filter) { var engines = vimperator.bookmarks.getSearchEngines().concat(vimperator.bookmarks.getKeywords()); - if (!filter) return engines.map(function (engine) { - return [engine[0], engine[1]]; - }); + if (!filter) + return [0, engines]; + var mapped = engines.map(function (engine) { return [[engine[0]], engine[1]]; }); - return buildLongestCommonSubstring(mapped, filter); + return [0, buildLongestCommonSubstring(mapped, filter)]; }, // TODO: support file:// and \ or / path separators on both platforms - file: function (filter) + // TODO: sort directories first + // if "short" is true, only return names without any directory components + file: function (filter, short) { // this is now also used as part of the url completion, so the // substrings shouldn't be cleared for that case if (!arguments[1]) substrings = []; - var matches = filter.match(/^(.*[\/\\])(.*?)$/); - var dir; - - if (!matches || !(dir = matches[1])) - return []; - - var compl = matches[2] || ""; - + var dir = "", compl = ""; + var matches = filter.match(/^(.*[\/\\])?(.*?)$/); + if (matches) + { + dir = matches[1] || ""; // "" is expanded inside readDirectory to the current dir + compl = matches[2] || ""; + } var files = [], mapped = []; + try { files = vimperator.io.readDirectory(dir); mapped = files.map(function (file) { - return [[file.path], file.isDirectory() ? "Directory" : "File"]; + return [[short ? file.leafName : dir + file.leafName], file.isDirectory() ? "Directory" : "File"]; }); } catch (e) @@ -234,21 +245,20 @@ vimperator.Completion = function () //{{{ return []; } - - return buildLongestStartingSubstring(mapped, filter); + return [short ? dir.length : 0, buildLongestStartingSubstring(mapped, compl)]; }, help: function (filter) { var helpArray = [[["introduction"], "Introductory text"], - [["initialization"], "Initialization and startup"], - [["mappings"], "Normal mode commands"], - [["commands"], "Ex commands"], - [["options"], "Configuration options"]]; // TODO: hardcoded until we have proper 'pages' + [["initialization"], "Initialization and startup"], + [["mappings"], "Normal mode commands"], + [["commands"], "Ex commands"], + [["options"], "Configuration options"]]; // TODO: hardcoded until we have proper 'pages' substrings = []; for (var command in vimperator.commands) helpArray.push([command.longNames.map(function ($_) { return ":" + $_; }), command.shortHelp]); - options = this.option(filter, true); + options = this.options(filter, true); helpArray = helpArray.concat(options.map(function ($_) { return [ $_[0].map(function ($_) { return "'" + $_ + "'"; }), @@ -258,14 +268,14 @@ vimperator.Completion = function () //{{{ for (var map in vimperator.mappings) helpArray.push([map.names, map.shortHelp]); - if (!filter) return helpArray.map(function ($_) { - return [$_[0][0], $_[1]]; // unfiltered, use the first command - }); + // unfiltered, use the first command + if (!filter) + return [0, helpArray.map(function ($_) { return [$_[0][0], $_[1]]; })]; - return buildLongestCommonSubstring(helpArray, filter); + return [0, buildLongestCommonSubstring(helpArray, filter)]; }, - command: function (filter) + commands: function (filter) { substrings = []; var completions = []; @@ -273,15 +283,16 @@ vimperator.Completion = function () //{{{ { for (var command in vimperator.commands) completions.push([command.name, command.shortHelp]); - return completions; + return [0, completions]; } for (var command in vimperator.commands) completions.push([command.longNames, command.shortHelp]); - return buildLongestStartingSubstring(completions, filter); + + return [0, buildLongestStartingSubstring(completions, filter)]; }, - option: function (filter, unfiltered) + options: function (filter, unfiltered) { substrings = []; var optionCompletions = []; @@ -290,6 +301,8 @@ vimperator.Completion = function () //{{{ if (prefix) filter = filter.replace(prefix, ""); + // needed for help-completions, don't return [start, options], just options + // FIXME: doesn't belong here to be honest (rather v.options.get(filter)) --mst if (unfiltered) { var options = []; @@ -311,7 +324,7 @@ vimperator.Completion = function () //{{{ continue; options.push([prefix + option.name, option.shortHelp]); } - return options; + return [0, options]; } // check if filter ends with =, then complete current value else if (filter.length > 0 && filter.lastIndexOf("=") == filter.length - 1) @@ -320,12 +333,9 @@ vimperator.Completion = function () //{{{ for (var option in vimperator.options) { if (option.hasName(filter)) - { - optionCompletions.push([filter + "=" + option.value, ""]); - return optionCompletions; - } + return [filter.length + 1, [[option.value, ""]]]; } - return optionCompletions; + return [0, optionCompletions]; } // can't use b_l_s_s, since this has special requirements (the prefix) @@ -357,10 +367,11 @@ vimperator.Completion = function () //{{{ } } - return optionCompletions; + return [0, optionCompletions]; }, - buffer: function (filter) + // FIXME: items shouldn't be [[[a], b]], but [[a, b]] and only mapped if at all for bLCS --mst + buffers: function (filter) { substrings = []; var items = []; @@ -390,10 +401,11 @@ vimperator.Completion = function () //{{{ items.push([[(i + 1) + ": " + title, (i + 1) + ": " + url], url]); } } - if (!filter) return items.map(function ($_) { - return [$_[0][0], $_[1]]; - }); - return buildLongestCommonSubstring(items, filter); + + if (!filter) + return [0, items.map(function ($_) { return [$_[0][0], $_[1]]; })]; + + return [0, buildLongestCommonSubstring(items, filter)]; }, sidebar: function (filter) @@ -406,13 +418,13 @@ vimperator.Completion = function () //{{{ nodes.push([menu.childNodes[i].label, ""]); if (!filter) - return nodes; + return [0, nodes]; var mapped = nodes.map(function (node) { return [[node[0]], node[1]]; }); - return buildLongestCommonSubstring(mapped, filter); + return [0, buildLongestCommonSubstring(mapped, filter)]; }, javascript: function (str) @@ -422,6 +434,9 @@ vimperator.Completion = function () //{{{ var objects = []; var filter = matches[3] || ""; var start = matches[1].length - 1; + var offset = matches[1] ? matches[1].length : 0; + offset += matches[2] ? matches[2].length : 0; + if (matches[2]) { var brackets = 0, parentheses = 0; @@ -488,7 +503,7 @@ vimperator.Completion = function () //{{{ completions = []; } - return buildLongestStartingSubstring(completions, filter); + return [offset, buildLongestStartingSubstring(completions, filter)]; }, // discard all entries in the 'urls' array, which don't match 'filter @@ -523,8 +538,8 @@ vimperator.Completion = function () //{{{ } // Longest Common Subsequence - // This shouldn't use buildLongestCommonSubstring - // for performance reasons, so as not to cycle through the urls twice + // This shouldn't use buildLongestCommonSubstring for performance + // reasons, so as not to cycle through the urls twice outer: for (var i = 0; i < urls.length; i++) { @@ -609,51 +624,54 @@ vimperator.Completion = function () //{{{ }, // FIXME: rename + // TODO: get completions for "nested" command lines like ":time :js " or ":tab :he" exTabCompletion: function (str) { var [count, cmd, special, args] = vimperator.commands.parseCommand(str); var completions = []; var start = 0; + var exLength = 0; // if there is no space between the command name and the cursor // then get completions of the command name var matches = str.match(/^(:*\d*)\w*$/); if (matches) + return [matches[1].length, this.commands(cmd)[1]]; + + // dynamically get completions as specified with the command's completer function + var command = vimperator.commands.get(cmd); + if (command && command.completer) { - completions = this.command(cmd); - start = matches[1].length; - } - else // dynamically get completions as specified with the command's completer function - { - var command = vimperator.commands.get(cmd); - if (command && command.completer) - { - matches = str.match(/^:*\d*\w+!?\s+/); - start = matches ? matches[0].length : 0; + matches = str.match(/^:*\d*\w+!?\s+/); + exLength = matches ? matches[0].length : 0; - // TODO: maybe we should move these checks to the complete functions - if (command.hasName("open") || command.hasName("tabopen") || command.hasName("winopen")) - { - var skip = args.match(/^(.*,\s+)(.*)/); // start after the last ", " - if (skip) - { - start += skip[1].length; - args = skip[2]; - } - } - else if (command.hasName("echo") || command.hasName("echoerr") || command.hasName("javascript")) - { - var skip = args.match(/^(.*?)(\w*)$/); // start at beginning of the last word - if (skip) - start += skip[1].length; - } + // // TODO: maybe we should move these checks to the complete functions + // if (command.hasName("open") || command.hasName("tabopen") || command.hasName("winopen")) + // { + // var skip = args.match(/^(.*,\s+)(.*)/); // start after the last ", " + // if (skip) + // { + // start += skip[1].length; + // args = skip[2]; + // } + // } + // else if (command.hasName("echo") || command.hasName("echoerr") || command.hasName("javascript")) + // { + // var skip = args.match(/^(.*?)(\w*)$/); // start at beginning of the last word + // if (skip) + // start += skip[1].length; + // } + // else if (command.hasName("source")) + // { + // var skip = args.match(/^(.*?)(\w*)$/); // start at beginning of the last word + // if (skip) + // start += skip[1].length; + // } - completions = command.completer.call(this, args); - } + [start, completions] = command.completer.call(this, args); } - return [start, completions]; + return [exLength + start, completions]; } - }; //}}} }; //}}} diff --git a/content/io.js b/content/io.js index dab6f4ca..af8b0823 100644 --- a/content/io.js +++ b/content/io.js @@ -36,6 +36,8 @@ vimperator.IO = function ()//{{{ var environmentService = Components.classes["@mozilla.org/process/environment;1"] .getService(Components.interfaces.nsIEnvironment); + const WINDOWS = navigator.platform == "Win32"; + /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ @@ -53,8 +55,6 @@ vimperator.IO = function ()//{{{ expandPath: function (path) { - const WINDOWS = navigator.platform == "Win32"; - // TODO: proper pathname separator translation like Vim if (WINDOWS) path = path.replace("/", "\\", "g"); @@ -96,7 +96,7 @@ vimperator.IO = function ()//{{{ { var pluginDir; - if (navigator.platform == "Win32") + if (WINDOWS) pluginDir = "~/vimperator/plugin"; else pluginDir = "~/.vimperator/plugin"; @@ -108,10 +108,10 @@ vimperator.IO = function ()//{{{ getRCFile: function () { - var rcFile1 = this.getFile(this.expandPath("~/.vimperatorrc")); - var rcFile2 = this.getFile(this.expandPath("~/_vimperatorrc")); + var rcFile1 = this.getFile("~/.vimperatorrc"); + var rcFile2 = this.getFile("~/_vimperatorrc"); - if (navigator.platform == "Win32") + if (WINDOWS) [rcFile1, rcFile2] = [rcFile2, rcFile1] if (rcFile1.exists() && rcFile1.isFile()) @@ -124,12 +124,18 @@ vimperator.IO = function ()//{{{ // return a nsILocalFile for path where you can call isDirectory(), etc. on // caller must check with .exists() if the returned file really exists + // also expands relative paths getFile: function (path) { var file = Components.classes["@mozilla.org/file/local;1"]. createInstance(Components.interfaces.nsILocalFile); - file.initWithPath(this.expandPath(path)); + // convert relative to absolute pathnames + path = this.expandPath(path); + if (!/^([a-zA-Z]+:|\/)/.test(path)) // starts not with either /, C: or file: + path = this.expandPath("~") + (WINDOWS ? "\\" : "/") + path; // TODO: for now homedir, later relative to current dir? + + file.initWithPath(path); return file; }, @@ -139,7 +145,7 @@ vimperator.IO = function ()//{{{ { var file = Components.classes["@mozilla.org/file/local;1"]. createInstance(Components.interfaces.nsILocalFile); - if (navigator.platform == "Win32") + if (WINDOWS) { var dir = environmentService.get("TMP") || environmentService.get("TEMP") || "C:\\"; file.initWithPath(dir + "\\vimperator.tmp");