Bug 1493675 - Send JS keywords from webconsole's autocomplete service; r=julienw.
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Fri, 19 Oct 2018 16:07:22 +0000
changeset 490534 cc4cf78044586c9eb1932686d453835824c67e46
parent 490533 887b52d6d707a0f3030c5a4ec10fc6ab3724c2be
child 490535 6eaed80ae36ffeaffe0bad1e969e5c92e1c9922c
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersjulienw
bugs1493675
milestone64.0a1
Bug 1493675 - Send JS keywords from webconsole's autocomplete service; r=julienw. Since we do now have the list of Javascript keywords, we import it from webconsole autocomplete service and send the keywords matching the current expression to the client. Differential Revision: https://phabricator.services.mozilla.com/D8709
devtools/client/webconsole/test/mochitest/browser_jsterm_completion_case_sensitivity.js
devtools/server/actors/webconsole.js
devtools/shared/webconsole/test/test_jsterm_autocomplete.html
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_completion_case_sensitivity.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_completion_case_sensitivity.js
@@ -77,16 +77,33 @@ async function performTests() {
   onPopupClose = autocompletePopup.once("popup-closed");
   EventUtils.synthesizeKey("KEY_Tab");
   await onPopupClose;
   checkInput("fooBar.Foo|", "The input was completed with the correct casing");
   checkJsTermCompletionValue(jsterm, "", "completeNode is empty");
 
   jsterm.setInputValue("");
 
+  info("Check that Javascript keywords are displayed first");
+  onPopUpOpen = autocompletePopup.once("popup-opened");
+  EventUtils.sendString("func");
+  await onPopUpOpen;
+
+  is(getAutocompletePopupLabels(autocompletePopup).join(" - "), "function - Function",
+    "popup has expected item");
+  checkJsTermCompletionValue(jsterm, "    tion", "completeNode has expected value");
+
+  onPopupClose = autocompletePopup.once("popup-closed");
+  EventUtils.synthesizeKey("KEY_Tab");
+  await onPopupClose;
+  checkInput("function|", "The input was completed as expected");
+  checkJsTermCompletionValue(jsterm, "", "completeNode is empty");
+
+  jsterm.setInputValue("");
+
   info("Check that filtering the cache works like on the server");
   onPopUpOpen = autocompletePopup.once("popup-opened");
   EventUtils.sendString("fooBar.");
   await onPopUpOpen;
   is(getAutocompletePopupLabels(autocompletePopup).join(" - "),
     "test - Foo - TEST - Test", "popup has expected items");
 
   onAutoCompleteUpdated = jsterm.once("autocomplete-updated");
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -29,16 +29,19 @@ loader.lazyRequireGetter(this, "addWebCo
 loader.lazyRequireGetter(this, "isCommand", "devtools/server/actors/webconsole/commands", true);
 loader.lazyRequireGetter(this, "validCommands", "devtools/server/actors/webconsole/commands", true);
 loader.lazyRequireGetter(this, "createMessageManagerMocks", "devtools/server/actors/webconsole/message-manager-mock", 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);
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
+// Generated by /devtools/shared/webconsole/GenerateReservedWordsJS.py
+loader.lazyRequireGetter(this, "RESERVED_JS_KEYWORDS", "devtools/shared/webconsole/reserved-js-words");
+
 // Overwrite implemented listeners for workers so that we don't attempt
 // to load an unsupported module.
 if (isWorker) {
   loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/worker-listeners", true);
   loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/worker-listeners", true);
 } else {
   loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/listeners/console-api", true);
   loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners/console-service", true);
@@ -1229,24 +1232,31 @@ WebConsoleActor.prototype =
       matchProp = result.matchProp;
       isElementAccess = result.isElementAccess;
 
       // We consider '$' as alphanumeric because it is used in the names of some
       // helper functions; we also consider whitespace as alphanum since it should not
       // be seen as break in the evaled string.
       const lastNonAlphaIsDot = /[.][a-zA-Z0-9$\s]*$/.test(reqText);
 
-      // We only return command when we are not dealing with a property or element access.
+      // We only return commands and keywords when we are not dealing with a property or
+      // element access.
       if (!lastNonAlphaIsDot && !isElementAccess) {
         this._getWebConsoleCommandsCache().forEach(n => {
           // filter out `screenshot` command as it is inaccessible without the `:` prefix
           if (n !== "screenshot" && n.startsWith(result.matchProp)) {
             matches.add(n);
           }
         });
+
+        for (const keyword of RESERVED_JS_KEYWORDS) {
+          if (keyword.startsWith(result.matchProp)) {
+            matches.add(keyword);
+          }
+        }
       }
 
       // Sort the results in order to display lowercased item first (e.g. we want to
       // display `document` then `Document` as we loosely match the user input if the
       // first letter they typed was lowercase).
       matches = Array.from(matches).sort((a, b) => {
         const startingQuoteRegex = /^('|"|`)/;
         const aFirstMeaningfulChar = startingQuoteRegex.test(a) ? a[1] : a[0];
--- a/devtools/shared/webconsole/test/test_jsterm_autocomplete.html
+++ b/devtools/shared/webconsole/test/test_jsterm_autocomplete.html
@@ -100,16 +100,17 @@
       doAutocompleteProxyThrowsPrototype,
       doAutocompleteProxyThrowsOwnKeys,
       doAutocompleteDotSurroundedBySpaces,
       doAutocompleteAfterOr,
       doInsensitiveAutocomplete,
       doElementAccessAutocomplete,
       doAutocompleteAfterOperator,
       dontAutocompleteAfterDeclaration,
+      doKeywordsAutocomplete,
     ];
 
     if (!isWorker) {
       // `Cu` is not defined in workers, then we can't test `Cu.Sandbox`
       tests.push(doAutocompleteSandbox);
       // Array literal, string and commands completion aren't handled in Workers yet.
       tests.push(
         doAutocompleteArray,
@@ -514,12 +515,33 @@
     info("test autocomplete for 'const win = win'");
     matches = (await client.autocomplete("const win = win")).matches;
     ok(matches.includes("window"), "autocompletion still happens after the `=` sign");
 
     info("test autocomplete for 'in var'");
     matches = (await client.autocomplete("in var")).matches;
     ok(matches.includes("varify"),
       "autocompletion still happens with a property name starting with 'var'");
+}
+
+async function doKeywordsAutocomplete(client) {
+  info("test autocomplete for 'func'");
+  let matches = (await client.autocomplete("func")).matches;
+  ok(matches.includes("function"), "keywords are returned");
+
+  info("test autocomplete for ':func'");
+  matches = (await client.autocomplete(":func")).matches;
+  is(!matches.includes("function"), true,
+    "'function' is not returned when prefixed with ':'");
+
+  info("test autocomplete for 'window.func'");
+  matches = (await client.autocomplete("window.func")).matches;
+  ok(!matches.includes("function"),
+    "'function' is not returned when doing a property access");
+
+  info("test autocomplete for 'window[func'");
+  matches = (await client.autocomplete("window[func")).matches;
+  ok(!matches.includes("function"),
+    "'function' is not returned when doing an element access");
   }
 </script>
 </body>
 </html>