diff options
-rw-r--r-- | common/content/commandline.js | 2 | ||||
-rw-r--r-- | common/content/dactyl.js | 13 | ||||
-rw-r--r-- | common/modules/addons.jsm | 20 | ||||
-rw-r--r-- | common/modules/completion.jsm | 14 | ||||
-rw-r--r-- | common/modules/util.jsm | 15 | ||||
-rw-r--r-- | common/tests/functional/testCommands.js | 177 | ||||
-rw-r--r-- | pentadactyl/content/config.js | 40 |
7 files changed, 174 insertions, 107 deletions
diff --git a/common/content/commandline.js b/common/content/commandline.js index 6c889eeb..fb585e9c 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -1418,7 +1418,7 @@ var CommandLine = Module("commandline", { let command = commandline.command; self.accepted = true; - modes.pop(); + return function () modes.pop(); }); [ diff --git a/common/content/dactyl.js b/common/content/dactyl.js index eca46353..c273ca1f 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -1989,19 +1989,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { context.completions = [[k, v[0], v[2]] for ([k, v] in Iterator(config.dialogs))]; }; - completion.extension = function extension(context) { - context.title = ["Extension"]; - context.anchored = false; - context.keys = { text: "name", description: "description", icon: "iconURL" }, - context.generate = function () { - context.incomplete = true; - AddonManager.getAddonsByTypes(["extension"], function (addons) { - context.incomplete = false; - context.completions = addons; - }); - }; - }; - completion.help = function help(context, unchunked) { dactyl.initHelp(); context.title = ["Help"]; diff --git a/common/modules/addons.jsm b/common/modules/addons.jsm index 3d5fe593..9e0d04fb 100644 --- a/common/modules/addons.jsm +++ b/common/modules/addons.jsm @@ -437,18 +437,32 @@ var Addons = Module("addons", { context.completions = types.map(function (t) [t, util.capitalize(t)]); } + if (AddonManager.getAllAddons) + context.incomplete = true; + context.generate = function generate() { update(base); - if (AddonManager.getAllAddons) { - context.incomplete = true; + if (AddonManager.getAllAddons) AddonManager.getAllAddons(function (addons) { context.incomplete = false; update(array.uniq(base.concat(addons.map(function (a) a.type)), true)); }); - } } } + + completion.extension = function extension(context, types) { + context.title = ["Extension"]; + context.anchored = false; + context.keys = { text: "name", description: "description", icon: "iconURL" }, + context.incomplete = true; + context.generate = function () { + AddonManager.getAddonsByTypes(types || ["extension"], function (addons) { + context.incomplete = false; + context.completions = addons; + }); + }; + }; } }); diff --git a/common/modules/completion.jsm b/common/modules/completion.jsm index b2f038b5..e029e1b7 100644 --- a/common/modules/completion.jsm +++ b/common/modules/completion.jsm @@ -798,14 +798,14 @@ var CompletionContext = Class("CompletionContext", { /** * Wait for all subcontexts to complete. * - * @param {boolean} interruptible When true, the call may be interrupted - * via <C-c>, in which case, "Interrupted" may be thrown. * @param {number} timeout The maximum time, in milliseconds, to wait. * If 0 or null, wait indefinitely. + * @param {boolean} interruptible When true, the call may be interrupted + * via <C-c>, in which case, "Interrupted" may be thrown. */ - wait: function wait(interruptable, timeout) { - util.waitFor(function () !this.incomplete, this, timeout); - return this.incomplete; + wait: function wait(timeout, interruptable) { + this.allItems; + return util.waitFor(function () !this.incomplete, this, timeout, interruptable); } }, { Sort: { @@ -852,7 +852,7 @@ var Completion = Module("completion", { return { items: res.map(function (i) ({ item: i })) }; context.contexts["/run"].completions = res; } - context.wait(true); + context.wait(null, true); return context.allItems; }, @@ -866,7 +866,7 @@ var Completion = Module("completion", { context.maxItems = maxItems; context.fork.apply(context, ["list", 0, this, name].concat(Array.slice(arguments, 3))); context = context.contexts["/list"]; - context.wait(); + context.wait(null, true); let contexts = context.contextList.filter(function (c) c.hasItems && c.items.length); if (!contexts.length) diff --git a/common/modules/util.jsm b/common/modules/util.jsm index 73b53e19..3fc01164 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -1536,10 +1536,19 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), this.yielders--; } }, + waitFor: function waitFor(test, self, timeout, interruptable) { - let end = timeout && Date.now() + timeout; - while ((!end || Date.now() < end) && !test.call(self)) - this.threadYield(false, interruptable); + let end = timeout && Date.now() + timeout, result; + + let timer = services.Timer(function () {}, 10, services.Timer.TYPE_REPEATING_SLACK); + try { + while (!(result = test.call(self)) && (!end || Date.now() < end)) + this.threadYield(false, interruptable); + } + finally { + timer.cancel(); + } + return result; }, yieldable: function yieldable(func) diff --git a/common/tests/functional/testCommands.js b/common/tests/functional/testCommands.js index 44e98a89..6b53e97f 100644 --- a/common/tests/functional/testCommands.js +++ b/common/tests/functional/testCommands.js @@ -19,6 +19,10 @@ var teardownModule = function (module) { dactyl.teardown(); } +function hasItems(context) context.allItems.items.length; +function hasntNullItems(context) hasItems(context) && + !context.allItems.items.some(function ({ text, description }) [text, description].some(function (text) /^\[object/.test(text))); + var tests = { "!": { multiOutput: ["echo foo"] @@ -32,18 +36,27 @@ var tests = { noOutput: [""] }, addons: { - multiOutput: ["", "dactyl", "-type=extension", "-type=extension dactyl"] + multiOutput: ["", "dactyl", "-type=extension", "-type=extension dactyl"], + completions: [ + "", + ["-types=", hasItems] + ] }, autocmd: { multiOutput: ["", "DOMLoad", "DOMLoad foo"], noOutput: ["DOMLoad foo bar", "-js DOMLoad foo bar"], - completions: ["", "DOMLoad foo ", "-js DOMLoad foo "] + completions: [ + ["", hasntNullItems], + "DOMLoad foo ", + "-js DOMLoad foo " + ] }, back: { noOutput: [""] }, bdelete: { init: ["tabopen about:pentadactyl", "tabopen about:pentadactyl"], noOutput: [""], - anyOutput: ["about:pentadactyl"] + anyOutput: ["about:pentadactyl"], + completions: [["", hasItems]] }, bmark: { someOutput: ["bmark", "bmark -tags=foo -titlt=bar -keyword=baz -charset=UTF-8 -post=quux about:pentadactyl"], @@ -52,7 +65,7 @@ var tests = { "-max=1 -keyword=", "-max=1 -keyword=foo -tags=", "-max=1 -keyword=foo -tags=bar -title=", - "-max=1 -keyword=foo -tags=bar -title=baz -charset=", + ["-max=1 -keyword=foo -tags=bar -title=baz -charset=", hasItems], "-max=1 -keyword=foo -tags=bar -title=baz -charset= about:" ] }, @@ -68,7 +81,10 @@ var tests = { buffer: { anyOutput: ["", "1"], noOutput: ["!", "! 1"], - completions: ["", "1"] + completions: [ + ["", hasItems], + ["1", hasItems] + ] }, buffers: { multiOutput: ["", "1"], @@ -95,7 +111,10 @@ var tests = { cookies: { anyOutput: ["dactyl.sf.net", "dactyl.sf.net list"], error: [""], - completions: ["", "dactyl.sf.net "] + completions: [ + "", + ["dactyl.sf.net ", hasItems] + ] }, delbmarks: { anyOutput: ["", "about:pentadactyl"] }, delcommand: { @@ -114,7 +133,9 @@ var tests = { }, dialog: { // Skip implementation for now - completions: ["", "pre"] + completions: [ + ["", hasntNullItems] + ] }, doautoall: {}, // Skip for now doautocmd: {}, // Skip for now @@ -147,24 +168,34 @@ var tests = { emenu: { noOutput: ["View.Zoom.Zoom In", "View.Zoom.Zoom Out"], error: [""], - completions: ["", "View."] + completions: [ + ["", hasItems], + ["View.", hasItems] + ] }, endif: {}, // Skip for now execute: { noOutput: ["", "'js " + "".quote() + "'"], - someOutput: ["'ls'"] + someOutput: ["'ls'"], + completions: [["", hasItems]] }, extadd: { - completions: [""], + completions: [["", hasItems]], error: [""] }, extdelete: { - completions: [""], + completions: [["", hasItems]], error: [""] }, get extdisable() this.extdelete, - get extenable() this.extdelete, - get extoptions() this.extdelete, + extenable: { + completions: [""], + error: [""] + }, + extoptions: { + completions: [""], + error: [""] + }, get extrehash() this.extdelete, get exttoggle() this.extdelete, get extupdate() this.extdelete, @@ -179,7 +210,10 @@ var tests = { help: { noOutput: ["", "intro"], cleanup: ["tabdelete", "tabdelete"], - completions: ["", "'wild"] + completions: [ + ["", hasItems], + ["'wild", hasItems] + ] }, get helpall() this.help, highlight: { @@ -190,34 +224,35 @@ var tests = { "Help -group=FontCode foo: bar;" ], completions: [ - "", - "Help", - "Help ", - "Help -group=", - "Help -group=FontCode ", - "Help foo: bar; -moz" + ["", hasItems], + ["Help", hasItems], + ["Help ", hasItems], + ["Help -group=", hasItems], + ["Help -group=FontCode ", hasItems], + ["Help foo: bar; -moz", hasItems] ] }, history: { + init: ["open about:pentadactyl"], anyOutput: ["-max=1", "-max=1 -sort=+date", "-max=1 dactyl"], completions: [ - "", - "dactyl", - "-sort=+", - "-sort=-", - "-sort=+date ", - "-sort=+date dactyl" + ["", hasItems], + "about:", + ["-sort=+", hasItems], + ["-sort=-", hasItems], + ["-sort=+date ", hasItems], + "-sort=+date about:" ] }, if: {}, // Skip for now javascript: { noOutput: ["''", "'\\n'", "<pre>foo bar</pre>", "window"], completions: [ - "", - "window", - "window.", - "window['", - "commands.get('" + ["", hasItems], + ["window", hasItems], + ["window.", hasItems], + ["window['", hasItems], + ["commands.get('", hasItems] ] }, jumps: { @@ -231,7 +266,10 @@ var tests = { let: {}, // Deprecated. Fuck it. listcommands: { anyOutput: ["", "in"], - completions: ["", "in "] + completions: [ + ["", hasItems], + "in " + ] }, get listkeys() this.listcommands, get listoptions() this.listcommands, @@ -256,14 +294,14 @@ var tests = { "-gtroup=some-nonexistent-group <C-a> <C-a>" ], completeions: [ - "", - "-", - "-mode=ex ", - "-mode=", - "-group=", - "-builtin i ", - "-ex i ", - "-javascript i ", + ["", hasItems], + ["-", hasItems], + ["-mode=ex ", hasItems], + ["-mode=", hasItems], + ["-group=", hasItems], + ["-builtin i ", hasItems], + ["-ex i ", hasItems], + ["-javascript i ", hasItems] ] }, mapclear: { @@ -326,7 +364,9 @@ var tests = { "some-nonexistent-pentadactyl-dir/", "some-nonexistent-pentadactyl-dir/foo.vim" ], - completeions: [""], + completeions: [ + ["", hasItems] + ], cleanup: ["silent !rm -r some-nonexistent-pentadactyl-dir/"] }, normal: { @@ -337,20 +377,20 @@ var tests = { open: { noOutput: ["about:blank | about:home"], completions: [ - "", - "./", - "./ | ", - "chrome://", - "chrome://browser/", - "chrome://browser/content/", - "about:", - "resource://", - "resource://dactyl/" + ["", hasItems], + ["./", hasItems], + ["./ | ", hasItems], + ["chrome://", hasItems], + ["chrome://browser/", hasItems], + ["chrome://browser/content/", hasItems], + ["about:", hasItems], + ["resource://", hasItems], + ["resource://dactyl/", hasItems] ] }, pageinfo: { multiOutput: ["", "fgm"], - completions: [""], + completions: [["", hasItems]], error: ["abcdefghijklmnopqrstuvwxyz", "f g m"] }, pagestyle: { @@ -366,7 +406,10 @@ var tests = { "m foo bar" ], error: ["", "#"], - completions: ["", "m "] + completions: [ + ["", hasItems], // Fails. Why? + ["m ", hasItems] + ] }, qmarks: [ { @@ -376,7 +419,7 @@ var tests = { { init: ["qmark x"], multiOutput: ["", "m", "x"], - completions: [""] + completions: [["", hasItems]] } ], quit: {}, // Skip for now @@ -446,7 +489,10 @@ var tests = { // "!" Previous sidebar isn't saved until the window loads. // We don't give it enough time. ], - completions: ["", "! "] + completions: [ + ["", hasntNullItems], + "! " + ] }, silent: { noOutput: [ @@ -455,7 +501,7 @@ var tests = { "echoerr 'foo'", "echoerr " + "foo\nbar".quote() ], - completions: [""] + completions: [["", hasItems]] }, source: {}, stop: {}, @@ -508,7 +554,7 @@ function runCommands(cmdName, testName, commands, test, forbidErrors) { commands.forEach(function (val) { var [cmd, testVal] = Array.concat(val); - // dump("CMD: " + testName + " " + cmdName + " " + cmd + "\n"); + dump("CMD: " + testName + " " + cmdName + " " + cmd + "\n"); dactyl.clearMessage(); dactyl.closeMessageWindow(); @@ -529,7 +575,7 @@ function _runCommands(cmdName, testName, commands) { commands.forEach(function (value) { var [cmd, test] = Array.concat(value); - // dump("CMD: " + testName + " " + cmdName + " " + cmd + "\n"); + dump("CMD: " + testName + " " + cmdName + " " + cmd + "\n"); var res = dactyl.runExCommand(cmd); controller.waitForPageLoad(controller.tabs.activeTab); if (test) @@ -602,12 +648,21 @@ for (var val in Iterator(tests)) (function ([command, paramsList]) { addTest(command, testName, function () { commands.forEach(function (val) { var [cmd, test] = Array.concat(val); + cmd = command + cmd.replace(/^(!?) ?/, "$1 "); dactyl.assertNoErrorMessages(function () { - dactyl.runExCompletion(command + cmd.replace(/^(!?) ?/, "$1 ")); - if (test) - jumlib.assert(test(dactyl.modules.commandline.commandSession.completions.context), - "Initializing for " + cmdName + " tests failed: " + cmd.quote() + " " + test); + dump("COMPL: " + cmd + "\n"); + dactyl.runExCompletion(cmd); + if (test) { + /* Freezes. :( + var context = dactyl.modules.commandline.commandSession.completions.context; + */ + var context = dactyl.modules.CompletionContext(cmd); + context.tabPressed = true; + context.fork("ex", 0, dactyl.modules.completion, "ex"); + jumlib.assert(context.wait(5000), "Completion failed: " + cmd.quote()); + jumlib.assert(test(context), "Completion tests failed: " + cmd.quote() + " " + test); + } }); }); }); diff --git a/pentadactyl/content/config.js b/pentadactyl/content/config.js index 89ace4cf..75e13d4c 100644 --- a/pentadactyl/content/config.js +++ b/pentadactyl/content/config.js @@ -281,11 +281,16 @@ var Config = Module("config", ConfigBase, { const { CompletionContext, bookmarkcache, bookmarks, completion } = modules; const { document } = window; - var searchRunning = false; // only until Firefox fixes https://bugzilla.mozilla.org/show_bug.cgi?id=510589 + var searchRunning = null; // only until Firefox fixes https://bugzilla.mozilla.org/show_bug.cgi?id=510589 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; @@ -297,30 +302,27 @@ var Config = Module("config", ConfigBase, { context.title = ["Smart Completions"]; context.cancel = function () { - if (searchRunning) { + this.incomplete = false; + if (searchRunning === this) { services.autoCompleteSearch.stopSearch(); - searchRunning = false; + searchRunning = null; } }; - if (searchRunning) - services.autoCompleteSearch.stopSearch(); - let timer = new Timer(50, 100, function (result) { - 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)) - ]; - }); + services.autoCompleteSearch.startSearch(context.filter, "", context.result, { onSearchResult: function onSearchResult(search, result) { - timer.tell(result); - if (result.searchResult <= result.RESULT_SUCCESS) { - searchRunning = false; - timer.flush(); - } - } + 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 = true; + searchRunning = context; }; completion.sidebar = function sidebar(context) { |