Bug 1494545 - Make Ctrl+Space open the autocomplete popup in the console; r=Honza.
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Thu, 27 Sep 2018 16:36:11 +0000
changeset 438573 241b06f4daf2fe4d33362b1eb70ded785b088b36
parent 438572 e828cf7c367e0c1e73ef537f1dd5dec4bf7e43cf
child 438574 6fabe40627008d10c2ff8a78e8d2932264149cb5
push id70041
push usernchevobbe@mozilla.com
push dateThu, 27 Sep 2018 16:53:05 +0000
treeherderautoland@241b06f4daf2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersHonza
bugs1494545
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1494545 - Make Ctrl+Space open the autocomplete popup in the console; r=Honza. Differential Revision: https://phabricator.services.mozilla.com/D7049
devtools/client/webconsole/components/JSTerm.js
devtools/client/webconsole/test/mochitest/browser.ini
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_control_space.js
--- a/devtools/client/webconsole/components/JSTerm.js
+++ b/devtools/client/webconsole/components/JSTerm.js
@@ -348,16 +348,25 @@ class JSTerm extends Component {
               if (!this.getInputValue()) {
                 this.hud.outputScroller.scrollTop = this.hud.outputScroller.scrollHeight;
                 return null;
               }
 
               return "CodeMirror.Pass";
             },
 
+            "Ctrl-Space": () => {
+              if (!this.autocompletePopup.isOpen) {
+                this.updateAutocompletion(true);
+                return null;
+              }
+
+              return "CodeMirror.Pass";
+            },
+
             "Esc": false,
             "Cmd-F": false,
             "Ctrl-F": false,
           }
         });
 
         this.editor.on("changes", this._inputEventHandler);
         this.editor.on("beforeChange", this._onBeforeChange);
@@ -801,16 +810,17 @@ class JSTerm extends Component {
    *
    * @private
    * @param Event event
    */
   _keyPress(event) {
     const inputNode = this.inputNode;
     const inputValue = this.getInputValue();
     let inputUpdated = false;
+
     if (event.ctrlKey) {
       switch (event.charCode) {
         case 101:
           // control-e
           if (Services.appinfo.OS == "WINNT") {
             break;
           }
           let lineEndPos = inputValue.length;
@@ -858,16 +868,23 @@ class JSTerm extends Component {
             // away from the input.
             this.focus();
           }
           this.clearCompletion();
           break;
         default:
           break;
       }
+
+      if (event.key === " " && !this.autocompletePopup.isOpen) {
+        // Open the autocompletion popup on Ctrl-Space (if it wasn't displayed).
+        this.updateAutocompletion(true);
+        event.preventDefault();
+      }
+
       return;
     } else if (event.keyCode == KeyCodes.DOM_VK_RETURN) {
       if (!this.autocompletePopup.isOpen && (
         event.shiftKey || !Debugger.isCompilableUnit(this.getInputValue())
       )) {
         // shift return or incomplete statement
         return;
       }
@@ -1097,35 +1114,45 @@ class JSTerm extends Component {
     if (node.selectionStart != node.selectionEnd) {
       return false;
     }
 
     return node.selectionStart == node.value.length ? true :
            node.selectionStart == 0 && !multiline;
   }
 
-  async updateAutocompletion() {
+  /**
+   *
+   * @param {Boolean} force: True to not perform any check before trying to show the
+   *                         autocompletion popup. Defaults to false.
+   */
+  async updateAutocompletion(force = false) {
     const inputValue = this.getInputValue();
     const {editor, inputNode} = this;
     const frameActor = this.getFrameActor(this.SELECTED_FRAME);
 
     const cursor = this.getSelectionStart();
 
     // Complete if:
-    // - The input is not empty
-    // - AND there is not text selected
-    // - AND the input or frameActor are different from previous completion
-    // - AND there is not an alphanumeric (+ "_" and "$") right after the cursor
-    if (
+    // - `force` is true OR
+    //   - The input is not empty
+    //   - AND there is not text selected
+    //   - AND the input or frameActor are different from previous completion
+    //   - AND there is not an alphanumeric (+ "_" and "$") right after the cursor
+    if (!force && (
       !inputValue ||
       (inputNode && inputNode.selectionStart != inputNode.selectionEnd) ||
       (editor && editor.getSelection()) ||
-      (this.lastInputValue === inputValue && frameActor === this._lastFrameActorId) ||
+      (
+        !force &&
+        this.lastInputValue === inputValue &&
+        frameActor === this._lastFrameActorId
+      ) ||
       /^[a-zA-Z0-9_$]/.test(inputValue.substring(cursor))
-    ) {
+    )) {
       this.clearCompletion();
       this.emit("autocomplete-updated");
       return;
     }
 
     const input = this.getInputValueBeforeCursor();
 
     // If the current input starts with the previous input, then we already
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -184,16 +184,17 @@ skip-if = verify
 [browser_console_webconsole_private_browsing.js]
 [browser_jsterm_accessibility.js]
 [browser_jsterm_add_edited_input_to_history.js]
 [browser_jsterm_autocomplete_accept_no_scroll.js]
 [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_control_space.js]
 [browser_jsterm_autocomplete_crossdomain_iframe.js]
 [browser_jsterm_autocomplete_escape_key.js]
 [browser_jsterm_autocomplete_extraneous_closing_brackets.js]
 [browser_jsterm_autocomplete_helpers.js]
 [browser_jsterm_autocomplete_in_chrome_tab.js]
 [browser_jsterm_autocomplete_in_debugger_stackframe.js]
 [browser_jsterm_autocomplete_inside_text.js]
 [browser_jsterm_autocomplete_native_getters.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_control_space.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that Ctrl+Space displays the autocompletion popup when it's hidden.
+
+const TEST_URI = `data:text/html;charset=utf-8,
+<head>
+  <script>
+    /* Create a prototype-less object so popup does not contain native
+     * Object prototype properties.
+     */
+    window.foo = Object.create(null);
+    Object.assign(window.foo, {
+      item0: "value0",
+      item1: "value1",
+    });
+  </script>
+</head>
+<body>bug 585991 - autocomplete popup ctrl+space usage test</body>`;
+
+add_task(async function() {
+  // Run test with legacy JsTerm
+  await pushPref("devtools.webconsole.jsterm.codeMirror", false);
+  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);
+  info("web console opened");
+
+  const { autocompletePopup: popup } = jsterm;
+
+  let onPopUpOpen = popup.once("popup-opened");
+
+  info("wait for completion: window.foo.");
+  jsterm.setInputValue("window.foo");
+  EventUtils.sendString(".");
+
+  await onPopUpOpen;
+
+  const {itemCount} = popup;
+  ok(popup.isOpen, "popup is open");
+  ok(itemCount > 0, "popup has items");
+
+  info("Check that Ctrl+Space when the popup is opened has no effect");
+  EventUtils.synthesizeKey(" ", {ctrlKey: true});
+  ok(popup.isOpen, "The popup wasn't closed on Ctrl+Space");
+  is(popup.itemCount, itemCount, "The popup wasn't modified on Ctrl+Space");
+
+  info("press Escape to close the popup");
+  const onPopupClose = popup.once("popup-closed");
+  EventUtils.synthesizeKey("KEY_Escape");
+  await onPopupClose;
+  ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
+
+  info("Check that Ctrl+Space opens the popup when it was closed");
+  onPopUpOpen = popup.once("popup-opened");
+  EventUtils.synthesizeKey(" ", {ctrlKey: true});
+  await onPopUpOpen;
+
+  ok(popup.isOpen, "popup opens on Ctrl+Space");
+  ok(popup.itemCount, itemCount, "popup has the expected items");
+}