readline: key interval delay for \r & \n
Emit two line events when there is a delay between CR('\r') and LF('\n'). Introduced a new option `crlfDelay`. If the delay between \r and \n exceeds `crlfDelay` milliseconds, both \r and \n will be treated as separate end-of-line input. Default to 100 milliseconds. `crlfDelay` will be coerced to [100, 2000] range. PR-URL: https://github.com/nodejs/node/pull/8109 Reviewed-By: Yorkie Liu <yorkiefixer@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net>
This commit is contained in:
parent
da0651ac1b
commit
a634554fca
@ -361,6 +361,10 @@ added: v0.1.98
|
|||||||
only if `terminal` is set to `true` by the user or by an internal `output`
|
only if `terminal` is set to `true` by the user or by an internal `output`
|
||||||
check, otherwise the history caching mechanism is not initialized at all.
|
check, otherwise the history caching mechanism is not initialized at all.
|
||||||
* `prompt` - the prompt string to use. Default: `'> '`
|
* `prompt` - the prompt string to use. Default: `'> '`
|
||||||
|
* `crlfDelay` {number} If the delay between `\r` and `\n` exceeds
|
||||||
|
`crlfDelay` milliseconds, both `\r` and `\n` will be treated as separate
|
||||||
|
end-of-line input. Default to `100` milliseconds.
|
||||||
|
`crlfDelay` will be coerced to `[100, 2000]` range.
|
||||||
|
|
||||||
The `readline.createInterface()` method creates a new `readline.Interface`
|
The `readline.createInterface()` method creates a new `readline.Interface`
|
||||||
instance.
|
instance.
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const kHistorySize = 30;
|
const kHistorySize = 30;
|
||||||
|
const kMincrlfDelay = 100;
|
||||||
|
const kMaxcrlfDelay = 2000;
|
||||||
|
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const debug = util.debuglog('readline');
|
const debug = util.debuglog('readline');
|
||||||
@ -39,13 +41,14 @@ function Interface(input, output, completer, terminal) {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._sawReturn = false;
|
this._sawReturnAt = 0;
|
||||||
this.isCompletionEnabled = true;
|
this.isCompletionEnabled = true;
|
||||||
this._sawKeyPress = false;
|
this._sawKeyPress = false;
|
||||||
this._previousKey = null;
|
this._previousKey = null;
|
||||||
|
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
var historySize;
|
var historySize;
|
||||||
|
let crlfDelay;
|
||||||
let prompt = '> ';
|
let prompt = '> ';
|
||||||
|
|
||||||
if (arguments.length === 1) {
|
if (arguments.length === 1) {
|
||||||
@ -57,6 +60,7 @@ function Interface(input, output, completer, terminal) {
|
|||||||
if (input.prompt !== undefined) {
|
if (input.prompt !== undefined) {
|
||||||
prompt = input.prompt;
|
prompt = input.prompt;
|
||||||
}
|
}
|
||||||
|
crlfDelay = input.crlfDelay;
|
||||||
input = input.input;
|
input = input.input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +89,8 @@ function Interface(input, output, completer, terminal) {
|
|||||||
this.output = output;
|
this.output = output;
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.historySize = historySize;
|
this.historySize = historySize;
|
||||||
|
this.crlfDelay = Math.max(kMincrlfDelay,
|
||||||
|
Math.min(kMaxcrlfDelay, crlfDelay >>> 0));
|
||||||
|
|
||||||
// Check arity, 2 - for async, 1 for sync
|
// Check arity, 2 - for async, 1 for sync
|
||||||
if (typeof completer === 'function') {
|
if (typeof completer === 'function') {
|
||||||
@ -345,9 +351,10 @@ Interface.prototype._normalWrite = function(b) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var string = this._decoder.write(b);
|
var string = this._decoder.write(b);
|
||||||
if (this._sawReturn) {
|
if (this._sawReturnAt &&
|
||||||
|
Date.now() - this._sawReturnAt <= this.crlfDelay) {
|
||||||
string = string.replace(/^\n/, '');
|
string = string.replace(/^\n/, '');
|
||||||
this._sawReturn = false;
|
this._sawReturnAt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run test() on the new string chunk, not on the entire line buffer.
|
// Run test() on the new string chunk, not on the entire line buffer.
|
||||||
@ -358,7 +365,7 @@ Interface.prototype._normalWrite = function(b) {
|
|||||||
this._line_buffer = null;
|
this._line_buffer = null;
|
||||||
}
|
}
|
||||||
if (newPartContainsEnding) {
|
if (newPartContainsEnding) {
|
||||||
this._sawReturn = string.endsWith('\r');
|
this._sawReturnAt = string.endsWith('\r') ? Date.now() : 0;
|
||||||
|
|
||||||
// got one or more newlines; process into "line" events
|
// got one or more newlines; process into "line" events
|
||||||
var lines = string.split(lineEnding);
|
var lines = string.split(lineEnding);
|
||||||
@ -846,20 +853,22 @@ Interface.prototype._ttyWrite = function(s, key) {
|
|||||||
/* No modifier keys used */
|
/* No modifier keys used */
|
||||||
|
|
||||||
// \r bookkeeping is only relevant if a \n comes right after.
|
// \r bookkeeping is only relevant if a \n comes right after.
|
||||||
if (this._sawReturn && key.name !== 'enter')
|
if (this._sawReturnAt && key.name !== 'enter')
|
||||||
this._sawReturn = false;
|
this._sawReturnAt = 0;
|
||||||
|
|
||||||
switch (key.name) {
|
switch (key.name) {
|
||||||
case 'return': // carriage return, i.e. \r
|
case 'return': // carriage return, i.e. \r
|
||||||
this._sawReturn = true;
|
this._sawReturnAt = Date.now();
|
||||||
this._line();
|
this._line();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'enter':
|
case 'enter':
|
||||||
if (this._sawReturn)
|
// When key interval > crlfDelay
|
||||||
this._sawReturn = false;
|
if (this._sawReturnAt === 0 ||
|
||||||
else
|
Date.now() - this._sawReturnAt > this.crlfDelay) {
|
||||||
this._line();
|
this._line();
|
||||||
|
}
|
||||||
|
this._sawReturnAt = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'backspace':
|
case 'backspace':
|
||||||
|
@ -26,6 +26,34 @@ function isWarned(emitter) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Default crlfDelay is 100ms
|
||||||
|
const fi = new FakeInput();
|
||||||
|
const rli = new readline.Interface({ input: fi, output: fi });
|
||||||
|
assert.strictEqual(rli.crlfDelay, 100);
|
||||||
|
rli.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Minimum crlfDelay is 100ms
|
||||||
|
const fi = new FakeInput();
|
||||||
|
const rli = new readline.Interface({ input: fi, output: fi, crlfDelay: 0});
|
||||||
|
assert.strictEqual(rli.crlfDelay, 100);
|
||||||
|
rli.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Maximum crlfDelay is 2000ms
|
||||||
|
const fi = new FakeInput();
|
||||||
|
const rli = new readline.Interface({
|
||||||
|
input: fi,
|
||||||
|
output: fi,
|
||||||
|
crlfDelay: 1 << 30
|
||||||
|
});
|
||||||
|
assert.strictEqual(rli.crlfDelay, 2000);
|
||||||
|
rli.close();
|
||||||
|
}
|
||||||
|
|
||||||
[ true, false ].forEach(function(terminal) {
|
[ true, false ].forEach(function(terminal) {
|
||||||
var fi;
|
var fi;
|
||||||
var rli;
|
var rli;
|
||||||
@ -199,6 +227,29 @@ function isWarned(emitter) {
|
|||||||
assert.equal(callCount, expectedLines.length);
|
assert.equal(callCount, expectedLines.length);
|
||||||
rli.close();
|
rli.close();
|
||||||
|
|
||||||
|
// Emit two line events when the delay
|
||||||
|
// between \r and \n exceeds crlfDelay
|
||||||
|
{
|
||||||
|
const fi = new FakeInput();
|
||||||
|
const delay = 200;
|
||||||
|
const rli = new readline.Interface({
|
||||||
|
input: fi,
|
||||||
|
output: fi,
|
||||||
|
terminal: terminal,
|
||||||
|
crlfDelay: delay
|
||||||
|
});
|
||||||
|
let callCount = 0;
|
||||||
|
rli.on('line', function(line) {
|
||||||
|
callCount++;
|
||||||
|
});
|
||||||
|
fi.emit('data', '\r');
|
||||||
|
setTimeout(common.mustCall(() => {
|
||||||
|
fi.emit('data', '\n');
|
||||||
|
assert.equal(callCount, 2);
|
||||||
|
rli.close();
|
||||||
|
}), delay * 2);
|
||||||
|
}
|
||||||
|
|
||||||
// \t when there is no completer function should behave like an ordinary
|
// \t when there is no completer function should behave like an ordinary
|
||||||
// character
|
// character
|
||||||
fi = new FakeInput();
|
fi = new FakeInput();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user