// USE OR OTHER DEALINGS IN THE SOFTWARE.
var net = require('net');
-var readline = require('readline');
+var repl = require('repl');
var inherits = require('util').inherits;
var spawn = require('child_process').spawn;
}
-// This class is the readline-enabled debugger interface which is invoked on
+// This class is the repl-enabled debugger interface which is invoked on
// "node debug"
function Interface() {
- var self = this;
- var child;
- var client;
-
- function complete(line) {
- return self.complete(line);
- }
-
- var term = readline.createInterface(process.stdin, process.stdout, complete);
- this.term = term;
+ var self = this,
+ child;
process.on('exit', function() {
self.killChild();
this.stdin = process.openStdin();
- term.setPrompt('debug> ');
- term.prompt();
-
- this.quitting = false;
-
- process.on('SIGINT', function() {
- self.handleSIGINT();
- });
-
- term.on('SIGINT', function() {
- self.handleSIGINT();
- });
-
- term.on('attemptClose', function() {
- self.tryQuit();
- });
-
- term.on('line', function(cmd) {
- // trim whitespace
- cmd = cmd.replace(/^\s*/, '').replace(/\s*$/, '');
-
- if (cmd.length) {
- self._lastCommand = cmd;
- self.handleCommand(cmd);
- } else {
- self.handleCommand(self._lastCommand);
- }
- });
-}
-
+ this.repl = repl.start('debug> ');
-Interface.prototype.complete = function(line) {
- // Match me with a command.
- var matches = [];
- // Remove leading whitespace
- line = line.replace(/^\s*/, '');
+ // Lift all instance methods to repl context
+ var proto = Interface.prototype,
+ ignored = ['pause', 'resume', 'handleSIGINT'];
- for (var i = 0; i < commands.length; i++) {
- if (commands[i].indexOf(line) === 0) {
- matches.push(commands[i]);
+ for (var i in proto) {
+ if (proto.hasOwnProperty(i) && ignored.indexOf(i) === -1) {
+ this.repl.context[i] = proto[i].bind(this);
}
}
- return [matches, line];
-};
-
-
-Interface.prototype.handleSIGINT = function() {
- if (this.paused) {
- this.child.kill('SIGINT');
- } else {
- this.tryQuit();
- }
-};
-
-
-Interface.prototype.quit = function() {
- if (this.quitting) return;
- this.quitting = true;
- this.killChild();
- this.term.close();
- process.exit(0);
-};
-
-
-Interface.prototype.tryQuit = function() {
- var self = this;
+ this.quitting = false;
+ this.paused = 0;
- if (self.child) {
- self.quitQuestion(function(yes) {
- if (yes) {
- self.quit();
- } else {
- self.term.prompt();
- }
- });
- } else {
- self.quit();
- }
+ process.on('SIGINT', function() {
+ self.handleSIGINT();
+ });
};
-
Interface.prototype.pause = function() {
- this.paused = true;
+ if (this.paused++ > 0) return false;
this.stdin.pause();
- this.term.pause();
+ this.repl.rli.pause();
};
-
Interface.prototype.resume = function() {
- if (!this.paused) return false;
- this.paused = false;
+ if (this.paused === 0 || --this.paused !== 0) return false;
this.stdin.resume();
- this.term.resume();
- this.term.prompt();
- return true;
+ this.repl.rli.resume();
+ process.stdout.write('\n');
+ this.repl.displayPrompt();
};
+Interface.prototype.handleSIGINT = function() {
+ this.child.kill('SIGINT');
+};
+
+
Interface.prototype.handleBreak = function(r) {
var result = '';
this.client.currentScript = r.script.name;
console.log(result);
-
- if (!this.resume()) this.term.prompt();
};
return s;
}
+Interface.prototype.help = function() {
+ this.pause();
+ process.stdout.write(helpMessage);
+ this.resume();
+};
+
+Interface.prototype.run = function() {
+ if (this.child) {
+ throw Error('App is already running... Try `restart()` instead');
+ } else {
+ this.trySpawn();
+ }
+};
+
+Interface.prototype.restart = function() {
+ if (!this.child) throw Error('App isn\'t running... Try `run()` instead');
+
+ var self = this;
+
+ this.killChild();
+
+ // XXX need to wait a little bit for the restart to work?
+ setTimeout(function() {
+ self.trySpawn();
+ }, 1000);
+};
+
+Interface.prototype.version = function() {
+ if (!this.child) throw Error('App isn\'t running... Try `run()` instead');
+
+ var self = this;
+
+ this.pause();
+ this.client.reqVersion(function(v) {
+ process.stdout.write(v);
+ self.resume();
+ });
+};
+
+
Interface.prototype.handleCommand = function(cmd) {
var self = this;
self.tryQuit();
} else if (/^r(un)?/.test(cmd)) {
- self._lastCommand = null;
- if (self.child) {
- self.restartQuestion(function(yes) {
- if (!yes) {
- self._lastCommand = null;
- term.prompt();
- } else {
- console.log('restarting...');
- self.killChild();
- // XXX need to wait a little bit for the restart to work?
- setTimeout(function() {
- self.trySpawn();
- }, 1000);
- }
- });
- } else {
- self.trySpawn();
- }
-
+ // DONE
} else if (/^help/.test(cmd)) {
- console.log(helpMessage);
- term.prompt();
-
+ // DONE
} else if ('version' == cmd) {
if (!client) {
self.printNotConnected();
}
client.reqVersion(function(v) {
console.log(v);
- term.prompt();
});
} else if (/info +breakpoints/.test(cmd)) {
}
client.listbreakpoints(function(res) {
console.log(res);
- term.prompt();
});
console.log(leftPad(lineno) + ' ' + lines[i]);
}
}
- term.prompt();
});
} else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) {
console.log(text);
}
- term.prompt();
});
} else if (cmd == 'scripts' || cmd == 'scripts full') {
return;
}
self.printScripts(cmd.indexOf('full') > 0);
- term.prompt();
} else if (/^c(ontinue)?/.test(cmd)) {
if (!client) {
}
});
} else {
- self.term.prompt();
}
} else if (/^next/.test(cmd) || /^n/.test(cmd)) {
var i = cmd.indexOf(' ');
if (i < 0) {
console.log('print [expression]');
- term.prompt();
} else {
cmd = cmd.slice(i);
client.reqEval(cmd, function(res) {
if (!res.success) {
console.log(res.message);
- term.prompt();
return;
}
client.mirrorObject(res.body, function(mirror) {
console.log(mirror);
- term.prompt();
});
});
}
// If it's not all white-space print this error message.
console.log('Unknown command "%s". Try "help"', cmd);
}
- term.prompt();
}
};
-Interface.prototype.yesNoQuestion = function(prompt, cb) {
- var self = this;
- self.resume();
- this.term.question(prompt, function(answer) {
- if (/^y(es)?$/i.test(answer)) {
- cb(true);
- } else if (/^n(o)?$/i.test(answer)) {
- cb(false);
- } else {
- console.log('Please answer y or n.');
- self.yesNoQuestion(prompt, cb);
- }
- });
-};
-
-
-Interface.prototype.restartQuestion = function(cb) {
- this.yesNoQuestion('The program being debugged has been started already.\n' +
- 'Start it from the beginning? (y or n) ', cb);
-};
-
-
-Interface.prototype.killQuestion = function(cb) {
- this.yesNoQuestion('Kill the program being debugged? (y or n) ', cb);
-};
-
-
-Interface.prototype.quitQuestion = function(cb) {
- this.yesNoQuestion('A debugging session is active. Quit anyway? (y or n) ',
- cb);
-};
-
-
Interface.prototype.killChild = function() {
if (this.child) {
this.child.kill();
var connectionAttempts = 0;
client.once('ready', function() {
- process.stdout.write(' ok\r\n');
+ process.stdout.write(' ok');
// since we did debug-brk, we're hitting a break point immediately
// continue before anything else.
client.reqContinue(function() {
+ self.resume();
if (cb) cb();
});
client.on('close', function() {
- console.log('\nprogram terminated');
+ console.log('program terminated');
self.client = null;
self.killChild();
- if (!self.quitting) self.term.prompt();
});
});
client.on('unhandledResponse', function(res) {
console.log('\r\nunhandled res:');
console.log(res);
- self.term.prompt();
});
client.on('break', function(res) {
};
-Interface.prototype.printNotConnected = function() {
- console.log("Program not running. Try 'run'.");
- this.term.prompt();
-};
-
-
// argument full tells if it should display internal node scripts or not
Interface.prototype.printScripts = function(displayNatives) {
var client = this.client;