[repl] Async global completion
authorFedor Indutny <fedor.indutny@gmail.com>
Wed, 7 Sep 2011 10:31:29 +0000 (17:31 +0700)
committerFedor Indutny <fedor.indutny@gmail.com>
Thu, 8 Sep 2011 19:06:06 +0000 (02:06 +0700)
lib/repl.js

index 3948a98..db0d06f 100644 (file)
@@ -171,43 +171,36 @@ function REPLServer(prompt, stream) {
                   e.constructor.name === 'SyntaxError')) {
               finish(e);
             } else {
-              tryExpr();
+              tryExpr(e);
             }
             return;
           }
 
-          if (typeof ret !== 'function') {
-            return tryExpr(ret);
-          }
-
-          tryExpr();
+          tryExpr(typeof ret === 'function', ret);
         });
       };
 
       // Now as statement without parens.
-      function tryExpr(ret) {
-
-        self.bufferedCommand = '';
-
-        if (ret !== undefined) {
-          self.context._ = ret;
-          self.outputStream.write(exports.writer(ret) + '\n');
-          return finish(null);
-        }
+      function tryExpr(e, ret) {
+        if (!e) return finish(null, ret);
 
         self.eval(self.bufferedCommand, self.context,
                   'repl', function(e, ret) {
 
           if (e) {
             // instanceof doesn't work across context switches.
-            if (!(e && e.constructor && e.constructor.name === 'SyntaxError')) {
+            if (!(e && e.constructor &&
+                  e.constructor.name === 'SyntaxError')) {
               return finish(e);
-            // It could also be an error from JSON.parse
+              // It could also be an error from JSON.parse
             } else if (e &&
                        e.stack &&
                        e.stack.match(/^SyntaxError: Unexpected token .*\n/) &&
                        e.stack.match(/\n    at Object.parse \(native\)\n/)) {
               return finish(e);
+            } else {
+              finish(true);
+              return;
             }
           }
           finish(null, ret);
@@ -222,12 +215,23 @@ function REPLServer(prompt, stream) {
       if (e) {
         if (e.stack) {
           self.outputStream.write(e.stack + '\n');
+        } else if (e === true) {
+          self.displayPrompt();
+          return;
         } else {
           self.outputStream.write(e.toString() + '\n');
         }
         // On error: Print the error and clear the buffer
         self.bufferedCommand = '';
+      } else {
+        self.bufferedCommand = '';
+      }
+
+      if (ret !== undefined) {
+        self.context._ = ret;
+        self.outputStream.write(exports.writer(ret) + '\n');
       }
+
       self.displayPrompt();
     };
   });
@@ -413,25 +417,48 @@ REPLServer.prototype.complete = function(line, callback) {
       // Resolve expr and get its completions.
       var obj, memberGroups = [];
       if (!expr) {
-        completionGroups.push(Object.getOwnPropertyNames(this.context));
-        // Global object properties
-        // (http://www.ecma-international.org/publications/standards/Ecma-262.htm)
-        completionGroups.push(['NaN', 'Infinity', 'undefined',
-          'eval', 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'decodeURI',
-          'decodeURIComponent', 'encodeURI', 'encodeURIComponent',
-          'Object', 'Function', 'Array', 'String', 'Boolean', 'Number',
-          'Date', 'RegExp', 'Error', 'EvalError', 'RangeError',
-          'ReferenceError', 'SyntaxError', 'TypeError', 'URIError',
-          'Math', 'JSON']);
-        // Common keywords. Exclude for completion on the empty string, b/c
-        // they just get in the way.
-        if (filter) {
-          completionGroups.push(['break', 'case', 'catch', 'const',
-            'continue', 'debugger', 'default', 'delete', 'do', 'else', 'export',
-            'false', 'finally', 'for', 'function', 'if', 'import', 'in',
-            'instanceof', 'let', 'new', 'null', 'return', 'switch', 'this',
-            'throw', 'true', 'try', 'typeof', 'undefined', 'var', 'void',
-            'while', 'with', 'yield']);
+        if (this.context.constructor.name === 'Context') {
+          completionGroups.push(Object.getOwnPropertyNames(this.context));
+          next();
+        } else {
+          this.eval('.scope', this.context, 'repl', function(err, globals) {
+            if (Array.isArray(globals[0])) {
+              // Add grouped globals
+              globals.forEach(function(group) {
+                completionGroups.push(group);
+              });
+              finish();
+            } else {
+              completionGroups.push(globals);
+              next();
+            }
+          });
+        }
+
+        return;
+
+        function next() {
+          // Global object properties
+          // (http://www.ecma-international.org/publications/standards/Ecma-262.htm)
+          completionGroups.push(['NaN', 'Infinity', 'undefined',
+            'eval', 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'decodeURI',
+            'decodeURIComponent', 'encodeURI', 'encodeURIComponent',
+            'Object', 'Function', 'Array', 'String', 'Boolean', 'Number',
+            'Date', 'RegExp', 'Error', 'EvalError', 'RangeError',
+            'ReferenceError', 'SyntaxError', 'TypeError', 'URIError',
+            'Math', 'JSON']);
+          // Common keywords. Exclude for completion on the empty string, b/c
+          // they just get in the way.
+          if (filter) {
+            completionGroups.push(['break', 'case', 'catch', 'const',
+              'continue', 'debugger', 'default', 'delete', 'do', 'else', 'export',
+              'false', 'finally', 'for', 'function', 'if', 'import', 'in',
+              'instanceof', 'let', 'new', 'null', 'return', 'switch', 'this',
+              'throw', 'true', 'try', 'typeof', 'undefined', 'var', 'void',
+              'while', 'with', 'yield']);
+          }
+
+          finish();
         }
       } else {
         this.eval(expr, this.context, 'repl', function(e, obj) {