Bug 1216668 - Implement console autocomplete for inline arrays and multiline strings;r=fitzgen
authorBrian Grinstead <bgrinstead@mozilla.com>
Wed, 21 Oct 2015 07:10:38 -0700
changeset 268704 9c494af00cfb40168b415f40c2341071189122ba
parent 268703 b491da27641b5c0d7357b1e5584bf156ead8a107
child 268705 113e19e7e902821fad7ac7a692db5b3132a90f7a
push id29561
push userkwierso@gmail.com
push dateWed, 21 Oct 2015 23:20:31 +0000
treeherdermozilla-central@f029ccdee154 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs1216668
milestone44.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 1216668 - Implement console autocomplete for inline arrays and multiline strings;r=fitzgen
devtools/shared/Parser.jsm
devtools/shared/webconsole/test/unit/test_js_property_provider.js
devtools/shared/webconsole/utils.js
--- a/devtools/shared/Parser.jsm
+++ b/devtools/shared/Parser.jsm
@@ -149,16 +149,24 @@ SyntaxTreesPool.prototype = {
   /**
    * @see SyntaxTree.prototype.getNamedFunctionDefinitions
    */
   getNamedFunctionDefinitions: function(aSubstring) {
     return this._call("getNamedFunctionDefinitions", -1, aSubstring);
   },
 
   /**
+   * @return SyntaxTree
+   *         The last tree in this._trees
+   */
+  getLastSyntaxTree: function() {
+    return this._trees[this._trees.length - 1];
+  },
+
+  /**
    * Gets the total number of scripts in the parent source.
    * @return number
    */
   get scriptCount() {
     return this._trees.length;
   },
 
   /**
--- a/devtools/shared/webconsole/test/unit/test_js_property_provider.js
+++ b/devtools/shared/webconsole/test/unit/test_js_property_provider.js
@@ -36,16 +36,56 @@ function run_test() {
   let results = JSPropertyProvider(dbgObject, null, "testArray[0].");
   do_print("Test that suggestions are given for 'foo[n]' where n is an integer.");
   test_has_result(results, "propA");
 
   do_print("Test that suggestions are given for multidimensional arrays.");
   results = JSPropertyProvider(dbgObject, null, "testArray[2][0].");
   test_has_result(results, "propE");
 
+  do_print("Test that suggestions are given for literal arrays.");
+  results = JSPropertyProvider(dbgObject, null, "[1,2,3].");
+  test_has_result(results, "indexOf");
+
+  do_print("Test that suggestions are given for literal arrays with newlines.");
+  results = JSPropertyProvider(dbgObject, null, "[1,2,3,\n4\n].");
+  test_has_result(results, "indexOf");
+
+  do_print("Test that suggestions are given for literal strings.");
+  results = JSPropertyProvider(dbgObject, null, "'foo'.");
+  test_has_result(results, "charAt");
+  results = JSPropertyProvider(dbgObject, null, '"foo".');
+  test_has_result(results, "charAt");
+  results = JSPropertyProvider(dbgObject, null, "`foo`.");
+  test_has_result(results, "charAt");
+  results = JSPropertyProvider(dbgObject, null, "'[1,2,3]'.");
+  test_has_result(results, "charAt");
+
+  do_print("Test that suggestions are not given for syntax errors.");
+  results = JSPropertyProvider(dbgObject, null, "'foo\"");
+  do_check_null(results);
+  results = JSPropertyProvider(dbgObject, null, "[1,',2]");
+  do_check_null(results);
+  results = JSPropertyProvider(dbgObject, null, "'[1,2].");
+  do_check_null(results);
+  results = JSPropertyProvider(dbgObject, null, "'foo'..");
+  do_check_null(results);
+
+  do_print("Test that suggestions are not given without a dot.");
+  results = JSPropertyProvider(dbgObject, null, "'foo'");
+  test_has_no_results(results);
+  results = JSPropertyProvider(dbgObject, null, "[1,2,3]");
+  test_has_no_results(results);
+  results = JSPropertyProvider(dbgObject, null, "[1,2,3].\n'foo'");
+  test_has_no_results(results);
+
+  do_print("Test that suggestions are not given for numeric literals.");
+  results = JSPropertyProvider(dbgObject, null, "1.");
+  do_check_null(results);
+
   do_print("Test that suggestions are not given for index that's out of bounds.");
   results = JSPropertyProvider(dbgObject, null, "testArray[10].");
   do_check_null(results);
 
   do_print("Test that no suggestions are given if an index is not numerical somewhere in the chain.");
   results = JSPropertyProvider(dbgObject, null, "testArray[0]['propC'][0].");
   do_check_null(results);
 
@@ -59,16 +99,25 @@ function run_test() {
   do_check_null(results);
 
   do_print("Test that suggestions are not given if there is an hyphen in the chain.");
   results = JSPropertyProvider(dbgObject, null, "testHyphenated['prop-A'].");
   do_check_null(results);
 }
 
 /**
+ * A helper that ensures an empty array of results were found.
+ * @param Object aResults
+ *        The results returned by JSPropertyProvider.
+ */
+function test_has_no_results(aResults) {
+  do_check_neq(aResults, null);
+  do_check_eq(aResults.matches.length, 0);
+}
+/**
  * A helper that ensures (required) results were found.
  * @param Object aResults
  *        The results returned by JSPropertyProvider.
  * @param String aRequiredSuggestion
  *        A suggestion that must be found from the results.
  */
 function test_has_result(aResults, aRequiredSuggestion) {
   do_check_neq(aResults, null);
--- a/devtools/shared/webconsole/utils.js
+++ b/devtools/shared/webconsole/utils.js
@@ -7,16 +7,17 @@
 "use strict";
 
 const {Cc, Ci, Cu, components} = require("chrome");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
+loader.lazyImporter(this, "Parser", "resource://gre/modules/devtools/shared/Parser.jsm");
 
 // TODO: Bug 842672 - browser/ imports modules from toolkit/.
 // Note that these are only used in WebConsoleCommands, see $0 and pprint().
 loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/client/shared/widgets/VariablesView.jsm");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 // Match the function name from the result of toString() or toSource().
 //
@@ -856,29 +857,44 @@ function JSPropertyProvider(aDbgObject, 
 
   // If the current state is not STATE_NORMAL, then we are inside of an string
   // which means that no completion is possible.
   if (beginning.state != STATE_NORMAL) {
     return null;
   }
 
   let completionPart = inputValue.substring(beginning.startPos);
+  let lastDot = completionPart.lastIndexOf(".");
 
   // Don't complete on just an empty string.
   if (completionPart.trim() == "") {
     return null;
   }
 
-  let lastDot = completionPart.lastIndexOf(".");
-  if (lastDot > 0 &&
-      (completionPart[0] == "'" || completionPart[0] == '"') &&
-      completionPart[lastDot - 1] == completionPart[0]) {
-    // We are completing a string literal.
-    let matchProp = completionPart.slice(lastDot + 1);
-    return getMatchedProps(String.prototype, matchProp);
+  // Catch literals like [1,2,3] or "foo" and return the matches from
+  // their prototypes.
+  if (lastDot > 0) {
+    let parser = new Parser();
+    parser.logExceptions = false;
+    let syntaxTree = parser.get(completionPart.slice(0, lastDot));
+    let lastTree = syntaxTree.getLastSyntaxTree();
+    let lastBody = lastTree && lastTree.AST.body[lastTree.AST.body.length - 1];
+
+    // Finding the last expression since we've sliced up until the dot.
+    // If there were parse errors this won't exist.
+    if (lastBody) {
+      let expression = lastBody.expression;
+      let matchProp = completionPart.slice(lastDot + 1);
+      if (expression.type === "ArrayExpression") {
+        return getMatchedProps(Array.prototype, matchProp);
+      } else if (expression.type === "Literal" &&
+                 (typeof expression.value === "string")) {
+        return getMatchedProps(String.prototype, matchProp);
+      }
+    }
   }
 
   // We are completing a variable / a property lookup.
   let properties = completionPart.split(".");
   let matchProp = properties.pop().trimLeft();
   let obj = aDbgObject;
 
   // The first property must be found in the environment if the debugger is