Bug 1473923 - Autocomplete console commands; r=yulia.

MozReview-Commit-ID: LpbIzheFmeT

--HG--
extra : rebase_source : 2e40528ff43b76571ec59ce8af591cd71c828251
This commit is contained in:
Nicolas Chevobbe 2018-07-13 08:54:23 +02:00
Родитель 8f114fd737
Коммит 2fef8acb19
5 изменённых файлов: 124 добавлений и 48 удалений

Просмотреть файл

@ -1173,10 +1173,10 @@ class JSTerm extends Component {
}
if (this._autocompleteQuery && input.startsWith(this._autocompleteQuery)) {
let filterBy = input;
// Find the last non-alphanumeric other than _ or $ if it exists.
const lastNonAlpha = input.match(/[^a-zA-Z0-9_$][a-zA-Z0-9_$]*$/);
// Find the last non-alphanumeric other than "_", ":", or "$" if it exists.
const lastNonAlpha = input.match(/[^a-zA-Z0-9_$:][a-zA-Z0-9_$:]*$/);
// If input contains non-alphanumerics, use the part after the last one
// to filter the cache
// to filter the cache.
if (lastNonAlpha) {
filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
}

Просмотреть файл

@ -185,6 +185,7 @@ skip-if = verify
[browser_jsterm_autocomplete_array_no_index.js]
[browser_jsterm_autocomplete_arrow_keys.js]
[browser_jsterm_autocomplete_cached_results.js]
[browser_jsterm_autocomplete_commands.js]
[browser_jsterm_autocomplete_crossdomain_iframe.js]
[browser_jsterm_autocomplete_escape_key.js]
[browser_jsterm_autocomplete_extraneous_closing_brackets.js]

Просмотреть файл

@ -0,0 +1,59 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that console commands are autocompleted.
const TEST_URI = `data:text/html;charset=utf-8,Test command autocomplete`;
add_task(async function() {
// Run test with legacy JsTerm
await performTests();
// And then run it with the CodeMirror-powered one.
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
await performTests();
});
async function performTests() {
const { jsterm } = await openNewTabAndConsole(TEST_URI);
const { autocompletePopup } = jsterm;
const onPopUpOpen = autocompletePopup.once("popup-opened");
info(`Enter ":"`);
jsterm.focus();
EventUtils.sendString(":");
await onPopUpOpen;
const expectedCommands = [":help", ":screenshot"];
is(getPopupItems(autocompletePopup).join("\n"), expectedCommands.join("\n"),
"popup contains expected commands");
let onAutocompleUpdated = jsterm.once("autocomplete-updated");
EventUtils.sendString("s");
await onAutocompleUpdated;
checkJsTermCompletionValue(jsterm, " creenshot",
"completion node has expected :screenshot value");
EventUtils.synthesizeKey("KEY_Tab");
is(jsterm.getInputValue(), ":screenshot", "Tab key correctly completed :screenshot");
ok(!autocompletePopup.isOpen, "popup is closed after Tab");
info("Test :hel completion");
jsterm.setInputValue(":he");
onAutocompleUpdated = jsterm.once("autocomplete-updated");
EventUtils.sendString("l");
await onAutocompleUpdated;
checkJsTermCompletionValue(jsterm, " p", "completion node has expected :help value");
EventUtils.synthesizeKey("KEY_Tab");
is(jsterm.getInputValue(), ":help", "Tab key correctly completes :help");
}
function getPopupItems(popup) {
return popup.items.map(item => item.label);
}

Просмотреть файл

@ -31,6 +31,7 @@ loader.lazyRequireGetter(this, "WebConsoleCommands", "devtools/server/actors/web
loader.lazyRequireGetter(this, "addWebConsoleCommands", "devtools/server/actors/webconsole/utils", true);
loader.lazyRequireGetter(this, "formatCommand", "devtools/server/actors/webconsole/commands", true);
loader.lazyRequireGetter(this, "isCommand", "devtools/server/actors/webconsole/commands", true);
loader.lazyRequireGetter(this, "validCommands", "devtools/server/actors/webconsole/commands", true);
loader.lazyRequireGetter(this, "CONSOLE_WORKER_IDS", "devtools/server/actors/webconsole/utils", true);
loader.lazyRequireGetter(this, "WebConsoleUtils", "devtools/server/actors/webconsole/utils", true);
loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
@ -1085,54 +1086,57 @@ WebConsoleActor.prototype =
let dbgObject = null;
let environment = null;
let hadDebuggee = false;
// This is the case of the paused debugger
if (frameActorId) {
const frameActor = this.conn.getActor(frameActorId);
try {
// Need to try/catch since accessing frame.environment
// can throw "Debugger.Frame is not live"
const frame = frameActor.frame;
environment = frame.environment;
} catch (e) {
DevToolsUtils.reportException("autocomplete",
Error("The frame actor was not found: " + frameActorId));
}
} else {
// This is the general case (non-paused debugger)
hadDebuggee = this.dbg.hasDebuggee(this.evalWindow);
dbgObject = this.dbg.addDebuggee(this.evalWindow);
}
const result = JSPropertyProvider(dbgObject, environment, request.text,
request.cursor, frameActorId) || {};
if (!hadDebuggee && dbgObject) {
this.dbg.removeDebuggee(this.evalWindow);
}
let matches = result.matches || [];
let matches = [];
let matchProp;
const reqText = request.text.substr(0, request.cursor);
// We consider '$' as alphanumerc because it is used in the names of some
// helper functions.
const lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText);
if (!lastNonAlphaIsDot) {
if (!this._webConsoleCommandsCache) {
const helpers = {
sandbox: Object.create(null)
};
addWebConsoleCommands(helpers);
this._webConsoleCommandsCache =
Object.getOwnPropertyNames(helpers.sandbox);
if (isCommand(reqText)) {
const commandsCache = this._getWebConsoleCommandsCache();
matchProp = reqText;
matches = validCommands
.filter(c => `:${c}`.startsWith(reqText)
&& commandsCache.find(n => `:${n}`.startsWith(reqText))
)
.map(c => `:${c}`);
} else {
// This is the case of the paused debugger
if (frameActorId) {
const frameActor = this.conn.getActor(frameActorId);
try {
// Need to try/catch since accessing frame.environment
// can throw "Debugger.Frame is not live"
const frame = frameActor.frame;
environment = frame.environment;
} catch (e) {
DevToolsUtils.reportException("autocomplete",
Error("The frame actor was not found: " + frameActorId));
}
} else {
// This is the general case (non-paused debugger)
hadDebuggee = this.dbg.hasDebuggee(this.evalWindow);
dbgObject = this.dbg.addDebuggee(this.evalWindow);
}
matches = matches.concat(this._webConsoleCommandsCache
.filter(n =>
// filter out `screenshot` command as it is inaccessible without
// the `:` prefix
n !== "screenshot" && n.startsWith(result.matchProp)
));
const result = JSPropertyProvider(dbgObject, environment, request.text,
request.cursor, frameActorId) || {};
if (!hadDebuggee && dbgObject) {
this.dbg.removeDebuggee(this.evalWindow);
}
matches = result.matches || [];
matchProp = result.matchProp;
// We consider '$' as alphanumerc because it is used in the names of some
// helper functions.
const lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText);
if (!lastNonAlphaIsDot) {
matches = matches.concat(this._getWebConsoleCommandsCache().filter(n =>
// filter out `screenshot` command as it is inaccessible without
// the `:` prefix
n !== "screenshot" && n.startsWith(result.matchProp)
));
}
}
// Make sure we return an array with unique items, since `matches` can hold twice
@ -1143,7 +1147,7 @@ WebConsoleActor.prototype =
return {
from: this.actorID,
matches,
matchProp: result.matchProp,
matchProp,
};
},
@ -1274,6 +1278,17 @@ WebConsoleActor.prototype =
return helpers;
},
_getWebConsoleCommandsCache: function() {
if (!this._webConsoleCommandsCache) {
const helpers = {
sandbox: Object.create(null)
};
addWebConsoleCommands(helpers);
this._webConsoleCommandsCache = Object.getOwnPropertyNames(helpers.sandbox);
}
return this._webConsoleCommandsCache;
},
/**
* Evaluates a string using the debugger API.
*

Просмотреть файл

@ -236,3 +236,4 @@ function getTypedValue(value) {
exports.formatCommand = formatCommand;
exports.isCommand = isCommand;
exports.validCommands = validCommands;