diff --git a/common/content/completion.js b/common/content/completion.js index c27734fc..058ef027 100644 --- a/common/content/completion.js +++ b/common/content/completion.js @@ -528,7 +528,9 @@ CompletionContext.prototype = { cancelAll: function () { - for (let [,context] in Iterator(this.contextList)) + // Kris: contextList gives undefined. And why do we have contexts and contextList? + // I am not too much of a fan of a huge API for classes + for (let [,context] in Iterator(this.top.contexts)) { if (context.cancel) context.cancel(); @@ -1576,6 +1578,7 @@ function Completion() //{{{ context.filterFunc = null; context.cancel = function () services.get("autoCompleteSearch").stopSearch(); context.compare = null; + // TODO: shouldn't this timer be deleted by context.cancel? let timer = new Timer(50, 100, function (result) { context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING; context.completions = [ @@ -1583,16 +1586,28 @@ function Completion() //{{{ for (i in util.range(0, result.matchCount)) ]; }); - services.get("autoCompleteSearch").stopSearch(); - services.get("autoCompleteSearch").startSearch(context.filter, "", context.result, { - onSearchResult: function onSearchResult(search, result) - { - context.result = result; - timer.tell(result); - if (result.searchResult <= result.RESULT_SUCCESS) - timer.flush(); - } - }); + context.generate = function () { + let minItems = context.minItems || 1; + services.get("autoCompleteSearch").stopSearch(); + services.get("autoCompleteSearch").startSearch(context.filter, "", context.result, { + onSearchResult: function (search, result) + { + context.result = result; + timer.tell(result); + if (result.searchResult <= result.RESULT_SUCCESS) + timer.flush(); + } + }); + + let end = Date.now() + 5000; + while (context.incomplete && context.completions.length < minItems && Date.now() < end) + liberator.threadYield(false, true); + + // context.message = "More results..."; // very very strange, if I enable this line, completions don't work; + services.get("autoCompleteSearch").stopSearch(); + + return context.completions; + } }, macro: function macro(context) diff --git a/common/content/liberator.js b/common/content/liberator.js index dd5d396c..e7085102 100644 --- a/common/content/liberator.js +++ b/common/content/liberator.js @@ -1362,6 +1362,7 @@ const liberator = (function () //{{{ callback.call(self); }, + // TODO: interruptable not used? threadYield: function (flush, interruptable) { let mainThread = services.get("threadManager").mainThread; diff --git a/common/content/ui.js b/common/content/ui.js index 0943f691..f45f5cfb 100644 --- a/common/content/ui.js +++ b/common/content/ui.js @@ -336,7 +336,7 @@ function CommandLine() //{{{ else idx = this.selected + 1; break; - case this.RESET: + case this.RESET: // TODO: never used for now idx = null; break; default: @@ -360,22 +360,37 @@ function CommandLine() //{{{ tab: function tab(reverse) { autocompleteTimer.flush(); + // Check if we need to run the completer. + let numElementsNeeded; + if (this.selected == null) + numElementsNeeded = reverse ? 100000 : 1; // better way to specify give me all items than setting to a very large number? + else + numElementsNeeded = reverse ? this.selected : this.selected + 2; // this.selected is zero-based + if (this.context.waitingForTab || this.wildIndex == -1) - this.complete(true, true); - - // Would prefer to only do this check when no completion - // is available, but there are complications. - if (this.items.length == 0 || this.context.incomplete) { - // No items. Wait for any unfinished completers. - let end = Date.now() + 5000; - while (this.context.incomplete && /* this.items.length == 0 && */ Date.now() < end) - liberator.threadYield(true, true); - - if (this.items.length == 0) - return liberator.beep(); + this.complete(true, true); } + else if (this.context.incomplete && numElementsNeeded > this.items.length) + { + for (let [, context] in Iterator(this.context.contexts)) + { + if (context.incomplete && context.generate) + { + // regenerate twice as many items as needed, as regenerating + // to often looks visually bad + context.minItems = numElementsNeeded * 2; // TODO: document minItems, or find a better way + context.regenerate = true; + context._generate(); // HACK + } + if (this.items.length >= numElementsNeeded) + break; + } + } + + if (this.items.length == 0) + return liberator.beep(); switch (this.wildtype.replace(/.*:/, "")) { @@ -1435,11 +1450,9 @@ function CommandLine() //{{{ { autocompleteTimer.reset(); - // liberator.dump("Resetting completions..."); if (completions) { completions.context.cancelAll(); - completions.wildIndex = -1; completions.previewClear(); }