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:" +
"- :tab help
" +
"- :tab prefs[!]
",
- 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");