Skip to content

Commit

Permalink
readline: should not require an output stream.
Browse files Browse the repository at this point in the history
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: nodejs/node-v0.x-archive#4408
Reviewed-by: Trevor Norris <[email protected]>
  • Loading branch information
Julien Gilli authored and trevnorris committed Oct 1, 2014
1 parent 8dc6be1 commit 862cc28
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 17 deletions.
13 changes: 10 additions & 3 deletions doc/api/readline.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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.

Expand Down
54 changes: 40 additions & 14 deletions lib/readline.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
}
});
}

Expand All @@ -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) {
Expand All @@ -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);
}
};

Expand Down Expand Up @@ -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 '';
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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) {
Expand All @@ -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');
Expand All @@ -1230,6 +1253,9 @@ exports.clearLine = clearLine;
*/

function clearScreenDown(stream) {
if (util.isNullOrUndefined(stream))
return;

stream.write('\x1b[0J');
}
exports.clearScreenDown = clearScreenDown;
Expand Down
31 changes: 31 additions & 0 deletions test/simple/test-readline-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
})
});

});

0 comments on commit 862cc28

Please sign in to comment.