- Removed extra newline from .question(); Users can input a newline if it they require it. - Removed .close() due to it only emulating closing, causing a bug where readline is left open to trigger events such as .on('line', ...'). - Removed ._attemptClose() - .pause() now triggers event .on('pause', ...) - .resume() now triggers event .on('resume', ...) - CTRL-C (SIGINT) in readline will now default to .pause() if no SIGINT event is present. - CTRL-D (delete right) will also default to .pause() if there is nothing to delete (signaling the end of the file). - Added new event `SIGTSTP` - Added new event `SIGCONT` - Added `resume` to `write` to resume the stream if paused. - Docs updated. - Updated repl.js
This commit is contained in:
parent
3bc3af0276
commit
ce485791db
@ -4,8 +4,8 @@ To use this module, do `require('readline')`. Readline allows reading of a
|
|||||||
stream (such as STDIN) on a line-by-line basis.
|
stream (such as STDIN) on a line-by-line basis.
|
||||||
|
|
||||||
Note that once you've invoked this module, your node program will not
|
Note that once you've invoked this module, your node program will not
|
||||||
terminate until you've closed the interface, and the STDIN stream. Here's how
|
terminate until you've paused the interface. Here's how to allow your
|
||||||
to allow your program to gracefully terminate:
|
program to gracefully pause:
|
||||||
|
|
||||||
var rl = require('readline');
|
var rl = require('readline');
|
||||||
|
|
||||||
@ -14,10 +14,7 @@ to allow your program to gracefully terminate:
|
|||||||
// TODO: Log the answer in a database
|
// TODO: Log the answer in a database
|
||||||
console.log("Thank you for your valuable feedback.");
|
console.log("Thank you for your valuable feedback.");
|
||||||
|
|
||||||
// These two lines together allow the program to terminate. Without
|
i.pause();
|
||||||
// them, it would run forever.
|
|
||||||
i.close();
|
|
||||||
process.stdin.destroy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
### rl.createInterface(input, output, completer)
|
### rl.createInterface(input, output, completer)
|
||||||
@ -48,6 +45,9 @@ Sets the prompt, for example when you run `node` on the command line, you see
|
|||||||
Readies readline for input from the user, putting the current `setPrompt`
|
Readies readline for input from the user, putting the current `setPrompt`
|
||||||
options on a new line, giving the user a new spot to write.
|
options on a new line, giving the user a new spot to write.
|
||||||
|
|
||||||
|
This will also resume the `in` stream used with `createInterface` if it has
|
||||||
|
been paused.
|
||||||
|
|
||||||
<!-- ### rl.getColumns() Not available? -->
|
<!-- ### rl.getColumns() Not available? -->
|
||||||
|
|
||||||
### rl.question(query, callback)
|
### rl.question(query, callback)
|
||||||
@ -56,27 +56,29 @@ Prepends the prompt with `query` and invokes `callback` with the user's
|
|||||||
response. Displays the query to the user, and then invokes `callback` with the
|
response. Displays the query to the user, and then invokes `callback` with the
|
||||||
user's response after it has been typed.
|
user's response after it has been typed.
|
||||||
|
|
||||||
|
This will also resume the `in` stream used with `createInterface` if it has
|
||||||
|
been paused.
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
interface.question('What is your favorite food?', function(answer) {
|
interface.question('What is your favorite food?', function(answer) {
|
||||||
console.log('Oh, so your favorite food is ' + answer);
|
console.log('Oh, so your favorite food is ' + answer);
|
||||||
});
|
});
|
||||||
|
|
||||||
### rl.close()
|
|
||||||
|
|
||||||
Closes tty.
|
|
||||||
|
|
||||||
### rl.pause()
|
### rl.pause()
|
||||||
|
|
||||||
Pauses tty.
|
Pauses the readline `in` stream, allowing it to be resumed later if needed.
|
||||||
|
|
||||||
### rl.resume()
|
### rl.resume()
|
||||||
|
|
||||||
Resumes tty.
|
Resumes the readline `in` stream.
|
||||||
|
|
||||||
### rl.write()
|
### rl.write()
|
||||||
|
|
||||||
Writes to tty.
|
Writes to tty.
|
||||||
|
|
||||||
|
This will also resume the `in` stream used with `createInterface` if it has
|
||||||
|
been paused.
|
||||||
|
|
||||||
### Event: 'line'
|
### Event: 'line'
|
||||||
|
|
||||||
@ -91,27 +93,98 @@ Example of listening for `line`:
|
|||||||
console.log('You just typed: '+cmd);
|
console.log('You just typed: '+cmd);
|
||||||
});
|
});
|
||||||
|
|
||||||
### Event: 'close'
|
### Event: 'pause'
|
||||||
|
|
||||||
`function () {}`
|
`function () {}`
|
||||||
|
|
||||||
Emitted whenever the `in` stream receives a `^C` or `^D`, respectively known
|
Emitted whenever the `in` stream is paused or receives `^D`, respectively known
|
||||||
as `SIGINT` and `EOT`. This is a good way to know the user is finished using
|
as `EOT`. This event is also called if there is no `SIGINT` event listener
|
||||||
your program.
|
present when the `in` stream receives a `^C`, respectively known as `SIGINT`.
|
||||||
|
|
||||||
Example of listening for `close`, and exiting the program afterward:
|
Also emitted whenever the `in` stream is not paused and receives the `SIGCONT`
|
||||||
|
event. (See events `SIGTSTP` and `SIGCONT`)
|
||||||
|
|
||||||
rl.on('close', function() {
|
Example of listening for `pause`:
|
||||||
console.log('goodbye!');
|
|
||||||
process.exit(0);
|
rl.on('pause', function() {
|
||||||
|
console.log('Readline paused.');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
### Event: 'resume'
|
||||||
|
|
||||||
|
`function () {}`
|
||||||
|
|
||||||
|
Emitted whenever the `in` stream is resumed.
|
||||||
|
|
||||||
|
Example of listening for `resume`:
|
||||||
|
|
||||||
|
rl.on('resume', function() {
|
||||||
|
console.log('Readline resumed.');
|
||||||
|
});
|
||||||
|
|
||||||
|
### Event: 'SIGINT'
|
||||||
|
|
||||||
|
`function () {}`
|
||||||
|
|
||||||
|
Emitted whenever the `in` stream receives a `^C`, respectively known as
|
||||||
|
`SIGINT`. If there is no `SIGINT` event listener present when the `in` stream
|
||||||
|
receives a `SIGINT`, `pause` will be triggered.
|
||||||
|
|
||||||
|
Example of listening for `SIGINT`:
|
||||||
|
|
||||||
|
rl.on('SIGINT', function() {
|
||||||
|
rl.question('Are you sure you want to exit?', function(answer) {
|
||||||
|
if (answer.match(/^y(es)?$/i)) rl.pause();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
### Event: 'SIGTSTP'
|
||||||
|
|
||||||
|
`function () {}`
|
||||||
|
|
||||||
|
Emitted whenever the `in` stream receives a `^Z`, respectively known as
|
||||||
|
`SIGTSTP`. If there is no `SIGTSTP` event listener present when the `in` stream
|
||||||
|
receives a `SIGTSTP`, the program will be sent to the background.
|
||||||
|
|
||||||
|
When the program is resumed with `fg`, the `pause` and `SIGCONT` events will be
|
||||||
|
emitted. You can use either to resume the stream.
|
||||||
|
|
||||||
|
The `pause` and `SIGCONT` events will not be triggered if the stream was paused
|
||||||
|
before the program was sent to the background.
|
||||||
|
|
||||||
|
Example of listening for `SIGTSTP`:
|
||||||
|
|
||||||
|
rl.on('SIGTSTP', function() {
|
||||||
|
// This will override SIGTSTP and prevent the program from going to the
|
||||||
|
// background.
|
||||||
|
console.log('Caught SIGTSTP.');
|
||||||
|
});
|
||||||
|
|
||||||
|
### Event: 'SIGCONT'
|
||||||
|
|
||||||
|
`function () {}`
|
||||||
|
|
||||||
|
Emitted whenever the `in` stream is sent to the background with `^Z`,
|
||||||
|
respectively known as `SIGTSTP`, and then continued with `fg`. This event only
|
||||||
|
emits if the stream was not paused before sending the program to the
|
||||||
|
background.
|
||||||
|
|
||||||
|
Example of listening for `SIGCONT`:
|
||||||
|
|
||||||
|
rl.on('SIGCONT', function() {
|
||||||
|
// `prompt` will automatically resume the stream
|
||||||
|
rl.prompt();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
Here's an example of how to use all these together to craft a tiny command
|
Here's an example of how to use all these together to craft a tiny command
|
||||||
line interface:
|
line interface:
|
||||||
|
|
||||||
var readline = require('readline'),
|
var readline = require('readline'),
|
||||||
rl = readline.createInterface(process.stdin, process.stdout),
|
rl = readline.createInterface(process.stdin, process.stdout);
|
||||||
prefix = 'OHAI> ';
|
|
||||||
|
rl.setPrompt('OHAI> ');
|
||||||
|
rl.prompt();
|
||||||
|
|
||||||
rl.on('line', function(line) {
|
rl.on('line', function(line) {
|
||||||
switch(line.trim()) {
|
switch(line.trim()) {
|
||||||
@ -122,18 +195,9 @@ line interface:
|
|||||||
console.log('Say what? I might have heard `' + line.trim() + '`');
|
console.log('Say what? I might have heard `' + line.trim() + '`');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
rl.setPrompt(prefix, prefix.length);
|
|
||||||
rl.prompt();
|
rl.prompt();
|
||||||
}).on('close', function() {
|
}).on('pause', function() {
|
||||||
console.log('Have a great day!');
|
console.log('Have a great day!');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
console.log(prefix + 'Good to see you. Try typing stuff.');
|
|
||||||
rl.setPrompt(prefix, prefix.length);
|
|
||||||
rl.prompt();
|
|
||||||
|
|
||||||
|
|
||||||
Take a look at this slightly more complicated
|
|
||||||
[example](https://gist.github.com/901104), and
|
|
||||||
[http-console](https://github.com/cloudhead/http-console) for a real-life use
|
|
||||||
case.
|
|
||||||
|
@ -126,6 +126,7 @@ Interface.prototype.setPrompt = function(prompt, length) {
|
|||||||
|
|
||||||
|
|
||||||
Interface.prototype.prompt = function(preserveCursor) {
|
Interface.prototype.prompt = function(preserveCursor) {
|
||||||
|
if (this.paused) this.resume();
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
if (!preserveCursor) this.cursor = 0;
|
if (!preserveCursor) this.cursor = 0;
|
||||||
this._refreshLine();
|
this._refreshLine();
|
||||||
@ -136,16 +137,13 @@ Interface.prototype.prompt = function(preserveCursor) {
|
|||||||
|
|
||||||
|
|
||||||
Interface.prototype.question = function(query, cb) {
|
Interface.prototype.question = function(query, cb) {
|
||||||
if (cb) {
|
if (typeof cb === 'function') {
|
||||||
this.resume();
|
|
||||||
if (this._questionCallback) {
|
if (this._questionCallback) {
|
||||||
this.output.write('\n');
|
|
||||||
this.prompt();
|
this.prompt();
|
||||||
} else {
|
} else {
|
||||||
this._oldPrompt = this._prompt;
|
this._oldPrompt = this._prompt;
|
||||||
this.setPrompt(query);
|
this.setPrompt(query);
|
||||||
this._questionCallback = cb;
|
this._questionCallback = cb;
|
||||||
this.output.write('\n');
|
|
||||||
this.prompt();
|
this.prompt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,8 +179,6 @@ Interface.prototype._addHistory = function() {
|
|||||||
|
|
||||||
|
|
||||||
Interface.prototype._refreshLine = function() {
|
Interface.prototype._refreshLine = function() {
|
||||||
if (this._closed) return;
|
|
||||||
|
|
||||||
// Cursor to left edge.
|
// Cursor to left edge.
|
||||||
this.output.cursorTo(0);
|
this.output.cursorTo(0);
|
||||||
|
|
||||||
@ -198,33 +194,29 @@ Interface.prototype._refreshLine = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Interface.prototype.close = function(d) {
|
|
||||||
if (this._closing) return;
|
|
||||||
this._closing = true;
|
|
||||||
if (this.enabled) {
|
|
||||||
tty.setRawMode(false);
|
|
||||||
}
|
|
||||||
this.emit('close');
|
|
||||||
this._closed = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Interface.prototype.pause = function() {
|
Interface.prototype.pause = function() {
|
||||||
|
if (this.paused) return;
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
tty.setRawMode(false);
|
tty.setRawMode(false);
|
||||||
}
|
}
|
||||||
|
this.input.pause();
|
||||||
|
this.paused = true;
|
||||||
|
this.emit('pause');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Interface.prototype.resume = function() {
|
Interface.prototype.resume = function() {
|
||||||
|
this.input.resume();
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
tty.setRawMode(true);
|
tty.setRawMode(true);
|
||||||
}
|
}
|
||||||
|
this.paused = false;
|
||||||
|
this.emit('resume');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Interface.prototype.write = function(d, key) {
|
Interface.prototype.write = function(d, key) {
|
||||||
if (this._closed) return;
|
if (this.paused) this.resume();
|
||||||
this.enabled ? this._ttyWrite(d, key) : this._normalWrite(d, key);
|
this.enabled ? this._ttyWrite(d, key) : this._normalWrite(d, key);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -454,16 +446,6 @@ Interface.prototype._historyPrev = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Interface.prototype._attemptClose = function() {
|
|
||||||
if (this.listeners('attemptClose').length) {
|
|
||||||
// User is to call interface.close() manually.
|
|
||||||
this.emit('attemptClose');
|
|
||||||
} else {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// handle a write from the tty
|
// handle a write from the tty
|
||||||
Interface.prototype._ttyWrite = function(s, key) {
|
Interface.prototype._ttyWrite = function(s, key) {
|
||||||
var next_word, next_non_word, previous_word, previous_non_word;
|
var next_word, next_non_word, previous_word, previous_non_word;
|
||||||
@ -489,8 +471,8 @@ Interface.prototype._ttyWrite = function(s, key) {
|
|||||||
if (this.listeners('SIGINT').length) {
|
if (this.listeners('SIGINT').length) {
|
||||||
this.emit('SIGINT');
|
this.emit('SIGINT');
|
||||||
} else {
|
} else {
|
||||||
// default behavior, end the readline
|
// Pause the stream
|
||||||
this._attemptClose();
|
this.pause();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -500,7 +482,7 @@ Interface.prototype._ttyWrite = function(s, key) {
|
|||||||
|
|
||||||
case 'd': // delete right or EOF
|
case 'd': // delete right or EOF
|
||||||
if (this.cursor === 0 && this.line.length === 0) {
|
if (this.cursor === 0 && this.line.length === 0) {
|
||||||
this._attemptClose();
|
this.pause();
|
||||||
} else if (this.cursor < this.line.length) {
|
} else if (this.cursor < this.line.length) {
|
||||||
this._deleteRight();
|
this._deleteRight();
|
||||||
}
|
}
|
||||||
@ -549,7 +531,22 @@ Interface.prototype._ttyWrite = function(s, key) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'z':
|
case 'z':
|
||||||
|
if (this.listeners('SIGTSTP').length) {
|
||||||
|
this.emit('SIGTSTP');
|
||||||
|
} else {
|
||||||
|
process.once('SIGCONT', (function(self) {
|
||||||
|
return function() {
|
||||||
|
// Don't raise events if stream has already been abandoned.
|
||||||
|
if (!self.paused) {
|
||||||
|
// Stream must be paused and resumed after SIGCONT to catch
|
||||||
|
// SIGINT, SIGTSTP, and EOF.
|
||||||
|
self.pause();
|
||||||
|
self.emit('SIGCONT');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
process.kill(process.pid, 'SIGTSTP');
|
process.kill(process.pid, 'SIGTSTP');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'w': // delete backwards to a word boundary
|
case 'w': // delete backwards to a word boundary
|
||||||
|
@ -130,7 +130,7 @@ function REPLServer(prompt, stream, eval, useGlobal, ignoreUndefined) {
|
|||||||
var sawSIGINT = false;
|
var sawSIGINT = false;
|
||||||
rli.on('SIGINT', function() {
|
rli.on('SIGINT', function() {
|
||||||
if (sawSIGINT) {
|
if (sawSIGINT) {
|
||||||
rli.close();
|
rli.pause();
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,7 +733,7 @@ function defineDefaultCommands(repl) {
|
|||||||
repl.defineCommand('exit', {
|
repl.defineCommand('exit', {
|
||||||
help: 'Exit the repl',
|
help: 'Exit the repl',
|
||||||
action: function() {
|
action: function() {
|
||||||
this.rli.close();
|
this.rli.pause();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user