diff --git a/content/bindings.xml b/content/bindings.xml
index 6d9ecb2b..d9653c5e 100644
--- a/content/bindings.xml
+++ b/content/bindings.xml
@@ -21,6 +21,117 @@
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
diff --git a/content/bookmarks.js b/content/bookmarks.js
index 7c2daf51..493e9431 100644
--- a/content/bookmarks.js
+++ b/content/bookmarks.js
@@ -235,7 +235,7 @@ function Bookmarks() //{{{
"string", "google",
{
completer: function completer(context) completion.search(context, true),
- validator: function validator(value) completion.runCompleter("search", "", true).some(function ([s]) s == value)
+ validator: options.validateCompleter
});
options.add(["preload"],
diff --git a/content/completion.js b/content/completion.js
index 684edbce..205edb7d 100644
--- a/content/completion.js
+++ b/content/completion.js
@@ -464,6 +464,14 @@ CompletionContext.prototype = {
context.incomplete = false;
}
},
+
+ wait: function wait(interruptable, timeout)
+ {
+ let end = Date.now() + timeout;
+ while (this.incomplete && (!timeout || Date.now() > end))
+ liberator.threadYield(true, interruptable);
+ return this.incomplete;
+ }
}
function Completion() //{{{
@@ -1015,8 +1023,7 @@ function Completion() //{{{
let res = context.fork.apply(context, ["run", 0, this, name].concat(Array.slice(arguments, 2)));
if (res) // FIXME
return { items: res.map(function (i) ({ item: i })) };
- while (context.incomplete)
- liberator.threadYield(true, true);
+ context.wait(true);
return context.allItems;
},
diff --git a/content/options.js b/content/options.js
index 6b672ab0..be173f52 100644
--- a/content/options.js
+++ b/content/options.js
@@ -944,10 +944,12 @@ function Options() //{{{
// TODO: Run this by default?
validateCompleter: function (values)
{
- let self = this;
- let completions = completion.runCompleter(function (context) self.completer(context), "");
+ let context = CompletionContext("");
+ let res = this.completer(context);
+ if (!res)
+ res = context.allItems.map(function (item) [item.text]);
return Array.concat(values).every(
- function (value) completions.some(function (item) item[0] == value));
+ function (value) res.some(function (item) item[0] == value));
},
get store() storage.options,
diff --git a/content/ui.js b/content/ui.js
index 302be48a..79b42057 100644
--- a/content/ui.js
+++ b/content/ui.js
@@ -106,6 +106,7 @@ function CommandLine() //{{{
var wildIndex = 0; // keep track how often we press in a row
var startHints = false; // whether we're waiting to start hints mode
+ var lastSubstring = "";
var statusTimer = new util.Timer(5, 100, function statusTell() {
if (completionIndex >= completions.items.length)
@@ -324,6 +325,9 @@ function CommandLine() //{{{
// highlight= won't work here.
let start = commandWidget.selectionStart;
let substring = completionContext.longestAllSubstring.substr(start - completionContext.allItems.start);
+ if (substring.length < 2 && substring != lastSubstring.substr(Math.max(0, lastSubstring.length - substring.length)))
+ return;
+ lastSubstring = substring;
let node = {substring}
editor.insertNode(util.xmlToDom(node, document), editor.rootElement, 1);
commandWidget.selectionStart = commandWidget.selectionEnd = start;