Browse Source

[debugger] readline => repl

Started porting to high-level javascript API and repl.
Fedor Indutny 14 years ago
parent
commit
bd69afbc83
  1. 241
      lib/_debugger.js

241
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,105 +609,46 @@ function Interface() {
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();
});
this.repl = repl.start('debug> ');
term.on('line', function(cmd) {
// trim whitespace
cmd = cmd.replace(/^\s*/, '').replace(/\s*$/, '');
// Lift all instance methods to repl context
var proto = Interface.prototype,
ignored = ['pause', 'resume', 'handleSIGINT'];
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]);
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();
}
process.on('SIGINT', function() {
self.handleSIGINT();
});
} 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 = '';
if (r.breakpoints) {
@ -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,44 +924,10 @@ 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) {
@ -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;

Loading…
Cancel
Save