[debugger] readline => repl

Started porting to high-level javascript API and repl.
This commit is contained in:
Fedor Indutny 2011-09-07 03:08:48 +07:00
parent 4527de8cba
commit bd69afbc83

View File

@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE. // USE OR OTHER DEALINGS IN THE SOFTWARE.
var net = require('net'); var net = require('net');
var readline = require('readline'); var repl = require('repl');
var inherits = require('util').inherits; var inherits = require('util').inherits;
var spawn = require('child_process').spawn; 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" // "node debug"
function Interface() { function Interface() {
var self = this; var self = this,
var child; child;
var client;
function complete(line) {
return self.complete(line);
}
var term = readline.createInterface(process.stdin, process.stdout, complete);
this.term = term;
process.on('exit', function() { process.on('exit', function() {
self.killChild(); self.killChild();
@ -617,104 +609,45 @@ function Interface() {
this.stdin = process.openStdin(); this.stdin = process.openStdin();
term.setPrompt('debug> '); this.repl = repl.start('debug> ');
term.prompt();
// 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.quitting = false;
this.paused = 0;
process.on('SIGINT', function() { process.on('SIGINT', function() {
self.handleSIGINT(); 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() { Interface.prototype.pause = function() {
this.paused = true; if (this.paused++ > 0) return false;
this.stdin.pause(); this.stdin.pause();
this.term.pause(); this.repl.rli.pause();
}; };
Interface.prototype.resume = function() { Interface.prototype.resume = function() {
if (!this.paused) return false; if (this.paused === 0 || --this.paused !== 0) return false;
this.paused = false;
this.stdin.resume(); this.stdin.resume();
this.term.resume(); this.repl.rli.resume();
this.term.prompt(); process.stdout.write('\n');
return true; this.repl.displayPrompt();
}; };
Interface.prototype.handleSIGINT = function() {
this.child.kill('SIGINT');
};
Interface.prototype.handleBreak = function(r) { Interface.prototype.handleBreak = function(r) {
var result = ''; var result = '';
@ -745,8 +678,6 @@ Interface.prototype.handleBreak = function(r) {
this.client.currentScript = r.script.name; this.client.currentScript = r.script.name;
console.log(result); console.log(result);
if (!this.resume()) this.term.prompt();
}; };
@ -774,6 +705,46 @@ function leftPad(n) {
return s; 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) { Interface.prototype.handleCommand = function(cmd) {
var self = this; var self = this;
@ -786,29 +757,9 @@ Interface.prototype.handleCommand = function(cmd) {
self.tryQuit(); self.tryQuit();
} else if (/^r(un)?/.test(cmd)) { } else if (/^r(un)?/.test(cmd)) {
self._lastCommand = null; // DONE
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();
}
} else if (/^help/.test(cmd)) { } else if (/^help/.test(cmd)) {
console.log(helpMessage); // DONE
term.prompt();
} else if ('version' == cmd) { } else if ('version' == cmd) {
if (!client) { if (!client) {
self.printNotConnected(); self.printNotConnected();
@ -816,7 +767,6 @@ Interface.prototype.handleCommand = function(cmd) {
} }
client.reqVersion(function(v) { client.reqVersion(function(v) {
console.log(v); console.log(v);
term.prompt();
}); });
} else if (/info +breakpoints/.test(cmd)) { } else if (/info +breakpoints/.test(cmd)) {
@ -826,7 +776,6 @@ Interface.prototype.handleCommand = function(cmd) {
} }
client.listbreakpoints(function(res) { client.listbreakpoints(function(res) {
console.log(res); console.log(res);
term.prompt();
}); });
@ -864,7 +813,6 @@ Interface.prototype.handleCommand = function(cmd) {
console.log(leftPad(lineno) + ' ' + lines[i]); console.log(leftPad(lineno) + ' ' + lines[i]);
} }
} }
term.prompt();
}); });
} else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) { } else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) {
@ -894,7 +842,6 @@ Interface.prototype.handleCommand = function(cmd) {
console.log(text); console.log(text);
} }
term.prompt();
}); });
} else if (cmd == 'scripts' || cmd == 'scripts full') { } else if (cmd == 'scripts' || cmd == 'scripts full') {
@ -903,7 +850,6 @@ Interface.prototype.handleCommand = function(cmd) {
return; return;
} }
self.printScripts(cmd.indexOf('full') > 0); self.printScripts(cmd.indexOf('full') > 0);
term.prompt();
} else if (/^c(ontinue)?/.test(cmd)) { } else if (/^c(ontinue)?/.test(cmd)) {
if (!client) { if (!client) {
@ -931,7 +877,6 @@ Interface.prototype.handleCommand = function(cmd) {
} }
}); });
} else { } else {
self.term.prompt();
} }
} else if (/^next/.test(cmd) || /^n/.test(cmd)) { } else if (/^next/.test(cmd) || /^n/.test(cmd)) {
@ -960,19 +905,16 @@ Interface.prototype.handleCommand = function(cmd) {
var i = cmd.indexOf(' '); var i = cmd.indexOf(' ');
if (i < 0) { if (i < 0) {
console.log('print [expression]'); console.log('print [expression]');
term.prompt();
} else { } else {
cmd = cmd.slice(i); cmd = cmd.slice(i);
client.reqEval(cmd, function(res) { client.reqEval(cmd, function(res) {
if (!res.success) { if (!res.success) {
console.log(res.message); console.log(res.message);
term.prompt();
return; return;
} }
client.mirrorObject(res.body, function(mirror) { client.mirrorObject(res.body, function(mirror) {
console.log(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. // If it's not all white-space print this error message.
console.log('Unknown command "%s". Try "help"', cmd); 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() { Interface.prototype.killChild = function() {
if (this.child) { if (this.child) {
this.child.kill(); this.child.kill();
@ -1050,26 +958,25 @@ Interface.prototype.trySpawn = function(cb) {
var connectionAttempts = 0; var connectionAttempts = 0;
client.once('ready', function() { 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 // since we did debug-brk, we're hitting a break point immediately
// continue before anything else. // continue before anything else.
client.reqContinue(function() { client.reqContinue(function() {
self.resume();
if (cb) cb(); if (cb) cb();
}); });
client.on('close', function() { client.on('close', function() {
console.log('\nprogram terminated'); console.log('program terminated');
self.client = null; self.client = null;
self.killChild(); self.killChild();
if (!self.quitting) self.term.prompt();
}); });
}); });
client.on('unhandledResponse', function(res) { client.on('unhandledResponse', function(res) {
console.log('\r\nunhandled res:'); console.log('\r\nunhandled res:');
console.log(res); console.log(res);
self.term.prompt();
}); });
client.on('break', function(res) { 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 // argument full tells if it should display internal node scripts or not
Interface.prototype.printScripts = function(displayNatives) { Interface.prototype.printScripts = function(displayNatives) {
var client = this.client; var client = this.client;