|
|
@ -64,9 +64,18 @@ module.paths = require('module')._nodeModulePaths(module.filename); |
|
|
|
exports.writer = util.inspect; |
|
|
|
|
|
|
|
|
|
|
|
function REPLServer(prompt, stream) { |
|
|
|
function REPLServer(prompt, stream, options) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self.eval = options && options.eval || function(code, context, file, cb) { |
|
|
|
try { |
|
|
|
var err, result = vm.runInContext(code, context, file); |
|
|
|
} catch (e) { |
|
|
|
err = e; |
|
|
|
} |
|
|
|
cb(err, result); |
|
|
|
}; |
|
|
|
|
|
|
|
self.resetContext(); |
|
|
|
self.bufferedCommand = ''; |
|
|
|
|
|
|
@ -82,8 +91,8 @@ function REPLServer(prompt, stream) { |
|
|
|
|
|
|
|
self.prompt = (prompt != undefined ? prompt : '> '); |
|
|
|
|
|
|
|
function complete(text) { |
|
|
|
return self.complete(text); |
|
|
|
function complete(text, callback) { |
|
|
|
self.complete(text, callback); |
|
|
|
} |
|
|
|
|
|
|
|
var rli = rl.createInterface(self.inputStream, self.outputStream, complete); |
|
|
@ -140,68 +149,85 @@ function REPLServer(prompt, stream) { |
|
|
|
} |
|
|
|
|
|
|
|
if (!skipCatchall) { |
|
|
|
// The catchall for errors
|
|
|
|
try { |
|
|
|
self.bufferedCommand += cmd + '\n'; |
|
|
|
// This try is for determining if the command is complete, or should
|
|
|
|
// continue onto the next line.
|
|
|
|
try { |
|
|
|
// We try to evaluate both expressions e.g.
|
|
|
|
// '{ a : 1 }'
|
|
|
|
// and statements e.g.
|
|
|
|
// 'for (var i = 0; i < 10; i++) console.log(i);'
|
|
|
|
|
|
|
|
var ret, success = false; |
|
|
|
try { |
|
|
|
// First we attempt to eval as expression with parens.
|
|
|
|
// This catches '{a : 1}' properly.
|
|
|
|
ret = vm.runInContext('(' + self.bufferedCommand + ')', |
|
|
|
self.context, |
|
|
|
'repl'); |
|
|
|
if (typeof ret !== 'function') success = true; |
|
|
|
} catch (e) { |
|
|
|
if (!(e && e.constructor && e.constructor.name === 'SyntaxError')) { |
|
|
|
throw e; |
|
|
|
self.bufferedCommand += cmd + '\n'; |
|
|
|
// This try is for determining if the command is complete, or should
|
|
|
|
// continue onto the next line.
|
|
|
|
// We try to evaluate both expressions e.g.
|
|
|
|
// '{ a : 1 }'
|
|
|
|
// and statements e.g.
|
|
|
|
// 'for (var i = 0; i < 10; i++) console.log(i);'
|
|
|
|
|
|
|
|
// 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 { |
|
|
|
success = false; |
|
|
|
tryExpr(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!success) { |
|
|
|
// Now as statement without parens.
|
|
|
|
ret = vm.runInContext(self.bufferedCommand, self.context, 'repl'); |
|
|
|
if (typeof ret !== 'function') { |
|
|
|
return tryExpr(ret); |
|
|
|
} |
|
|
|
|
|
|
|
tryExpr(); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
// Now as statement without parens.
|
|
|
|
function tryExpr(ret) { |
|
|
|
self.eval(self.bufferedCommand, self.context, |
|
|
|
'repl', function(e, ret) { |
|
|
|
|
|
|
|
if (ret !== undefined) { |
|
|
|
self.context._ = ret; |
|
|
|
self.outputStream.write(exports.writer(ret) + '\n'); |
|
|
|
} |
|
|
|
|
|
|
|
self.bufferedCommand = ''; |
|
|
|
} catch (e) { |
|
|
|
// instanceof doesn't work across context switches.
|
|
|
|
if (!(e && e.constructor && e.constructor.name === 'SyntaxError')) { |
|
|
|
throw 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/)) { |
|
|
|
throw e; |
|
|
|
|
|
|
|
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); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
// On error: Print the error and clear the buffer
|
|
|
|
finish(null, ret); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
return tryParens(); |
|
|
|
} |
|
|
|
|
|
|
|
finish(null); |
|
|
|
function finish(e, ret) { |
|
|
|
if (e) { |
|
|
|
if (e.stack) { |
|
|
|
self.outputStream.write(e.stack + '\n'); |
|
|
|
} else { |
|
|
|
self.outputStream.write(e.toString() + '\n'); |
|
|
|
} |
|
|
|
// On error: Print the error and clear the buffer
|
|
|
|
self.bufferedCommand = ''; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self.displayPrompt(); |
|
|
|
self.displayPrompt(); |
|
|
|
}; |
|
|
|
}); |
|
|
|
|
|
|
|
rli.addListener('close', function() { |
|
|
@ -269,7 +295,7 @@ var simpleExpressionRE = |
|
|
|
//
|
|
|
|
// Warning: This eval's code like "foo.bar.baz", so it will run property
|
|
|
|
// getter code.
|
|
|
|
REPLServer.prototype.complete = function(line) { |
|
|
|
REPLServer.prototype.complete = function(line, callback) { |
|
|
|
var completions; |
|
|
|
|
|
|
|
// list of completion lists, one for each inheritance "level"
|
|
|
@ -406,84 +432,90 @@ REPLServer.prototype.complete = function(line) { |
|
|
|
'while', 'with', 'yield']); |
|
|
|
} |
|
|
|
} else { |
|
|
|
try { |
|
|
|
obj = vm.runInContext(expr, this.context, 'repl'); |
|
|
|
} catch (e) { |
|
|
|
//console.log("completion eval error, expr='"+expr+"': "+e);
|
|
|
|
} |
|
|
|
if (obj != null) { |
|
|
|
if (typeof obj === 'object' || typeof obj === 'function') { |
|
|
|
memberGroups.push(Object.getOwnPropertyNames(obj)); |
|
|
|
} |
|
|
|
// works for non-objects
|
|
|
|
try { |
|
|
|
var p = Object.getPrototypeOf(obj); |
|
|
|
var sentinel = 5; |
|
|
|
while (p !== null) { |
|
|
|
memberGroups.push(Object.getOwnPropertyNames(p)); |
|
|
|
p = Object.getPrototypeOf(p); |
|
|
|
// Circular refs possible? Let's guard against that.
|
|
|
|
sentinel--; |
|
|
|
if (sentinel <= 0) { |
|
|
|
break; |
|
|
|
this.eval(expr, this.context, 'repl', function(e, obj) { |
|
|
|
// if (e) console.log(e);
|
|
|
|
|
|
|
|
if (obj != null) { |
|
|
|
if (typeof obj === 'object' || typeof obj === 'function') { |
|
|
|
memberGroups.push(Object.getOwnPropertyNames(obj)); |
|
|
|
} |
|
|
|
// works for non-objects
|
|
|
|
try { |
|
|
|
var p = Object.getPrototypeOf(obj); |
|
|
|
var sentinel = 5; |
|
|
|
while (p !== null) { |
|
|
|
memberGroups.push(Object.getOwnPropertyNames(p)); |
|
|
|
p = Object.getPrototypeOf(p); |
|
|
|
// Circular refs possible? Let's guard against that.
|
|
|
|
sentinel--; |
|
|
|
if (sentinel <= 0) { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
//console.log("completion error walking prototype chain:" + e);
|
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
//console.log("completion error walking prototype chain:" + e);
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (memberGroups.length) { |
|
|
|
for (i = 0; i < memberGroups.length; i++) { |
|
|
|
completionGroups.push(memberGroups[i].map(function(member) { |
|
|
|
return expr + '.' + member; |
|
|
|
})); |
|
|
|
} |
|
|
|
if (filter) { |
|
|
|
filter = expr + '.' + filter; |
|
|
|
if (memberGroups.length) { |
|
|
|
for (i = 0; i < memberGroups.length; i++) { |
|
|
|
completionGroups.push(memberGroups[i].map(function(member) { |
|
|
|
return expr + '.' + member; |
|
|
|
})); |
|
|
|
} |
|
|
|
if (filter) { |
|
|
|
filter = expr + '.' + filter; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Filter, sort (within each group), uniq and merge the completion groups.
|
|
|
|
if (completionGroups.length && filter) { |
|
|
|
var newCompletionGroups = []; |
|
|
|
for (i = 0; i < completionGroups.length; i++) { |
|
|
|
group = completionGroups[i].filter(function(elem) { |
|
|
|
return elem.indexOf(filter) == 0; |
|
|
|
}); |
|
|
|
if (group.length) { |
|
|
|
newCompletionGroups.push(group); |
|
|
|
// If reach this point - work like sync
|
|
|
|
finish(null, ret); |
|
|
|
function finish(err, ret) { |
|
|
|
if (err) throw err; |
|
|
|
|
|
|
|
// Filter, sort (within each group), uniq and merge the completion groups.
|
|
|
|
if (completionGroups.length && filter) { |
|
|
|
var newCompletionGroups = []; |
|
|
|
for (i = 0; i < completionGroups.length; i++) { |
|
|
|
group = completionGroups[i].filter(function(elem) { |
|
|
|
return elem.indexOf(filter) == 0; |
|
|
|
}); |
|
|
|
if (group.length) { |
|
|
|
newCompletionGroups.push(group); |
|
|
|
} |
|
|
|
} |
|
|
|
completionGroups = newCompletionGroups; |
|
|
|
} |
|
|
|
completionGroups = newCompletionGroups; |
|
|
|
} |
|
|
|
|
|
|
|
if (completionGroups.length) { |
|
|
|
var uniq = {}; // unique completions across all groups
|
|
|
|
completions = []; |
|
|
|
// Completion group 0 is the "closest" (least far up the inheritance chain)
|
|
|
|
// so we put its completions last: to be closest in the REPL.
|
|
|
|
for (i = completionGroups.length - 1; i >= 0; i--) { |
|
|
|
group = completionGroups[i]; |
|
|
|
group.sort(); |
|
|
|
for (var j = 0; j < group.length; j++) { |
|
|
|
c = group[j]; |
|
|
|
if (!uniq.hasOwnProperty(c)) { |
|
|
|
completions.push(c); |
|
|
|
uniq[c] = true; |
|
|
|
if (completionGroups.length) { |
|
|
|
var uniq = {}; // unique completions across all groups
|
|
|
|
completions = []; |
|
|
|
// Completion group 0 is the "closest"
|
|
|
|
// (least far up the inheritance chain)
|
|
|
|
// so we put its completions last: to be closest in the REPL.
|
|
|
|
for (i = completionGroups.length - 1; i >= 0; i--) { |
|
|
|
group = completionGroups[i]; |
|
|
|
group.sort(); |
|
|
|
for (var j = 0; j < group.length; j++) { |
|
|
|
c = group[j]; |
|
|
|
if (!uniq.hasOwnProperty(c)) { |
|
|
|
completions.push(c); |
|
|
|
uniq[c] = true; |
|
|
|
} |
|
|
|
} |
|
|
|
completions.push(''); // separator btwn groups
|
|
|
|
} |
|
|
|
while (completions.length && completions[completions.length - 1] === '') { |
|
|
|
completions.pop(); |
|
|
|
} |
|
|
|
completions.push(''); // separator btwn groups
|
|
|
|
} |
|
|
|
while (completions.length && completions[completions.length - 1] === '') { |
|
|
|
completions.pop(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return [completions || [], completeOn]; |
|
|
|
callback(null, [completions || [], completeOn]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|