Skip to content

Commit

Permalink
readline: treat bare \r as a line ending
Browse files Browse the repository at this point in the history
Fixes #3305
  • Loading branch information
isaacs authored and TooTallNate committed Jan 30, 2013
1 parent 9bd9c54 commit 60f18ed
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 4 deletions.
30 changes: 26 additions & 4 deletions lib/readline.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ function Interface(input, output, completer, terminal) {
return new Interface(input, output, completer, terminal);
}

this._sawReturn = false;

EventEmitter.call(this);

if (arguments.length === 1) {
Expand Down Expand Up @@ -292,18 +294,27 @@ Interface.prototype.write = function(d, key) {
this.terminal ? this._ttyWrite(d, key) : this._normalWrite(d);
};

// \r\n, \n, or \r followed by something other than \n
var lineEnding = /\r?\n|\r(?!\n)/;
Interface.prototype._normalWrite = function(b) {
if (b === undefined) {
return;
}
var string = this._decoder.write(b);
if (this._sawReturn) {
string = string.replace(/^\n/, '');
this._sawReturn = false;
}

if (this._line_buffer) {
string = this._line_buffer + string;
this._line_buffer = null;
}
if (string.indexOf('\n') !== -1) {
if (lineEnding.test(string)) {
this._sawReturn = /\r$/.test(string);

// got one or more newlines; process into "line" events
var lines = string.split(/\r?\n/);
var lines = string.split(lineEnding);
// either '' or (concievably) the unfinished portion of the next line
string = lines.pop();
this._line_buffer = string;
Expand Down Expand Up @@ -733,11 +744,23 @@ Interface.prototype._ttyWrite = function(s, key) {
} else {
/* No modifier keys used */

// \r bookkeeping is only relevant if a \n comes right after.
if (this._sawReturn && key.name !== 'enter')
this._sawReturn = false;

switch (key.name) {
case 'enter':
case 'return': // carriage return, i.e. \r
this._sawReturn = true;
this._line();
break;

case 'enter':
if (this._sawReturn)
this._sawReturn = false
else
this._line();
break;

case 'backspace':
this._deleteLeft();
break;
Expand All @@ -758,7 +781,6 @@ Interface.prototype._ttyWrite = function(s, key) {
this._moveCursor(+1);
break;

case 'return': // carriage return, i.e. \r
case 'home':
this._moveCursor(-Infinity);
break;
Expand Down
30 changes: 30 additions & 0 deletions test/simple/test-readline-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,36 @@ FakeInput.prototype.end = function() {};
assert.equal(callCount, expectedLines.length - 1);
rli.close();

// \r\n should emit one line event when split across multiple writes.
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
expectedLines = ['foo', 'bar', 'baz', 'bat'];
callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
expectedLines.forEach(function(line) {
fi.emit('data', line + '\r');
fi.emit('data', '\n');
});
assert.equal(callCount, expectedLines.length);
rli.close();

// \r should behave like \n when alone
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: true });
expectedLines = ['foo', 'bar', 'baz', 'bat'];
callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join('\r'));
assert.equal(callCount, expectedLines.length - 1);
rli.close();


// sending a multi-byte utf8 char over multiple writes
var buf = Buffer('☮', 'utf8');
fi = new FakeInput();
Expand Down

0 comments on commit 60f18ed

Please sign in to comment.