Browse Source

[repl, readline] refactor async completion and execution

Fedor Indutny 13 years ago
parent
commit
e13ed4a8d0
  1. 4
      lib/readline.js
  2. 105
      lib/repl.js

4
lib/readline.js

@ -250,8 +250,8 @@ Interface.prototype._insertString = function(c) {
Interface.prototype._tabComplete = function() { Interface.prototype._tabComplete = function() {
var self = this; var self = this;
this.pause(); self.pause();
this.completer(self.line.slice(0, self.cursor), function(err, rv) { self.completer(self.line.slice(0, self.cursor), function(err, rv) {
self.resume(); self.resume();
if (err) { if (err) {

105
lib/repl.js

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

Loading…
Cancel
Save