summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Gilli <julien.gilli@joyent.com>2014-09-22 13:21:11 -0700
committerTrevor Norris <trev.norris@gmail.com>2014-10-01 15:00:02 -0700
commit862cc28183b03f0e1a67b052b5c8250a3e2e8995 (patch)
tree5514582ca85ba2729988daddb9e5ea840f8e0d45
parent8dc6be1747d3a48af56f89e973ecb5665f89a2e1 (diff)
downloadnode-862cc28183b03f0e1a67b052b5c8250a3e2e8995.tar.gz
readline: should not require an output stream.
Passing null as the output stream to readline.Interface()'s constructor is now supported. Any output written by readline is just discarded. It makes it easier to use readline just as a line parser. Fixes: https://github.com/joyent/node/issues/4408 Reviewed-by: Trevor Norris <trev.norris@gmail.com>
-rw-r--r--doc/api/readline.markdown13
-rw-r--r--lib/readline.js54
-rw-r--r--test/simple/test-readline-interface.js31
3 files changed, 81 insertions, 17 deletions
diff --git a/doc/api/readline.markdown b/doc/api/readline.markdown
index 16bbd3c0c..6aab28679 100644
--- a/doc/api/readline.markdown
+++ b/doc/api/readline.markdown
@@ -30,7 +30,7 @@ the following values:
- `input` - the readable stream to listen to (Required).
- - `output` - the writable stream to write readline data to (Required).
+ - `output` - the writable stream to write readline data to (Optional).
- `completer` - an optional function that is used for Tab autocompletion. See
below for an example of using this.
@@ -100,6 +100,9 @@ to `true` to prevent the cursor placement being reset to `0`.
This will also resume the `input` stream used with `createInterface` if it has
been paused.
+If `output` is set to `null` or `undefined` when calling `createInterface`, the
+prompt is not written.
+
### rl.question(query, callback)
Prepends the prompt with `query` and invokes `callback` with the user's
@@ -109,6 +112,9 @@ with the user's response after it has been typed.
This will also resume the `input` stream used with `createInterface` if
it has been paused.
+If `output` is set to `null` or `undefined` when calling `createInterface`,
+nothing is displayed.
+
Example usage:
interface.question('What is your favorite food?', function(answer) {
@@ -130,8 +136,9 @@ Closes the `Interface` instance, relinquishing control on the `input` and
### rl.write(data[, key])
-Writes `data` to `output` stream. `key` is an object literal to represent a key
-sequence; available if the terminal is a TTY.
+Writes `data` to `output` stream, unless `output` is set to `null` or
+`undefined` when calling `createInterface`. `key` is an object literal to
+represent a key sequence; available if the terminal is a TTY.
This will also resume the `input` stream if it has been paused.
diff --git a/lib/readline.js b/lib/readline.js
index a3f1b9d58..bafef00e0 100644
--- a/lib/readline.js
+++ b/lib/readline.js
@@ -68,7 +68,7 @@ function Interface(input, output, completer, terminal) {
// backwards compat; check the isTTY prop of the output stream
// when `terminal` was not specified
- if (util.isUndefined(terminal)) {
+ if (util.isUndefined(terminal) && !util.isNullOrUndefined(output)) {
terminal = !!output.isTTY;
}
@@ -142,11 +142,15 @@ function Interface(input, output, completer, terminal) {
this.history = [];
this.historyIndex = -1;
- output.on('resize', onresize);
+ if (!util.isNullOrUndefined(output))
+ output.on('resize', onresize);
+
self.once('close', function() {
input.removeListener('keypress', onkeypress);
input.removeListener('end', ontermend);
- output.removeListener('resize', onresize);
+ if (!util.isNullOrUndefined(output)) {
+ output.removeListener('resize', onresize);
+ }
});
}
@@ -156,7 +160,10 @@ function Interface(input, output, completer, terminal) {
inherits(Interface, EventEmitter);
Interface.prototype.__defineGetter__('columns', function() {
- return this.output.columns || Infinity;
+ var columns = Infinity;
+ if (this.output && this.output.columns)
+ columns = this.output.columns;
+ return columns;
});
Interface.prototype.setPrompt = function(prompt) {
@@ -177,7 +184,7 @@ Interface.prototype.prompt = function(preserveCursor) {
if (!preserveCursor) this.cursor = 0;
this._refreshLine();
} else {
- this.output.write(this._prompt);
+ this._writeToOutput(this._prompt);
}
};
@@ -207,6 +214,13 @@ Interface.prototype._onLine = function(line) {
}
};
+Interface.prototype._writeToOutput = function _writeToOutput(stringToWrite) {
+ if (!util.isString(stringToWrite))
+ throw new TypeError('stringToWrite must be a string');
+
+ if (!util.isNullOrUndefined(this.output))
+ this.output.write(stringToWrite);
+};
Interface.prototype._addHistory = function() {
if (this.line.length === 0) return '';
@@ -245,11 +259,11 @@ Interface.prototype._refreshLine = function() {
exports.clearScreenDown(this.output);
// Write the prompt and the current buffer content.
- this.output.write(line);
+ this._writeToOutput(line);
// Force terminal to allocate a new line
if (lineCols === 0) {
- this.output.write(' ');
+ this._writeToOutput(' ');
}
// Move cursor to original position.
@@ -351,7 +365,7 @@ Interface.prototype._insertString = function(c) {
if (this._getCursorPos().cols === 0) {
this._refreshLine();
} else {
- this.output.write(c);
+ this._writeToOutput(c);
}
// a hack to get the line refreshed if it's needed
@@ -378,7 +392,7 @@ Interface.prototype._tabComplete = function() {
if (completions.length === 1) {
self._insertString(completions[0].slice(completeOn.length));
} else {
- self.output.write('\r\n');
+ self._writeToOutput('\r\n');
var width = completions.reduce(function(a, b) {
return a.length > b.length ? a : b;
}).length + 2; // 2 space padding
@@ -422,17 +436,17 @@ function handleGroup(self, group, width, maxColumns) {
break;
}
var item = group[idx];
- self.output.write(item);
+ self._writeToOutput(item);
if (col < maxColumns - 1) {
for (var s = 0, itemLen = item.length; s < width - itemLen;
s++) {
- self.output.write(' ');
+ self._writeToOutput(' ');
}
}
}
- self.output.write('\r\n');
+ self._writeToOutput('\r\n');
}
- self.output.write('\r\n');
+ self._writeToOutput('\r\n');
}
function commonPrefix(strings) {
@@ -525,7 +539,7 @@ Interface.prototype._deleteLineRight = function() {
Interface.prototype.clearLine = function() {
this._moveCursor(+Infinity);
- this.output.write('\r\n');
+ this._writeToOutput('\r\n');
this.line = '';
this.cursor = 0;
this.prevRows = 0;
@@ -1168,6 +1182,9 @@ function emitKeys(stream, s) {
*/
function cursorTo(stream, x, y) {
+ if (util.isNullOrUndefined(stream))
+ return;
+
if (!util.isNumber(x) && !util.isNumber(y))
return;
@@ -1188,6 +1205,9 @@ exports.cursorTo = cursorTo;
*/
function moveCursor(stream, dx, dy) {
+ if (util.isNullOrUndefined(stream))
+ return;
+
if (dx < 0) {
stream.write('\x1b[' + (-dx) + 'D');
} else if (dx > 0) {
@@ -1211,6 +1231,9 @@ exports.moveCursor = moveCursor;
*/
function clearLine(stream, dir) {
+ if (util.isNullOrUndefined(stream))
+ return;
+
if (dir < 0) {
// to the beginning
stream.write('\x1b[1K');
@@ -1230,6 +1253,9 @@ exports.clearLine = clearLine;
*/
function clearScreenDown(stream) {
+ if (util.isNullOrUndefined(stream))
+ return;
+
stream.write('\x1b[0J');
}
exports.clearScreenDown = clearScreenDown;
diff --git a/test/simple/test-readline-interface.js b/test/simple/test-readline-interface.js
index f91c10821..b86dd5a8a 100644
--- a/test/simple/test-readline-interface.js
+++ b/test/simple/test-readline-interface.js
@@ -307,5 +307,36 @@ function isWarned(emitter) {
assert.equal(isWarned(process.stdout._events), false);
}
+ //can create a new readline Interface with a null output arugument
+ fi = new FakeInput();
+ rli = new readline.Interface({input: fi, output: null, terminal: terminal });
+
+ called = false;
+ rli.on('line', function(line) {
+ called = true;
+ assert.equal(line, 'asdf');
+ });
+ fi.emit('data', 'asdf\n');
+ assert.ok(called);
+
+ assert.doesNotThrow(function() {
+ rli.setPrompt("ddd> ");
+ });
+
+ assert.doesNotThrow(function() {
+ rli.prompt();
+ });
+
+ assert.doesNotThrow(function() {
+ rli.write('really shouldnt be seeing this');
+ });
+
+ assert.doesNotThrow(function() {
+ rli.question("What do you think of node.js? ", function(answer) {
+ console.log("Thank you for your valuable feedback:", answer);
+ rli.close();
+ })
+ });
+
});