From 991174e3eedddf6c6381cd14776e86077d3d2d7f Mon Sep 17 00:00:00 2001 From: Raoof Date: Wed, 4 Apr 2018 00:00:35 +0430 Subject: [PATCH 1/6] export ESCAPE_CODE_TIMEOUT --- lib/readline.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index cd8240bc26c0b8..ca960ae70a01c9 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -62,9 +62,6 @@ const lineEnding = /\r?\n|\r(?!\n)/; const KEYPRESS_DECODER = Symbol('keypress-decoder'); const ESCAPE_DECODER = Symbol('escape-decoder'); -// GNU readline library - keyseq-timeout is 500ms (default) -const ESCAPE_CODE_TIMEOUT = 500; - function createInterface(input, output, completer, terminal) { return new Interface(input, output, completer, terminal); } @@ -1020,7 +1017,7 @@ function emitKeypressEvents(stream, iface) { stream[ESCAPE_DECODER].next(r[i]); // Escape letter at the tail position if (r[i] === kEscape && i + 1 === r.length) { - timeoutId = setTimeout(escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); + timeoutId = setTimeout(escapeCodeTimeout, module.exports.ESCAPE_CODE_TIMEOUT); } } catch (err) { // if the generator throws (it could happen in the `keypress` @@ -1139,5 +1136,6 @@ module.exports = { createInterface, cursorTo, emitKeypressEvents, - moveCursor + moveCursor, + ESCAPE_CODE_TIMEOUT : 500 // GNU readline library - keyseq-timeout is 500ms (default) }; From a58d9dbad153fe6ef80386bb7bd5e79158f2af60 Mon Sep 17 00:00:00 2001 From: Raoof Date: Wed, 4 Apr 2018 10:59:16 +0430 Subject: [PATCH 2/6] lib: add escapeCodeTimeout as an option to createInterface escapeCodeTimeout option in createInterface default to ESCAPE_CODE_TIMEOUT = 500 when set to a NaN or negative number or a value other than a number set back to default value --- doc/api/readline.md | 5 +++++ lib/readline.js | 24 +++++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/doc/api/readline.md b/doc/api/readline.md index ccc9ee92fe4f63..d0c77a3a4e734b 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -375,6 +375,11 @@ changes: * `removeHistoryDuplicates` {boolean} If `true`, when a new input line added to the history list duplicates an older one, this removes the older line from the list. **Default:** `false`. + * `escapeCodeTimeout` {number} The duration `readline` will wait for a + character when reading an ambiguous key sequence (one that can both form a + complete key sequence using the input read so far and can take additional + input to complete a longer key sequence). The default value is used if + `NaN`, a negative number, or a non-number is provided. **Default:** `500`. The `readline.createInterface()` method creates a new `readline.Interface` instance. diff --git a/lib/readline.js b/lib/readline.js index ca960ae70a01c9..251cff932edfd9 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -62,6 +62,9 @@ const lineEnding = /\r?\n|\r(?!\n)/; const KEYPRESS_DECODER = Symbol('keypress-decoder'); const ESCAPE_DECODER = Symbol('escape-decoder'); +// GNU readline library - keyseq-timeout is 500ms (default) +const ESCAPE_CODE_TIMEOUT = 500; + function createInterface(input, output, completer, terminal) { return new Interface(input, output, completer, terminal); } @@ -85,6 +88,7 @@ function Interface(input, output, completer, terminal) { var removeHistoryDuplicates = false; let crlfDelay; let prompt = '> '; + let escapeCodeTimeout = ESCAPE_CODE_TIMEOUT; if (input && input.input) { // an options object was given @@ -97,9 +101,16 @@ function Interface(input, output, completer, terminal) { prompt = input.prompt; } crlfDelay = input.crlfDelay; + escapeCodeTimeout = input.escapeCodeTimeout; input = input.input; } + if (typeof escapeCodeTimeout !== "number" || + Number.isNaN(escapeCodeTimeout) || + escapeCodeTimeout < 0) { + escapeCodeTimeout = ESCAPE_CODE_TIMEOUT; + } + if (completer && typeof completer !== 'function') { throw new ERR_INVALID_OPT_VALUE('completer', completer); } @@ -128,7 +139,7 @@ function Interface(input, output, completer, terminal) { this.removeHistoryDuplicates = !!removeHistoryDuplicates; this.crlfDelay = crlfDelay ? Math.max(kMincrlfDelay, crlfDelay) : kMincrlfDelay; - + this.escapeCodeTimeout = escapeCodeTimeout; // Check arity, 2 - for async, 1 for sync if (typeof completer === 'function') { this.completer = completer.length === 2 ? @@ -995,7 +1006,7 @@ function emitKeypressEvents(stream, iface) { stream[ESCAPE_DECODER] = emitKeys(stream); stream[ESCAPE_DECODER].next(); - const escapeCodeTimeout = () => stream[ESCAPE_DECODER].next(''); + const escapeCodeTimeoutFn = () => stream[ESCAPE_DECODER].next(''); let timeoutId; function onData(b) { @@ -1017,7 +1028,11 @@ function emitKeypressEvents(stream, iface) { stream[ESCAPE_DECODER].next(r[i]); // Escape letter at the tail position if (r[i] === kEscape && i + 1 === r.length) { - timeoutId = setTimeout(escapeCodeTimeout, module.exports.ESCAPE_CODE_TIMEOUT); + if(iface){ + timeoutId = setTimeout(escapeCodeTimeoutFn, iface.escapeCodeTimeout); + }else{ + timeoutId = setTimeout(escapeCodeTimeoutFn, ESCAPE_CODE_TIMEOUT); + } } } catch (err) { // if the generator throws (it could happen in the `keypress` @@ -1136,6 +1151,5 @@ module.exports = { createInterface, cursorTo, emitKeypressEvents, - moveCursor, - ESCAPE_CODE_TIMEOUT : 500 // GNU readline library - keyseq-timeout is 500ms (default) + moveCursor }; From 0586244bb257e70dbbd391e6d9b3578a16c8ecdb Mon Sep 17 00:00:00 2001 From: Raoof Date: Wed, 4 Apr 2018 20:45:28 +0430 Subject: [PATCH 3/6] test: add simple test to ensures escapeCodeTimeout option set correctly --- lib/readline.js | 18 +++-- ...st-readline-interface-escapecodetimeout.js | 71 +++++++++++++++++++ 2 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 test/parallel/test-readline-interface-escapecodetimeout.js diff --git a/lib/readline.js b/lib/readline.js index 251cff932edfd9..765e50944a7212 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -105,8 +105,8 @@ function Interface(input, output, completer, terminal) { input = input.input; } - if (typeof escapeCodeTimeout !== "number" || - Number.isNaN(escapeCodeTimeout) || + if (typeof escapeCodeTimeout !== 'number' || + Number.isNaN(escapeCodeTimeout) || escapeCodeTimeout < 0) { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT; } @@ -1028,10 +1028,16 @@ function emitKeypressEvents(stream, iface) { stream[ESCAPE_DECODER].next(r[i]); // Escape letter at the tail position if (r[i] === kEscape && i + 1 === r.length) { - if(iface){ - timeoutId = setTimeout(escapeCodeTimeoutFn, iface.escapeCodeTimeout); - }else{ - timeoutId = setTimeout(escapeCodeTimeoutFn, ESCAPE_CODE_TIMEOUT); + if (iface) { + timeoutId = setTimeout( + escapeCodeTimeoutFn, + iface.escapeCodeTimeout + ); + } else { + timeoutId = setTimeout( + escapeCodeTimeoutFn, + ESCAPE_CODE_TIMEOUT + ); } } } catch (err) { diff --git a/test/parallel/test-readline-interface-escapecodetimeout.js b/test/parallel/test-readline-interface-escapecodetimeout.js new file mode 100644 index 00000000000000..fbb82d35b05996 --- /dev/null +++ b/test/parallel/test-readline-interface-escapecodetimeout.js @@ -0,0 +1,71 @@ +'use strict'; +require('../common'); + +// This test ensures that the escapeCodeTimeout option set correctly + +const assert = require('assert'); +const readline = require('readline'); +const EventEmitter = require('events').EventEmitter; +const ESCAPE_CODE_TIMEOUT = 500; + +class FakeInput extends EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +{ + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: 50 + }); + assert.strictEqual(rli.escapeCodeTimeout, 50); + rli.close(); +} + +{ + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: null + }); + assert.strictEqual(rli.escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); + rli.close(); +} + +{ + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: {} + }); + assert.strictEqual(rli.escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); + rli.close(); +} + +{ + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: NaN + }); + assert.strictEqual(rli.escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); + rli.close(); +} + +{ + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: '50' + }); + assert.strictEqual(rli.escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); + rli.close(); +} From b0b8ba5dfebfbb3185e78bc113e50109979e2da5 Mon Sep 17 00:00:00 2001 From: Raoof Date: Wed, 11 Apr 2018 19:24:13 +0430 Subject: [PATCH 4/6] lib: rewrite the same code for escapeCodeTimeout option as suggested by @BridgeAR --- doc/api/readline.md | 9 +++++---- lib/readline.js | 26 +++++++++----------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/doc/api/readline.md b/doc/api/readline.md index d0c77a3a4e734b..ecb93b492f910e 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -376,10 +376,11 @@ changes: to the history list duplicates an older one, this removes the older line from the list. **Default:** `false`. * `escapeCodeTimeout` {number} The duration `readline` will wait for a - character when reading an ambiguous key sequence (one that can both form a - complete key sequence using the input read so far and can take additional - input to complete a longer key sequence). The default value is used if - `NaN`, a negative number, or a non-number is provided. **Default:** `500`. + character when reading an ambiguous key sequence in milliseconds (one that + can both form a complete key sequence using the input read so far and can + take additional input to complete a longer key sequence). The default + value is used if `NaN`, a negative number, or a non-number is provided. + **Default:** `500`. The `readline.createInterface()` method creates a new `readline.Interface` instance. diff --git a/lib/readline.js b/lib/readline.js index 765e50944a7212..5c0ac9c1ecfa3b 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -100,16 +100,15 @@ function Interface(input, output, completer, terminal) { if (input.prompt !== undefined) { prompt = input.prompt; } + if (typeof input.escapeCodeTimeout === 'number' && + !Number.isNaN(input.escapeCodeTimeout) && + input.escapeCodeTimeout > 0) { + escapeCodeTimeout = input.escapeCodeTimeout; + } crlfDelay = input.crlfDelay; - escapeCodeTimeout = input.escapeCodeTimeout; input = input.input; } - if (typeof escapeCodeTimeout !== 'number' || - Number.isNaN(escapeCodeTimeout) || - escapeCodeTimeout < 0) { - escapeCodeTimeout = ESCAPE_CODE_TIMEOUT; - } if (completer && typeof completer !== 'function') { throw new ERR_INVALID_OPT_VALUE('completer', completer); @@ -1028,17 +1027,10 @@ function emitKeypressEvents(stream, iface) { stream[ESCAPE_DECODER].next(r[i]); // Escape letter at the tail position if (r[i] === kEscape && i + 1 === r.length) { - if (iface) { - timeoutId = setTimeout( - escapeCodeTimeoutFn, - iface.escapeCodeTimeout - ); - } else { - timeoutId = setTimeout( - escapeCodeTimeoutFn, - ESCAPE_CODE_TIMEOUT - ); - } + timeoutId = setTimeout( + escapeCodeTimeoutFn, + iface ? iface.escapeCodeTimeout : ESCAPE_CODE_TIMEOUT + ); } } catch (err) { // if the generator throws (it could happen in the `keypress` From fb1ce389e1945b864e11c3baead54274f35c67e9 Mon Sep 17 00:00:00 2001 From: Raoof Date: Tue, 15 May 2018 23:26:30 +0430 Subject: [PATCH 5/6] lib: rewrite the same code again :) --- doc/api/readline.md | 6 +- lib/readline.js | 19 +++--- ...st-readline-interface-escapecodetimeout.js | 63 ++++++------------- 3 files changed, 32 insertions(+), 56 deletions(-) diff --git a/doc/api/readline.md b/doc/api/readline.md index ecb93b492f910e..a765c9b8d0523e 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -376,11 +376,9 @@ changes: to the history list duplicates an older one, this removes the older line from the list. **Default:** `false`. * `escapeCodeTimeout` {number} The duration `readline` will wait for a - character when reading an ambiguous key sequence in milliseconds (one that + character (when reading an ambiguous key sequence in milliseconds one that can both form a complete key sequence using the input read so far and can - take additional input to complete a longer key sequence). The default - value is used if `NaN`, a negative number, or a non-number is provided. - **Default:** `500`. + take additional input to complete a longer key sequence). The `readline.createInterface()` method creates a new `readline.Interface` instance. diff --git a/lib/readline.js b/lib/readline.js index 5c0ac9c1ecfa3b..e36ecac4220dd1 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -82,13 +82,13 @@ function Interface(input, output, completer, terminal) { this.isCompletionEnabled = true; this._sawKeyPress = false; this._previousKey = null; + this.escapeCodeTimeout = ESCAPE_CODE_TIMEOUT; EventEmitter.call(this); var historySize; var removeHistoryDuplicates = false; let crlfDelay; let prompt = '> '; - let escapeCodeTimeout = ESCAPE_CODE_TIMEOUT; if (input && input.input) { // an options object was given @@ -100,15 +100,19 @@ function Interface(input, output, completer, terminal) { if (input.prompt !== undefined) { prompt = input.prompt; } - if (typeof input.escapeCodeTimeout === 'number' && - !Number.isNaN(input.escapeCodeTimeout) && - input.escapeCodeTimeout > 0) { - escapeCodeTimeout = input.escapeCodeTimeout; + if (input.escapeCodeTimeout !== undefined) { + this.escapeCodeTimeout = input.escapeCodeTimeout; } crlfDelay = input.crlfDelay; input = input.input; } + if (!Number.isFinite(this.escapeCodeTimeout)) { + throw new ERR_INVALID_OPT_VALUE( + 'escapeCodeTimeout', + this.escapeCodeTimeout + ); + } if (completer && typeof completer !== 'function') { throw new ERR_INVALID_OPT_VALUE('completer', completer); @@ -138,7 +142,6 @@ function Interface(input, output, completer, terminal) { this.removeHistoryDuplicates = !!removeHistoryDuplicates; this.crlfDelay = crlfDelay ? Math.max(kMincrlfDelay, crlfDelay) : kMincrlfDelay; - this.escapeCodeTimeout = escapeCodeTimeout; // Check arity, 2 - for async, 1 for sync if (typeof completer === 'function') { this.completer = completer.length === 2 ? @@ -1005,7 +1008,7 @@ function emitKeypressEvents(stream, iface) { stream[ESCAPE_DECODER] = emitKeys(stream); stream[ESCAPE_DECODER].next(); - const escapeCodeTimeoutFn = () => stream[ESCAPE_DECODER].next(''); + const escapeCodeTimeout = () => stream[ESCAPE_DECODER].next(''); let timeoutId; function onData(b) { @@ -1028,7 +1031,7 @@ function emitKeypressEvents(stream, iface) { // Escape letter at the tail position if (r[i] === kEscape && i + 1 === r.length) { timeoutId = setTimeout( - escapeCodeTimeoutFn, + escapeCodeTimeout, iface ? iface.escapeCodeTimeout : ESCAPE_CODE_TIMEOUT ); } diff --git a/test/parallel/test-readline-interface-escapecodetimeout.js b/test/parallel/test-readline-interface-escapecodetimeout.js index fbb82d35b05996..0e25423e8550c0 100644 --- a/test/parallel/test-readline-interface-escapecodetimeout.js +++ b/test/parallel/test-readline-interface-escapecodetimeout.js @@ -1,12 +1,11 @@ 'use strict'; -require('../common'); +const common = require('../common'); // This test ensures that the escapeCodeTimeout option set correctly const assert = require('assert'); const readline = require('readline'); const EventEmitter = require('events').EventEmitter; -const ESCAPE_CODE_TIMEOUT = 500; class FakeInput extends EventEmitter { resume() {} @@ -26,46 +25,22 @@ class FakeInput extends EventEmitter { rli.close(); } -{ - const fi = new FakeInput(); - const rli = new readline.Interface({ - input: fi, - output: fi, - escapeCodeTimeout: null - }); - assert.strictEqual(rli.escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); - rli.close(); -} - -{ - const fi = new FakeInput(); - const rli = new readline.Interface({ - input: fi, - output: fi, - escapeCodeTimeout: {} - }); - assert.strictEqual(rli.escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); - rli.close(); -} - -{ - const fi = new FakeInput(); - const rli = new readline.Interface({ - input: fi, - output: fi, - escapeCodeTimeout: NaN +[ + null, + {}, + NaN, + '50' +].forEach((invalidInput) => { + common.expectsError(() => { + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: invalidInput + }); + rli.close(); + }, { + type: TypeError, + code: 'ERR_INVALID_OPT_VALUE' }); - assert.strictEqual(rli.escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); - rli.close(); -} - -{ - const fi = new FakeInput(); - const rli = new readline.Interface({ - input: fi, - output: fi, - escapeCodeTimeout: '50' - }); - assert.strictEqual(rli.escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); - rli.close(); -} +}); From 5a25fd5c3fb230baeebb318d07d430671fdb9701 Mon Sep 17 00:00:00 2001 From: Raoof Date: Wed, 16 May 2018 09:35:17 +0430 Subject: [PATCH 6/6] lib: rewrite the same code for the third time --- doc/api/readline.md | 1 + lib/readline.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/api/readline.md b/doc/api/readline.md index a765c9b8d0523e..e737ced5b90fa8 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -379,6 +379,7 @@ changes: character (when reading an ambiguous key sequence in milliseconds one that can both form a complete key sequence using the input read so far and can take additional input to complete a longer key sequence). + **Default:** `500`. The `readline.createInterface()` method creates a new `readline.Interface` instance. diff --git a/lib/readline.js b/lib/readline.js index e36ecac4220dd1..4eeefb227ca746 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -101,19 +101,19 @@ function Interface(input, output, completer, terminal) { prompt = input.prompt; } if (input.escapeCodeTimeout !== undefined) { - this.escapeCodeTimeout = input.escapeCodeTimeout; + if (Number.isFinite(input.escapeCodeTimeout)) { + this.escapeCodeTimeout = input.escapeCodeTimeout; + } else { + throw new ERR_INVALID_OPT_VALUE( + 'escapeCodeTimeout', + this.escapeCodeTimeout + ); + } } crlfDelay = input.crlfDelay; input = input.input; } - if (!Number.isFinite(this.escapeCodeTimeout)) { - throw new ERR_INVALID_OPT_VALUE( - 'escapeCodeTimeout', - this.escapeCodeTimeout - ); - } - if (completer && typeof completer !== 'function') { throw new ERR_INVALID_OPT_VALUE('completer', completer); }