diff --git a/doc/api/readline.md b/doc/api/readline.md index 86deccac73e209..cc71b20bc9b951 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -357,10 +357,12 @@ a `'resize'` event on the `output` if/when the columns ever change Move cursor to the specified position in a given TTY stream. -## readline.emitKeypressEvents(stream) +## readline.emitKeypressEvents(stream[, interface]) Causes `stream` to begin emitting `'keypress'` events corresponding to its input. +Optionally, `interface` specifies a `readline.Interface` instance for which +autocompletion is disabled when copy-pasted input is detected. Note that the stream, if it is a TTY, needs to be in raw mode: ```js diff --git a/lib/readline.js b/lib/readline.js index dce74d2bba3e6f..cd78b75ac31454 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -41,6 +41,7 @@ function Interface(input, output, completer, terminal) { } this._sawReturn = false; + this.isCompletionEnabled = true; EventEmitter.call(this); var historySize; @@ -130,7 +131,7 @@ function Interface(input, output, completer, terminal) { } else { - emitKeypressEvents(input); + emitKeypressEvents(input, this); // input usually refers to stdin input.on('keypress', onkeypress); @@ -883,7 +884,7 @@ Interface.prototype._ttyWrite = function(s, key) { case 'tab': // If tab completion enabled, do that... - if (typeof this.completer === 'function') { + if (typeof this.completer === 'function' && this.isCompletionEnabled) { this._tabComplete(); break; } @@ -917,7 +918,7 @@ exports.Interface = Interface; const KEYPRESS_DECODER = Symbol('keypress-decoder'); const ESCAPE_DECODER = Symbol('escape-decoder'); -function emitKeypressEvents(stream) { +function emitKeypressEvents(stream, iface) { if (stream[KEYPRESS_DECODER]) return; var StringDecoder = require('string_decoder').StringDecoder; // lazy load stream[KEYPRESS_DECODER] = new StringDecoder('utf8'); @@ -930,6 +931,10 @@ function emitKeypressEvents(stream) { var r = stream[KEYPRESS_DECODER].write(b); if (r) { for (var i = 0; i < r.length; i++) { + if (r[i] === '\t' && typeof r[i + 1] === 'string' && iface) { + iface.isCompletionEnabled = false; + } + try { stream[ESCAPE_DECODER].next(r[i]); } catch (err) { @@ -938,6 +943,10 @@ function emitKeypressEvents(stream) { stream[ESCAPE_DECODER] = emitKeys(stream); stream[ESCAPE_DECODER].next(); throw err; + } finally { + if (iface) { + iface.isCompletionEnabled = true; + } } } } diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index ab0c6d8824b982..b7f906d7c21686 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -227,7 +227,9 @@ function isWarned(emitter) { assert.strictEqual(called, false); called = true; }); - fi.emit('data', '\tfo\to\t'); + for (var character of '\tfo\to\t') { + fi.emit('data', character); + } fi.emit('data', '\n'); assert.ok(called); rli.close();