diff --git a/lib/_debugger.js b/lib/_debugger.js index 4ae19d542e7..afee9b4a5b3 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -20,7 +20,7 @@ // 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; @@ -597,19 +597,11 @@ function SourceInfo(body) { } -// 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(); @@ -617,104 +609,45 @@ function Interface() { this.stdin = process.openStdin(); - term.setPrompt('debug> '); - term.prompt(); + this.repl = repl.start('debug> '); + + // Lift all instance methods to repl context + var proto = Interface.prototype, + ignored = ['pause', 'resume', 'handleSIGINT']; + + for (var i in proto) { + if (proto.hasOwnProperty(i) && ignored.indexOf(i) === -1) { + this.repl.context[i] = proto[i].bind(this); + } + } this.quitting = false; + this.paused = 0; 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); - } - }); -} - - -Interface.prototype.complete = function(line) { - // Match me with a command. - var matches = []; - // Remove leading whitespace - line = line.replace(/^\s*/, ''); - - for (var i = 0; i < commands.length; i++) { - if (commands[i].indexOf(line) === 0) { - matches.push(commands[i]); - } - } - - 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; - - if (self.child) { - self.quitQuestion(function(yes) { - if (yes) { - self.quit(); - } else { - self.term.prompt(); - } - }); - } else { - self.quit(); - } -}; - - 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 = ''; @@ -745,8 +678,6 @@ Interface.prototype.handleBreak = function(r) { this.client.currentScript = r.script.name; console.log(result); - - if (!this.resume()) this.term.prompt(); }; @@ -774,6 +705,46 @@ function leftPad(n) { 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; @@ -786,29 +757,9 @@ Interface.prototype.handleCommand = function(cmd) { 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(); @@ -816,7 +767,6 @@ Interface.prototype.handleCommand = function(cmd) { } client.reqVersion(function(v) { console.log(v); - term.prompt(); }); } else if (/info +breakpoints/.test(cmd)) { @@ -826,7 +776,6 @@ Interface.prototype.handleCommand = function(cmd) { } client.listbreakpoints(function(res) { console.log(res); - term.prompt(); }); @@ -864,7 +813,6 @@ Interface.prototype.handleCommand = function(cmd) { console.log(leftPad(lineno) + ' ' + lines[i]); } } - term.prompt(); }); } else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) { @@ -894,7 +842,6 @@ Interface.prototype.handleCommand = function(cmd) { console.log(text); } - term.prompt(); }); } else if (cmd == 'scripts' || cmd == 'scripts full') { @@ -903,7 +850,6 @@ Interface.prototype.handleCommand = function(cmd) { return; } self.printScripts(cmd.indexOf('full') > 0); - term.prompt(); } else if (/^c(ontinue)?/.test(cmd)) { if (!client) { @@ -931,7 +877,6 @@ Interface.prototype.handleCommand = function(cmd) { } }); } else { - self.term.prompt(); } } else if (/^next/.test(cmd) || /^n/.test(cmd)) { @@ -960,19 +905,16 @@ Interface.prototype.handleCommand = function(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(); }); }); } @@ -982,45 +924,11 @@ Interface.prototype.handleCommand = function(cmd) { // 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(); @@ -1050,26 +958,25 @@ Interface.prototype.trySpawn = function(cb) { 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) { @@ -1098,12 +1005,6 @@ Interface.prototype.trySpawn = function(cb) { }; -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;