Bug 710152 - GCLI javascript completion should stop providing completions with complex JS input; r=dcamp
authorJoe Walker <jwalker@mozilla.com>
Mon, 09 Jan 2012 15:38:48 +0000
changeset 85328 62cca592625d6be53dcd2e31acb844c55555d975
parent 85327 a64fb47161e7afccff4a468dca7fe0cc69fc7488
child 85329 fdab6d891be0645b2cbb2842df8c9d66e2f4e3de
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdcamp
bugs710152
milestone12.0a1
Bug 710152 - GCLI javascript completion should stop providing completions with complex JS input; r=dcamp
browser/devtools/webconsole/gcli.jsm
browser/locales/en-US/chrome/browser/devtools/gcli.properties
--- a/browser/devtools/webconsole/gcli.jsm
+++ b/browser/devtools/webconsole/gcli.jsm
@@ -3024,16 +3024,22 @@ JavascriptType.prototype.parse = functio
   // should be completed.
   var beginning = this._findCompletionBeginning(typed);
 
   // There was an error analyzing the string.
   if (beginning.err) {
     return new Conversion(typed, arg, Status.ERROR, beginning.err);
   }
 
+  // If the current state is ParseState.COMPLEX, then we can't do completion.
+  // so bail out now
+  if (beginning.state === ParseState.COMPLEX) {
+    return new Conversion(typed, arg);
+  }
+
   // If the current state is not ParseState.NORMAL, then we are inside of a
   // string which means that no completion is possible.
   if (beginning.state !== ParseState.NORMAL) {
     return new Conversion(typed, arg, Status.INCOMPLETE, '');
   }
 
   var completionPart = typed.substring(beginning.startPos);
   var properties = completionPart.split('.');
@@ -3063,17 +3069,17 @@ JavascriptType.prototype.parse = functio
 
       try {
         scope = scope[prop];
       }
       catch (ex) {
         // It would be nice to be able to report this error in some way but
         // as it can happen just when someone types '{sessionStorage.', it
         // almost doesn't really count as an error, so we ignore it
-        return new Conversion(typed, arg, Status.INCOMPLETE, '');
+        return new Conversion(typed, arg, Status.VALID, '');
       }
     }
   }
   else {
     matchProp = properties[0].trimLeft();
   }
 
   // If the reason we just stopped adjusting the scope was a non-simple string,
@@ -3241,30 +3247,54 @@ function isVendorPrefixed(name) {
          name.indexOf('webkit') === 0 ||
          name.indexOf('ms') === 0;
 }
 
 /**
  * Constants used in return value of _findCompletionBeginning()
  */
 var ParseState = {
+  /**
+   * We have simple input like window.foo, without any punctuation that makes
+   * completion prediction be confusing or wrong
+   */
   NORMAL: 0,
+
+  /**
+   * The cursor is in some Javascript that makes completion hard to predict,
+   * like console.log(
+   */
+  COMPLEX: 1,
+
+  /**
+   * The cursor is inside single quotes (')
+   */
   QUOTE: 2,
+
+  /**
+   * The cursor is inside single quotes (")
+   */
   DQUOTE: 3
 };
 
 var OPEN_BODY = '{[('.split('');
 var CLOSE_BODY = '}])'.split('');
 var OPEN_CLOSE_BODY = {
   '{': '}',
   '[': ']',
   '(': ')'
 };
 
 /**
+ * How we distinguish between simple and complex JS input. We attempt
+ * completion against simple JS.
+ */
+var simpleChars = /[a-zA-Z0-9.]/;
+
+/**
  * Analyzes a given string to find the last statement that is interesting for
  * later completion.
  * @param text A string to analyze
  * @return If there was an error in the string detected, then a object like
  *   { err: 'ErrorMesssage' }
  * is returned, otherwise a object like
  *   {
  *     state: ParseState.NORMAL|ParseState.QUOTE|ParseState.DQUOTE,
@@ -3272,18 +3302,23 @@ var OPEN_CLOSE_BODY = {
  *   }
  */
 JavascriptType.prototype._findCompletionBeginning = function(text) {
   var bodyStack = [];
 
   var state = ParseState.NORMAL;
   var start = 0;
   var c;
+  var complex = false;
+
   for (var i = 0; i < text.length; i++) {
     c = text[i];
+    if (!simpleChars.test(c)) {
+      complex = true;
+    }
 
     switch (state) {
       // Normal JS state.
       case ParseState.NORMAL:
         if (c === '"') {
           state = ParseState.DQUOTE;
         }
         else if (c === '\'') {
@@ -3339,16 +3374,20 @@ JavascriptType.prototype._findCompletion
         }
         else if (c === '\'') {
           state = ParseState.NORMAL;
         }
         break;
     }
   }
 
+  if (state === ParseState.NORMAL && complex) {
+    state = ParseState.COMPLEX;
+  }
+
   return {
     state: state,
     startPos: start
   };
 };
 
 /**
  * Return true if the passed object is either an iterator or a generator, and
@@ -4333,17 +4372,16 @@ Requisition.prototype.toCanonicalString 
     // named parameters in place of positional params. Both can wait.
     if (assignment.getValue() !== assignment.param.defaultValue) {
       line.push(' ');
       line.push(type.stringify(assignment.getValue()));
     }
   }, this);
 
   // Canonically, if we've opened with a { then we should have a } to close
-  var command = this.commandAssignment.getValue();
   if (cmd === '{') {
     if (this.getAssignment(0).getArg().suffix.indexOf('}') === -1) {
       line.push(' }');
     }
   }
 
   return line.join('');
 };
@@ -6131,17 +6169,17 @@ Completer.prototype.update = function(in
   // Add a grey '}' to the end of the command line when we've opened
   // with a { but haven't closed it
   var command = this.requisition.commandAssignment.getValue();
   var unclosedJs = command && command.name === '{' &&
           this.requisition.getAssignment(0).getArg().suffix.indexOf('}') === -1;
   if (unclosedJs) {
     var close = dom.createElement(document, 'span');
     close.classList.add('gcli-in-closebrace');
-    close.appendChild(document.createTextNode('}'));
+    close.appendChild(document.createTextNode(' }'));
     this.element.appendChild(close);
   }
 };
 
 /**
  * Mark-up an array of Status values with spans
  */
 Completer.prototype.appendMarkupStatus = function(element, scores, input) {
--- a/browser/locales/en-US/chrome/browser/devtools/gcli.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gcli.properties
@@ -108,17 +108,17 @@ helpManual=Provide help either on a spec
 # parameter to the 'help' command. See helpSearchManual for a fuller
 # description of what it does. This string is designed to be shown in a dialog
 # with restricted space, which is why it should be as short as possible.
 helpSearchDesc=Search string
 
 # LOCALIZATION NOTE (helpSearchManual): A fuller description of the 'search'
 # parameter to the 'help' command. Displayed when the user asks for help on
 # what it does.
-helpSearchManual=A search string to use in narrowing down the list of commands that are displayed to the user. Any part of the string can match, regular expressions are not supported.
+helpSearchManual=A search string to use in narrowing down the list of commands that are displayed to the user. Any part of the command name can match, regular expressions are not supported.
 
 # LOCALIZATION NOTE (helpManSynopsis): A heading shown at the top of a help
 # page for a command in the console It labels a summary of the parameters to
 # the command
 helpManSynopsis=Synopsis
 
 # LOCALIZATION NOTE (helpManDescription): A heading shown in a help page for a
 # command in the console. This heading precedes the top level description.