Browse Source

Revert "Don't use a separate context for the repl."

This reverts commit b70fed48a7.
v0.7.4-release
isaacs 14 years ago
parent
commit
caf70f5e94
  1. 18
      doc/api/repl.markdown
  2. 116
      lib/repl.js
  3. 11
      test/common.js

18
doc/api/repl.markdown

@ -83,12 +83,28 @@ The special variable `_` (underscore) contains the result of the last expression
> _ += 1
4
The REPL provides access to any variables in the global scope.
The REPL provides access to any variables in the global scope. You can expose
a variable to the REPL explicitly by assigning it to the `context` object
associated with each `REPLServer`. For example:
// repl_test.js
var repl = require("repl"),
msg = "message";
repl.start().context.m = msg;
Things in the `context` object appear as local within the REPL:
mjr:~$ node repl_test.js
> m
'message'
There are a few special REPL commands:
- `.break` - While inputting a multi-line expression, sometimes you get lost
or just don't care about completing it. `.break` will start over.
- `.clear` - Resets the `context` object to an empty object and clears any
multi-line expression.
- `.exit` - Close the I/O stream, which will cause the REPL to exit.
- `.help` - Show this list of special commands.

116
lib/repl.js

@ -46,10 +46,6 @@ var path = require('path');
var fs = require('fs');
var rl = require('readline');
global.module = module;
global.exports = exports;
global.require = require;
// If obj.hasOwnProperty has been overridden, then calling
// obj.hasOwnProperty(prop) will break.
// See: https://github.com/joyent/node/issues/1707
@ -57,6 +53,9 @@ function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
var context;
exports.disableColors = process.env.NODE_DISABLE_COLORS ? true : false;
// hack for require.resolve("./relative") to work properly.
@ -72,27 +71,16 @@ exports.writer = util.inspect;
function REPLServer(prompt, stream, eval) {
var self = this;
var contextWarning;
Object.defineProperty(this, 'context', {
get: function() {
if (!contextWarning) {
contextWarning = 'repl.context is deprecated.';
console.error(contextWarning);
}
return global;
}
});
self.eval = eval || function(code, file, cb) {
self.eval = eval || function(code, context, file, cb) {
try {
var err, result = vm.runInThisContext(code, file);
var err, result = vm.runInContext(code, context, file);
} catch (e) {
err = e;
}
cb(err, result);
};
self.resetContext();
self.bufferedCommand = '';
if (stream) {
@ -185,13 +173,14 @@ function REPLServer(prompt, stream, eval) {
// First we attempt to eval as expression with parens.
// This catches '{a : 1}' properly.
self.eval('(' + evalCmd + ')',
self.context,
'repl',
function(e, ret) {
if (e && !isSyntaxError(e)) return finish(e);
if (typeof ret === 'function' || e) {
// Now as statement without parens.
self.eval(evalCmd, 'repl', finish);
self.eval(evalCmd, self.context, 'repl', finish);
} else {
finish(null, ret);
}
@ -228,8 +217,8 @@ function REPLServer(prompt, stream, eval) {
self.bufferedCommand = '';
// If we got any output - print it (if no error)
if (!e) {
global._ = ret;
if (!e && ret !== undefined) {
self.context._ = ret;
self.outputStream.write(exports.writer(ret) + '\n');
}
@ -256,12 +245,25 @@ exports.start = function(prompt, source, eval) {
};
var resetWarning;
REPLServer.prototype.createContext = function() {
var context = vm.createContext();
for (var i in global) context[i] = global[i];
context.module = module;
context.require = require;
context.global = context;
context.global.global = context;
return context;
};
REPLServer.prototype.resetContext = function(force) {
if (!resetWarning) {
resetWarning = 'REPLServer.resetContext is deprecated.';
console.error(resetWarning);
if (!context || force) {
context = this.createContext();
for (var i in require.cache) delete require.cache[i];
}
this.context = context;
};
REPLServer.prototype.displayPrompt = function() {
@ -411,9 +413,26 @@ REPLServer.prototype.complete = function(line, callback) {
if (!expr) {
// If context is instance of vm.ScriptContext
// Get global vars synchronously
completionGroups.push(Object.getOwnPropertyNames(global));
addStandardGlobals();
completionGroupsLoaded();
if (this.context.constructor.name === 'Context') {
completionGroups.push(Object.getOwnPropertyNames(this.context));
addStandardGlobals();
completionGroupsLoaded();
} else {
this.eval('.scope', this.context, 'repl', function(err, globals) {
if (err || !globals) {
addStandardGlobals();
} else if (Array.isArray(globals[0])) {
// Add grouped globals
globals.forEach(function(group) {
completionGroups.push(group);
});
} else {
completionGroups.push(globals);
addStandardGlobals();
}
completionGroupsLoaded();
});
}
function addStandardGlobals() {
// Global object properties
@ -438,7 +457,7 @@ REPLServer.prototype.complete = function(line, callback) {
}
} else {
this.eval(expr, 'repl', function(e, obj) {
this.eval(expr, this.context, 'repl', function(e, obj) {
// if (e) console.log(e);
if (obj != null) {
@ -565,6 +584,16 @@ function defineDefaultCommands(repl) {
}
});
repl.defineCommand('clear', {
help: 'Break, and also clear the local context',
action: function() {
this.outputStream.write('Clearing context...\n');
this.bufferedCommand = '';
this.resetContext(true);
this.displayPrompt();
}
});
repl.defineCommand('exit', {
help: 'Exit the repl',
action: function() {
@ -599,3 +628,32 @@ function trimWhitespace(cmd) {
function regexpEscape(s) {
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
/**
* Converts commands that use var and function <name>() to use the
* local exports.context when evaled. This provides a local context
* on the REPL.
*
* @param {String} cmd The cmd to convert.
* @return {String} The converted command.
*/
REPLServer.prototype.convertToContext = function(cmd) {
var self = this, matches,
scopeVar = /^\s*var\s*([_\w\$]+)(.*)$/m,
scopeFunc = /^\s*function\s*([_\w\$]+)/;
// Replaces: var foo = "bar"; with: self.context.foo = bar;
matches = scopeVar.exec(cmd);
if (matches && matches.length === 3) {
return 'self.context.' + matches[1] + matches[2];
}
// Replaces: function foo() {}; with: foo = function foo() {};
matches = scopeFunc.exec(self.bufferedCommand);
if (matches && matches.length === 2) {
return matches[1] + ' = ' + self.bufferedCommand;
}
return cmd;
};

11
test/common.js

@ -123,17 +123,6 @@ process.on('exit', function() {
knownGlobals.push(DataView);
}
// repl pollution
if (global.hasOwnProperty('module')) {
knownGlobals.push(global.module);
}
if (global.hasOwnProperty('require')) {
knownGlobals.push(global.require);
}
if (global.hasOwnProperty('exports')) {
knownGlobals.push(global.exports);
}
for (var x in global) {
var found = false;

Loading…
Cancel
Save