diff --git a/common/content/bookmarks.js b/common/content/bookmarks.js
index b4a01295..95663eb2 100644
--- a/common/content/bookmarks.js
+++ b/common/content/bookmarks.js
@@ -706,9 +706,9 @@ var Bookmarks = Module("bookmarks", {
});
};
- completion.addUrlCompleter("S", "Suggest engines", completion.searchEngineSuggest);
- completion.addUrlCompleter("b", "Bookmarks", completion.bookmark);
- completion.addUrlCompleter("s", "Search engines and keyword URLs", completion.search);
+ completion.addUrlCompleter("suggestion", "Suggest engines", completion.searchEngineSuggest);
+ completion.addUrlCompleter("bookmark", "Bookmarks", completion.bookmark);
+ completion.addUrlCompleter("search", "Search engines and keyword URLs", completion.search);
}
});
diff --git a/common/content/buffer.js b/common/content/buffer.js
index db647fb9..00c96972 100644
--- a/common/content/buffer.js
+++ b/common/content/buffer.js
@@ -1955,7 +1955,7 @@ var Buffer = Module("buffer", {
dactyl.assert(url, _("error.clipboardEmpty"));
let proto = /^([-\w]+):/.exec(url);
- if (proto && "@mozilla.org/network/protocol;1?name=" + proto[1] in Cc && !RegExp(options["urlseparator"]).test(url))
+ if (proto && services.PROTOCOL + proto[1] in Cc && !RegExp(options["urlseparator"]).test(url))
return url.replace(/\s+/g, "");
return url;
}
diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index be494453..f64a639f 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -1108,7 +1108,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
// If it starts with a valid protocol, pass it through.
let proto = /^([-\w]+):/.exec(url);
- if (proto && "@mozilla.org/network/protocol;1?name=" + proto[1] in Cc)
+ if (proto && services.PROTOCOL + proto[1] in Cc)
return url;
// Check for a matching search keyword.
diff --git a/common/content/history.js b/common/content/history.js
index 9dba789d..ff1781de 100644
--- a/common/content/history.js
+++ b/common/content/history.js
@@ -312,7 +312,7 @@ var History = Module("history", {
context.generate = function () history.get(context.filter, this.maxItems, sort);
};
- completion.addUrlCompleter("h", "History", completion.history);
+ completion.addUrlCompleter("history", "History", completion.history);
},
mappings: function () {
function bind() mappings.add.apply(mappings, [config.browserModes].concat(Array.slice(arguments)));
diff --git a/common/content/key-processors.js b/common/content/key-processors.js
index c90d1528..63b27938 100644
--- a/common/content/key-processors.js
+++ b/common/content/key-processors.js
@@ -279,9 +279,9 @@ var KeyProcessor = Class("KeyProcessor", {
return KeyArgProcessor(this, map, true, "motion");
return this.execute(map, {
- keyEvents: this.keyEvents,
command: this.command,
count: this.count,
+ keyEvents: events.keyEvents,
keypressEvents: this.events
});
}
@@ -313,7 +313,8 @@ var KeyArgProcessor = Class("KeyArgProcessor", KeyProcessor, {
let args = {
command: this.parent.command,
count: this.count || this.parent.count,
- events: this.parent.events.concat(this.events)
+ keyEvents: events.keyEvents,
+ keypressEvents: this.parent.events.concat(this.events)
};
args[this.argName] = this.command;
diff --git a/common/locale/en-US/messages.properties b/common/locale/en-US/messages.properties
index e144728f..b586ac05 100644
--- a/common/locale/en-US/messages.properties
+++ b/common/locale/en-US/messages.properties
@@ -37,6 +37,10 @@ autocmd.noMatching = No matching autocommands
autocmd.noGroup-1 = No such group or event: %S
autocmd.cantExecuteAll = Can't execute autocommands for ALL events
+autocomplete.description-1 = Native '%S' autocompletions
+autocomplete.noSuchProvider-1 = No such autocomplete provider '%S'
+autocomplete.title-1 = '%S'
+
bookmark.noMatching-2 = No bookmarks matching tags %S and string %S
bookmark.noMatchingTags-1 = No bookmarks matching tags %S
bookmark.noMatchingString-1 = No bookmarks matching string %S
diff --git a/common/locale/en-US/options.xml b/common/locale/en-US/options.xml
index 8f530c9c..465ed092 100644
--- a/common/locale/en-US/options.xml
+++ b/common/locale/en-US/options.xml
@@ -510,22 +510,35 @@
Items which are completed at the :open prompts. Available items:
- - s
- Search engines and keyword URLs
- - f
- Local files
- - l
- &dactyl.host; location bar entries (bookmarks and history sorted in an intelligent way)
- - b
- Bookmarks
- - h
- History
- - S
- Search engine suggestions
+ - search
- Search engines and keyword URLs
+ - file
- Local files
+ - location
- &dactyl.host; location bar entries (bookmarks and history sorted in an intelligent way)
+ - bookmark
- Bookmarks
+ - history
- History
+ - suggest
- Search engine suggestions
+
+ Additionally, native search providers can be added by prefixing
+ their names with the string native:. These
+ providers are often added by other add-ons and are occasionally
+ useful.
+
+
The order is important, such that bsf will
list bookmarks followed by matching quick searches and then
matching files.
+
+ For backward compatibility, this option currently accepts a single
+ entry containing single-letter names for completers. This usage
+ is deprecated and will be removed in the future.
+
+
- Using b and h can make completion very slow if
+ Using bookmark and history can make completion very slow if
there are many items.
diff --git a/common/modules/bookmarkcache.jsm b/common/modules/bookmarkcache.jsm
index 6f303226..8bf2ba16 100644
--- a/common/modules/bookmarkcache.jsm
+++ b/common/modules/bookmarkcache.jsm
@@ -10,7 +10,14 @@ defineModule("bookmarkcache", {
require: ["services", "storage", "util"]
}, this);
-function newURI(url, charset, base) services.io.newURI(url, charset, base);
+function newURI(url, charset, base) {
+ try {
+ return services.io.newURI(url, charset, base);
+ }
+ catch (e) {
+ throw Error(e);
+ }
+}
var Bookmark = Struct("url", "title", "icon", "post", "keyword", "tags", "charset", "id");
var Keyword = Struct("keyword", "title", "icon", "url");
diff --git a/common/modules/completion.jsm b/common/modules/completion.jsm
index f23f2330..cf52e7fb 100644
--- a/common/modules/completion.jsm
+++ b/common/modules/completion.jsm
@@ -211,6 +211,16 @@ var CompletionContext = Class("CompletionContext", {
return this;
},
+ __title: Class.Memoize(function () this._title.map(function (s)
+ typeof s == "string" ? messages.get("completion.title." + s, s)
+ : s)),
+
+ set title(val) {
+ delete this.__title;
+ return this._title = val;
+ },
+ get title() this.__title,
+
// Temporary
/**
* @property {Object}
@@ -631,7 +641,17 @@ var CompletionContext = Class("CompletionContext", {
start = Math.max(0, start || 0);
end = Math.min(items.length, end != null ? end : items.length);
for (let i in util.range(start, end, step))
- yield [i, cache[i] = cache[i] || util.xmlToDom(self.createRow(items[i]), doc)];
+ try {
+ yield [i, cache[i] = cache[i] || util.xmlToDom(self.createRow(items[i]), doc)];
+ }
+ catch (e) {
+ util.reportError(e);
+ yield [i, cache[i] = cache[i] || util.xmlToDom(
+
+
{items[i].text}
+ {e}
+ , doc)];
+ }
},
/**
@@ -849,6 +869,7 @@ var Completion = Module("completion", {
Local: function (dactyl, modules, window) ({
urlCompleters: {},
+ get modules() modules,
get options() modules.options,
// FIXME
@@ -920,8 +941,9 @@ var Completion = Module("completion", {
if (/^about:/.test(context.filter))
context.fork("about", 6, this, function (context) {
context.generate = function () {
- const PREFIX = "@mozilla.org/network/protocol/about;1?what=";
- return [[k.substr(PREFIX.length), ""] for (k in Cc) if (k.indexOf(PREFIX) == 0)];
+ return [[k.substr(services.ABOUT.length), ""]
+ for (k in Cc)
+ if (k.indexOf(services.ABOUT) == 0)];
};
});
@@ -930,7 +952,7 @@ var Completion = Module("completion", {
// Will, and should, throw an error if !(c in opts)
Array.forEach(complete, function (c) {
- let completer = this.urlCompleters[c];
+ let completer = this.urlCompleters[c] || { args: [], completer: this.autocomplete(c.replace(/^native:/, "")) };
context.forkapply(c, 0, this, completer.completer, completer.args);
}, this);
},
@@ -941,6 +963,64 @@ var Completion = Module("completion", {
this.urlCompleters[opt] = completer;
},
+ autocomplete: curry(function autocomplete(provider, context) {
+ let running = context.getCache("autocomplete-search-running", Object);
+
+ let name = "autocomplete:" + provider;
+ if (!services.has(name))
+ services.add(name, services.AUTOCOMPLETE + provider, "nsIAutoCompleteSearch");
+ let service = services[name];
+
+ util.assert(service, _("autocomplete.noSuchProvider", provider), false);
+
+ if (running[provider]) {
+ this.completions = this.completions;
+ this.cancel();
+ }
+
+ context.anchored = false;
+ context.compare = CompletionContext.Sort.unsorted;
+ context.filterFunc = null;
+
+ let words = context.filter.toLowerCase().split(/\s+/g);
+ context.hasItems = true;
+ context.completions = context.completions.filter(function ({ url, title })
+ words.every(function (w) (url + " " + title).toLowerCase().indexOf(w) >= 0))
+ context.incomplete = true;
+
+ context.format = this.modules.bookmarks.format;
+ context.keys.extra = function (item) {
+ try {
+ return bookmarkcache.get(item.url).extra;
+ }
+ catch (e) {}
+ return null;
+ };
+ context.title = [_("autocomplete.title", provider)];
+
+ context.cancel = function () {
+ this.incomplete = false;
+ if (running[provider])
+ service.stopSearch();
+ running[provider] = false;
+ };
+
+ service.startSearch(context.filter, "", context.result, {
+ onSearchResult: function onSearchResult(search, result) {
+ if (result.searchResult <= result.RESULT_SUCCESS)
+ running[provider] = null;
+
+ context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING;
+ context.completions = [
+ { url: result.getValueAt(i), title: result.getCommentAt(i), icon: result.getImageAt(i) }
+ for (i in util.range(0, result.matchCount))
+ ];
+ },
+ get onUpdateSearchResult() this.onSearchResult
+ });
+ running[provider] = true;
+ }),
+
urls: function (context, tags) {
let compare = String.localeCompare;
let contains = String.indexOf;
@@ -1053,8 +1133,32 @@ var Completion = Module("completion", {
options.add(["complete", "cpt"],
"Items which are completed at the :open prompts",
- "charlist", config.defaults.complete == null ? "slf" : config.defaults.complete,
- { get values() values(completion.urlCompleters).toArray() });
+ "stringlist", config.defaults.complete == null ? "slf" : config.defaults.complete,
+ {
+ valueMap: {
+ S: "suggestion",
+ b: "bookmark",
+ f: "file",
+ h: "history",
+ l: "location",
+ s: "search"
+ },
+
+ get values() values(completion.urlCompleters).toArray()
+ .concat([let (name = k.substr(services.AUTOCOMPLETE.length))
+ ["native:" + name, _("autocomplete.description", name)]
+ for (k in Cc)
+ if (k.indexOf(services.AUTOCOMPLETE) == 0)]),
+
+ setter: function setter(values) {
+ if (values.length == 1 && !Set.has(values[0], this.values)
+ && Array.every(values[0], Set.has(this.valueMap)))
+ return Array.map(values[0], function (v) this[v], this.valueMap);
+ return values;
+ },
+
+ validator: function validator(values) validator.supercall(this, this.setter(values))
+ });
options.add(["wildanchor", "wia"],
"Define which completion groups only match at the beginning of their text",
diff --git a/common/modules/config.jsm b/common/modules/config.jsm
index df607445..4453edf4 100644
--- a/common/modules/config.jsm
+++ b/common/modules/config.jsm
@@ -19,7 +19,7 @@ AboutHandler.prototype = {
classID: Components.ID("81495d80-89ee-4c36-a88d-ea7c4e5ac63f"),
- get contractID() "@mozilla.org/network/protocol/about;1?what=" + config.name,
+ get contractID() services.ABOUT + config.name,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
diff --git a/common/modules/io.jsm b/common/modules/io.jsm
index e724ab6d..c81ff18e 100644
--- a/common/modules/io.jsm
+++ b/common/modules/io.jsm
@@ -976,7 +976,7 @@ unlet s:cpo_save
};
};
- completion.addUrlCompleter("f", "Local files", function (context, full) {
+ completion.addUrlCompleter("file", "Local files", function (context, full) {
let match = util.regexp(
diff --git a/common/modules/protocol.jsm b/common/modules/protocol.jsm
index 06d25498..ba02a156 100644
--- a/common/modules/protocol.jsm
+++ b/common/modules/protocol.jsm
@@ -93,7 +93,7 @@ function ProtocolBase() {
};
}
ProtocolBase.prototype = {
- get contractID() "@mozilla.org/network/protocol;1?name=" + this.scheme,
+ get contractID() services.PROTOCOL + this.scheme,
get classDescription() this.scheme + " utility protocol",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
diff --git a/common/modules/services.jsm b/common/modules/services.jsm
index ba007dfa..f70dd543 100644
--- a/common/modules/services.jsm
+++ b/common/modules/services.jsm
@@ -16,13 +16,16 @@ defineModule("services", {
* A lazily-instantiated XPCOM class and service cache.
*/
var Services = Module("Services", {
+ ABOUT: "@mozilla.org/network/protocol/about;1?what=",
+ AUTOCOMPLETE: "@mozilla.org/autocomplete/search;1?name=",
+ PROTOCOL: "@mozilla.org/network/protocol;1?name=",
+
init: function () {
this.services = {};
this.add("annotation", "@mozilla.org/browser/annotation-service;1", "nsIAnnotationService");
this.add("appShell", "@mozilla.org/appshell/appShellService;1", "nsIAppShellService");
this.add("appStartup", "@mozilla.org/toolkit/app-startup;1", "nsIAppStartup");
- this.add("autoCompleteSearch", "@mozilla.org/autocomplete/search;1?name=history", "nsIAutoCompleteSearch");
this.add("bookmarks", "@mozilla.org/browser/nav-bookmarks-service;1", "nsINavBookmarksService");
this.add("bootstrap", "@dactyl.googlecode.com/base/bootstrap");
this.add("browserSearch", "@mozilla.org/browser/search-service;1", "nsIBrowserSearchService");
@@ -34,7 +37,7 @@ var Services = Module("Services", {
this.add("commandLineHandler", "@mozilla.org/commandlinehandler/general-startup;1?type=dactyl");
this.add("console", "@mozilla.org/consoleservice;1", "nsIConsoleService");
this.add("dactyl", "@dactyl.googlecode.com/extra/utils", "dactylIUtils");
- this.add("dactyl:", "@mozilla.org/network/protocol;1?name=dactyl");
+ this.add("dactyl:", this.PROTOCOL + "dactyl");
this.add("debugger", "@mozilla.org/js/jsd/debugger-service;1", "jsdIDebuggerService");
this.add("directory", "@mozilla.org/file/directory_service;1", "nsIProperties");
this.add("downloadManager", "@mozilla.org/download-manager;1", "nsIDownloadManager");
@@ -43,7 +46,7 @@ var Services = Module("Services", {
this.add("externalApp", "@mozilla.org/uriloader/external-helper-app-service;1", "nsPIExternalAppLauncher")
this.add("externalProtocol", "@mozilla.org/uriloader/external-protocol-service;1", "nsIExternalProtocolService");
this.add("favicon", "@mozilla.org/browser/favicon-service;1", "nsIFaviconService");
- this.add("file:", "@mozilla.org/network/protocol;1?name=file", "nsIFileProtocolHandler");
+ this.add("file:", this.PROTOCOL + "file", "nsIFileProtocolHandler");
this.add("focus", "@mozilla.org/focus-manager;1", "nsIFocusManager");
this.add("history", "@mozilla.org/browser/global-history;2",
["nsIBrowserHistory", "nsIGlobalHistory2", "nsINavHistoryService", "nsPIPlacesDatabase"]);
@@ -57,7 +60,7 @@ var Services = Module("Services", {
this.add("pref", "@mozilla.org/preferences-service;1", ["nsIPrefBranch2", "nsIPrefService"]);
this.add("privateBrowsing", "@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService");
this.add("profile", "@mozilla.org/toolkit/profile-service;1", "nsIToolkitProfileService");
- this.add("resource:", "@mozilla.org/network/protocol;1?name=resource", ["nsIProtocolHandler", "nsIResProtocolHandler"]);
+ this.add("resource:", this.PROTOCOL + "resource", ["nsIProtocolHandler", "nsIResProtocolHandler"]);
this.add("runtime", "@mozilla.org/xre/runtime;1", ["nsIXULAppInfo", "nsIXULRuntime"]);
this.add("rdf", "@mozilla.org/rdf/rdf-service;1", "nsIRDFService");
this.add("sessionStore", "@mozilla.org/browser/sessionstore;1", "nsISessionStore");
diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS
index c5e1c429..ad3a2521 100644
--- a/pentadactyl/NEWS
+++ b/pentadactyl/NEWS
@@ -168,6 +168,8 @@
- Boolean options no longer accept an argument. [b4]
- 'cdpath' and 'runtimepath' no longer treat ",,"
specially. Use "." instead. [b2]
+ - 'complete' is now a [stringlist] rather than a [charlist]
+ and supports native autocomplete providers. [b8]
- 'extendedhinttags' is now a [regexpmap] rather than a
string. [b2]
- 'guioptions' default value has changed. [b4][b7]
diff --git a/pentadactyl/content/config.js b/pentadactyl/content/config.js
index 4d289f28..0b787978 100644
--- a/pentadactyl/content/config.js
+++ b/pentadactyl/content/config.js
@@ -128,7 +128,7 @@ var Config = Module("config", ConfigBase, {
},
defaults: {
- complete: "slf",
+ complete: "search,location,file",
guioptions: "bCrs",
showtabline: "always",
titlestring: "Pentadactyl"
@@ -280,55 +280,12 @@ var Config = Module("config", ConfigBase, {
const { CompletionContext, bookmarkcache, completion } = modules;
const { document } = window;
- var searchRunning = null;
completion.location = function location(context) {
- if (!services.autoCompleteSearch)
- return;
-
- if (searchRunning) {
- searchRunning.completions = searchRunning.completions;
- searchRunning.cancel();
- }
-
- context.anchored = false;
- context.compare = CompletionContext.Sort.unsorted;
- context.filterFunc = null;
-
- let words = context.filter.toLowerCase().split(/\s+/g);
- context.hasItems = true;
- context.completions = context.completions.filter(function ({ url, title })
- words.every(function (w) (url + " " + title).toLowerCase().indexOf(w) >= 0))
- context.incomplete = true;
-
- context.format = modules.bookmarks.format;
- context.keys.extra = function (item) (bookmarkcache.get(item.url) || {}).extra;
+ completion.autocomplete("history", context);
context.title = ["Smart Completions"];
-
- context.cancel = function () {
- this.incomplete = false;
- if (searchRunning === this) {
- services.autoCompleteSearch.stopSearch();
- searchRunning = null;
- }
- };
-
- services.autoCompleteSearch.startSearch(context.filter, "", context.result, {
- onSearchResult: function onSearchResult(search, result) {
- if (result.searchResult <= result.RESULT_SUCCESS)
- searchRunning = null;
-
- context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING;
- context.completions = [
- { url: result.getValueAt(i), title: result.getCommentAt(i), icon: result.getImageAt(i) }
- for (i in util.range(0, result.matchCount))
- ];
- },
- get onUpdateSearchResult() this.onSearchResult
- });
- searchRunning = context;
};
- completion.addUrlCompleter("l",
+ completion.addUrlCompleter("location",
"Firefox location bar entries (bookmarks and history sorted in an intelligent way)",
completion.location);