readline: use a "string_decoder" to parse "keypress" events
While updating the readline test cases to test both "terimal: false" and "terminal: true" mode, it turned out that the test case testing utf8 chars being sent over multiple write() calls was failing. The solution is to use a string_decoder instance when parsing the "keypress" events.
This commit is contained in:
parent
e95e095289
commit
3c91a7ae10
@ -801,12 +801,14 @@ exports.Interface = Interface;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function emitKeypressEvents(stream) {
|
function emitKeypressEvents(stream) {
|
||||||
if (stream._emitKeypress) return;
|
if (stream._keypressDecoder) return;
|
||||||
stream._emitKeypress = true;
|
var StringDecoder = require('string_decoder').StringDecoder; // lazy load
|
||||||
|
stream._keypressDecoder = new StringDecoder('utf8');
|
||||||
|
|
||||||
function onData(b) {
|
function onData(b) {
|
||||||
if (stream.listeners('keypress').length > 0) {
|
if (stream.listeners('keypress').length > 0) {
|
||||||
emitKey(stream, b);
|
var r = stream._keypressDecoder.write(b);
|
||||||
|
if (r) emitKey(stream, r);
|
||||||
} else {
|
} else {
|
||||||
// Nobody's watching anyway
|
// Nobody's watching anyway
|
||||||
stream.removeListener('data', onData);
|
stream.removeListener('data', onData);
|
||||||
|
@ -32,100 +32,104 @@ function FakeInput() {
|
|||||||
inherits(FakeInput, EventEmitter);
|
inherits(FakeInput, EventEmitter);
|
||||||
FakeInput.prototype.resume = function() {};
|
FakeInput.prototype.resume = function() {};
|
||||||
FakeInput.prototype.pause = function() {};
|
FakeInput.prototype.pause = function() {};
|
||||||
|
FakeInput.prototype.write = function() {};
|
||||||
|
FakeInput.prototype.end = function() {};
|
||||||
|
|
||||||
var fi;
|
[ true, false ].forEach(function(terminal) {
|
||||||
var rli;
|
var fi;
|
||||||
var called;
|
var rli;
|
||||||
|
var called;
|
||||||
|
|
||||||
// sending a full line
|
// sending a full line
|
||||||
fi = new FakeInput();
|
fi = new FakeInput();
|
||||||
rli = new readline.Interface(fi, {});
|
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
|
||||||
called = false;
|
called = false;
|
||||||
rli.on('line', function(line) {
|
rli.on('line', function(line) {
|
||||||
called = true;
|
called = true;
|
||||||
assert.equal(line, 'asdf');
|
assert.equal(line, 'asdf');
|
||||||
|
});
|
||||||
|
fi.emit('data', 'asdf\n');
|
||||||
|
assert.ok(called);
|
||||||
|
|
||||||
|
// sending a blank line
|
||||||
|
fi = new FakeInput();
|
||||||
|
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
|
||||||
|
called = false;
|
||||||
|
rli.on('line', function(line) {
|
||||||
|
called = true;
|
||||||
|
assert.equal(line, '');
|
||||||
|
});
|
||||||
|
fi.emit('data', '\n');
|
||||||
|
assert.ok(called);
|
||||||
|
|
||||||
|
// sending a single character with no newline
|
||||||
|
fi = new FakeInput();
|
||||||
|
rli = new readline.Interface(fi, {});
|
||||||
|
called = false;
|
||||||
|
rli.on('line', function(line) {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
fi.emit('data', 'a');
|
||||||
|
assert.ok(!called);
|
||||||
|
rli.close();
|
||||||
|
|
||||||
|
// sending a single character with no newline and then a newline
|
||||||
|
fi = new FakeInput();
|
||||||
|
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
|
||||||
|
called = false;
|
||||||
|
rli.on('line', function(line) {
|
||||||
|
called = true;
|
||||||
|
assert.equal(line, 'a');
|
||||||
|
});
|
||||||
|
fi.emit('data', 'a');
|
||||||
|
assert.ok(!called);
|
||||||
|
fi.emit('data', '\n');
|
||||||
|
assert.ok(called);
|
||||||
|
rli.close();
|
||||||
|
|
||||||
|
// sending multiple newlines at once
|
||||||
|
fi = new FakeInput();
|
||||||
|
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
|
||||||
|
var expectedLines = ['foo', 'bar', 'baz'];
|
||||||
|
var callCount = 0;
|
||||||
|
rli.on('line', function(line) {
|
||||||
|
assert.equal(line, expectedLines[callCount]);
|
||||||
|
callCount++;
|
||||||
|
});
|
||||||
|
fi.emit('data', expectedLines.join('\n') + '\n');
|
||||||
|
assert.equal(callCount, expectedLines.length);
|
||||||
|
rli.close();
|
||||||
|
|
||||||
|
// sending multiple newlines at once that does not end with a new line
|
||||||
|
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++;
|
||||||
|
});
|
||||||
|
fi.emit('data', expectedLines.join('\n'));
|
||||||
|
assert.equal(callCount, expectedLines.length - 1);
|
||||||
|
rli.close();
|
||||||
|
|
||||||
|
// sending a multi-byte utf8 char over multiple writes
|
||||||
|
var buf = Buffer('☮', 'utf8');
|
||||||
|
fi = new FakeInput();
|
||||||
|
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
|
||||||
|
callCount = 0;
|
||||||
|
rli.on('line', function(line) {
|
||||||
|
callCount++;
|
||||||
|
assert.equal(line, buf.toString('utf8'));
|
||||||
|
});
|
||||||
|
[].forEach.call(buf, function(i) {
|
||||||
|
fi.emit('data', Buffer([i]));
|
||||||
|
});
|
||||||
|
assert.equal(callCount, 0);
|
||||||
|
fi.emit('data', '\n');
|
||||||
|
assert.equal(callCount, 1);
|
||||||
|
rli.close();
|
||||||
|
|
||||||
|
assert.deepEqual(fi.listeners('end'), []);
|
||||||
|
assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
|
||||||
});
|
});
|
||||||
fi.emit('data', 'asdf\n');
|
|
||||||
assert.ok(called);
|
|
||||||
|
|
||||||
// sending a blank line
|
|
||||||
fi = new FakeInput();
|
|
||||||
rli = new readline.Interface(fi, {});
|
|
||||||
called = false;
|
|
||||||
rli.on('line', function(line) {
|
|
||||||
called = true;
|
|
||||||
assert.equal(line, '');
|
|
||||||
});
|
|
||||||
fi.emit('data', '\n');
|
|
||||||
assert.ok(called);
|
|
||||||
|
|
||||||
// sending a single character with no newline
|
|
||||||
fi = new FakeInput();
|
|
||||||
rli = new readline.Interface(fi, {});
|
|
||||||
called = false;
|
|
||||||
rli.on('line', function(line) {
|
|
||||||
called = true;
|
|
||||||
});
|
|
||||||
fi.emit('data', 'a');
|
|
||||||
assert.ok(!called);
|
|
||||||
rli.close();
|
|
||||||
|
|
||||||
// sending a single character with no newline and then a newline
|
|
||||||
fi = new FakeInput();
|
|
||||||
rli = new readline.Interface(fi, {});
|
|
||||||
called = false;
|
|
||||||
rli.on('line', function(line) {
|
|
||||||
called = true;
|
|
||||||
assert.equal(line, 'a');
|
|
||||||
});
|
|
||||||
fi.emit('data', 'a');
|
|
||||||
assert.ok(!called);
|
|
||||||
fi.emit('data', '\n');
|
|
||||||
assert.ok(called);
|
|
||||||
rli.close();
|
|
||||||
|
|
||||||
// sending multiple newlines at once
|
|
||||||
fi = new FakeInput();
|
|
||||||
rli = new readline.Interface(fi, {});
|
|
||||||
var expectedLines = ['foo\n', 'bar\n', 'baz\n'];
|
|
||||||
var callCount = 0;
|
|
||||||
rli.on('line', function(line) {
|
|
||||||
assert.equal(line, expectedLines[callCount]);
|
|
||||||
callCount++;
|
|
||||||
});
|
|
||||||
fi.emit('data', expectedLines.join('\n') + '\n');
|
|
||||||
assert.equal(callCount, expectedLines.length);
|
|
||||||
rli.close();
|
|
||||||
|
|
||||||
// sending multiple newlines at once that does not end with a new line
|
|
||||||
fi = new FakeInput();
|
|
||||||
rli = new readline.Interface(fi, {});
|
|
||||||
var expectedLines = ['foo\n', 'bar\n', 'baz\n', 'bat'];
|
|
||||||
var callCount = 0;
|
|
||||||
rli.on('line', function(line) {
|
|
||||||
assert.equal(line, expectedLines[callCount]);
|
|
||||||
callCount++;
|
|
||||||
});
|
|
||||||
fi.emit('data', expectedLines.join('\n'));
|
|
||||||
assert.equal(callCount, expectedLines.length - 1);
|
|
||||||
rli.close();
|
|
||||||
|
|
||||||
// sending a multi-byte utf8 char over multiple writes
|
|
||||||
var buf = Buffer('☮', 'utf8');
|
|
||||||
fi = new FakeInput();
|
|
||||||
rli = new readline.Interface(fi, {});
|
|
||||||
callCount = 0;
|
|
||||||
rli.on('line', function(line) {
|
|
||||||
callCount++;
|
|
||||||
assert.equal(line, buf.toString('utf8'));
|
|
||||||
});
|
|
||||||
[].forEach.call(buf, function(i) {
|
|
||||||
fi.emit('data', Buffer([i]));
|
|
||||||
});
|
|
||||||
assert.equal(callCount, 0);
|
|
||||||
fi.emit('data', '\n');
|
|
||||||
assert.equal(callCount, 1);
|
|
||||||
rli.close();
|
|
||||||
|
|
||||||
assert.deepEqual(fi.listeners('end'), []);
|
|
||||||
assert.deepEqual(fi.listeners('data'), []);
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user