[repl, readline] refactor async completion and execution
This commit is contained in:
parent
2c2397d333
commit
e13ed4a8d0
@ -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) {
|
||||||
|
125
lib/repl.js
125
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() {
|
self.eval('(' + self.bufferedCommand + ')',
|
||||||
var success = false;
|
self.context,
|
||||||
|
'repl',
|
||||||
|
function(e, ret) {
|
||||||
|
if (e) return finish(e);
|
||||||
|
|
||||||
self.eval('(' + self.bufferedCommand + ')',
|
if (ret === 'function' || e) {
|
||||||
self.context,
|
// Now as statement without parens.
|
||||||
'repl',
|
self.eval(self.bufferedCommand, self.context, 'repl', finish);
|
||||||
function(e, ret) {
|
} else {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
||||||
self.displayPrompt();
|
if (e && e.match(/^SyntaxError/) &&
|
||||||
return;
|
!(e.match(/^SyntaxError: Unexpected token .*\n/) &&
|
||||||
} else {
|
e.match(/\n at Object.parse \(native\)\n/))) {
|
||||||
self.outputStream.write(e.toString() + '\n');
|
// Start buffering data like that:
|
||||||
}
|
// {
|
||||||
// On error: Print the error and clear the buffer
|
// ... x: 1
|
||||||
self.bufferedCommand = '';
|
// ... }
|
||||||
} else {
|
self.displayPrompt();
|
||||||
self.bufferedCommand = '';
|
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.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…
x
Reference in New Issue
Block a user