// First we attempt to eval as expression with parens.
// This catches '{a : 1}' properly.
- function tryParens() {
- var success = false;
-
- self.eval('(' + self.bufferedCommand + ')',
- self.context,
- 'repl',
- function(e, ret) {
- if (e) {
- if (!(e && e.constructor &&
- e.constructor.name === 'SyntaxError')) {
- finish(e);
- } else {
- tryExpr(e);
- }
- return;
- }
-
- tryExpr(typeof ret === 'function', ret);
- });
- };
-
- // Now as statement without parens.
- 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')) {
- return finish(e);
- // 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;
- }
- }
+ self.eval('(' + self.bufferedCommand + ')',
+ self.context,
+ 'repl',
+ function(e, ret) {
+ if (e) return finish(e);
+
+ if (ret === 'function' || e) {
+ // Now as statement without parens.
+ self.eval(self.bufferedCommand, self.context, 'repl', finish);
+ } else {
finish(null, ret);
- });
- };
+ }
+ });
- return tryParens();
+ } else {
+ finish(null);
}
- finish(null);
function finish(e, ret) {
- 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 = '';
+ // Convert error to string
+ e = e && (e.stack || e.toString());
+
+ // If error was SyntaxError and not JSON.parse error
+ if (e && e.match(/^SyntaxError/) &&
+ !(e.match(/^SyntaxError: Unexpected token .*\n/) &&
+ e.match(/\n at Object.parse \(native\)\n/))) {
+ // Start buffering data like that:
+ // {
+ // ... x: 1
+ // ... }
+ self.displayPrompt();
+ return;
+ } else if (e) {
+ self.outputStream.write(e + '\n');
}
- if (ret !== undefined) {
+ // Clear buffer if no SyntaxErrors
+ self.bufferedCommand = '';
+
+ // If we got any output - print it (if no error)
+ if (!e && ret !== undefined) {
self.context._ = ret;
self.outputStream.write(exports.writer(ret) + '\n');
}
+ // Display prompt again
self.displayPrompt();
};
});
filter = match[1];
}
+ completionGroupsLoaded();
} else if (match = line.match(requireRE)) {
// require('...<Tab>')
//TODO: suggest require.exts be exposed to be introspec registered
completionGroups.push(builtinLibs);
}
+ completionGroupsLoaded();
+
// Handle variable member lookup.
// We support simple chained expressions like the following (no function
// calls, etc.). That is for simplicity and also because we *eval* that
// Resolve expr and get its completions.
var obj, memberGroups = [];
if (!expr) {
+ // If context is instance of vm.ScriptContext
+ // Get global vars synchronously
if (this.context.constructor.name === 'Context') {
completionGroups.push(Object.getOwnPropertyNames(this.context));
- next();
+ addStandardGlobals();
+ completionGroupsLoaded();
} else {
this.eval('.scope', this.context, 'repl', function(err, globals) {
if (Array.isArray(globals[0])) {
globals.forEach(function(group) {
completionGroups.push(group);
});
- finish();
} else {
completionGroups.push(globals);
- next();
+ addStandardGlobals();
}
+ completionGroupsLoaded();
});
}
- return;
-
- function next() {
+ function addStandardGlobals() {
// Global object properties
// (http://www.ecma-international.org/publications/standards/Ecma-262.htm)
completionGroups.push(['NaN', 'Infinity', 'undefined',
'throw', 'true', 'try', 'typeof', 'undefined', 'var', 'void',
'while', 'with', 'yield']);
}
-
- finish();
}
+
} else {
this.eval(expr, this.context, 'repl', function(e, obj) {
// if (e) console.log(e);
}
}
- finish();
+ completionGroupsLoaded();
});
- return;
}
+ } else {
+ completionGroupsLoaded();
}
}
- // If reach this point - work like sync
- finish(null);
- function finish(err, ret) {
+ // Will be called when all completionGroups are in place
+ // Useful for async autocompletion
+ function completionGroupsLoaded(err) {
if (err) throw err;
// Filter, sort (within each group), uniq and merge the completion groups.