From 1c0ba1061e5a6eb9abf817a64550fe0a501c04d7 Mon Sep 17 00:00:00 2001 From: Vladislav Kaminsky Date: Fri, 22 Feb 2019 16:00:36 +0400 Subject: [PATCH 1/8] console: don't use ANSI escape codes when TERM=dumb Fixes: https://github.com/nodejs/node/issues/26187 --- lib/internal/console/constructor.js | 2 +- test/pseudo-tty/console-dumb-tty.js | 9 +++++++++ test/pseudo-tty/console-dumb-tty.out | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/pseudo-tty/console-dumb-tty.js create mode 100644 test/pseudo-tty/console-dumb-tty.out diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 2fa812d9fe1bcc..86ba935651edf2 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -346,7 +346,7 @@ const consoleMethods = { clear() { // It only makes sense to clear if _stdout is a TTY. // Otherwise, do nothing. - if (this._stdout.isTTY) { + if (this._stdout.isTTY && process.env.TERM !== 'dumb') { // The require is here intentionally to avoid readline being // required too early when console is first loaded. const { cursorTo, clearScreenDown } = require('readline'); diff --git a/test/pseudo-tty/console-dumb-tty.js b/test/pseudo-tty/console-dumb-tty.js new file mode 100644 index 00000000000000..93f9b99b0ff0fb --- /dev/null +++ b/test/pseudo-tty/console-dumb-tty.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); + +process.env.TERM = 'dumb'; + +console.log({ foo: 'bar' }); +console.dir({ foo: 'bar' }); +console.log('%s q', 'string'); +console.log('%o with object format param', { foo: 'bar' }); diff --git a/test/pseudo-tty/console-dumb-tty.out b/test/pseudo-tty/console-dumb-tty.out new file mode 100644 index 00000000000000..c776b496474307 --- /dev/null +++ b/test/pseudo-tty/console-dumb-tty.out @@ -0,0 +1,4 @@ +{ foo: 'bar' } +{ foo: 'bar' } +string q +{ foo: 'bar' } with object format param From 1b7434df9054dfeee3aa049f1a9284cc0a50777e Mon Sep 17 00:00:00 2001 From: Vladislav Kaminsky Date: Fri, 22 Feb 2019 16:07:43 +0400 Subject: [PATCH 2/8] readline: support TERM=dumb When TERM=dumb and .isTTY=true don't use ANSI escape codes and ignore all keys, except 'escape', 'return' and 'ctrl-c'. Fixes: https://github.com/nodejs/node/issues/26187 --- lib/readline.js | 61 +++++++++++++++++++++++++-- test/pseudo-tty/readline-dumb-tty.js | 21 +++++++++ test/pseudo-tty/readline-dumb-tty.out | 3 ++ 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 test/pseudo-tty/readline-dumb-tty.js create mode 100644 test/pseudo-tty/readline-dumb-tty.out diff --git a/lib/readline.js b/lib/readline.js index 0983f3ab615916..aa394022176127 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -181,7 +181,11 @@ function Interface(input, output, completer, terminal) { } function onkeypress(s, key) { - self._ttyWrite(s, key); + if (process.env.TERM === 'dumb') { + self._ttyWriteDumb(s, key); + } else { + self._ttyWrite(s, key); + } if (key && key.sequence) { // If the key.sequence is half of a surrogate pair // (>= 0xd800 and <= 0xdfff), refresh the line so @@ -276,7 +280,7 @@ Interface.prototype._setRawMode = function(mode) { Interface.prototype.prompt = function(preserveCursor) { if (this.paused) this.resume(); - if (this.terminal) { + if (this.terminal && process.env.TERM !== 'dumb') { if (!preserveCursor) this.cursor = 0; this._refreshLine(); } else { @@ -417,7 +421,15 @@ Interface.prototype.resume = function() { Interface.prototype.write = function(d, key) { if (this.paused) this.resume(); - this.terminal ? this._ttyWrite(d, key) : this._normalWrite(d); + if (this.terminal) { + if (process.env.TERM === 'dumb') { + this._ttyWriteDumb(d, key); + } else { + this._ttyWrite(d, key); + } + } else { + this._normalWrite(d); + } }; Interface.prototype._normalWrite = function(b) { @@ -789,6 +801,49 @@ Interface.prototype._moveCursor = function(dx) { } }; +Interface.prototype._ttyWriteDumb = function(s, key) { + key = key || {}; + + if (key.name === 'escape') return; + + if (this._sawReturnAt && key.name !== 'enter') + this._sawReturnAt = 0; + + if (key.ctrl && key.name === 'c') { + if (this.listenerCount('SIGINT') > 0) { + this.emit('SIGINT'); + } else { + // This readline instance is finished + this.close(); + } + } + + switch (key.name) { + case 'return': // carriage return, i.e. \r + this._sawReturnAt = Date.now(); + this._line(); + break; + + case 'enter': + // When key interval > crlfDelay + if (this._sawReturnAt === 0 || + Date.now() - this._sawReturnAt > this.crlfDelay) { + this._line(); + } + this._sawReturnAt = 0; + break; + + default: + if (s instanceof Buffer) + s = s.toString('utf-8'); + + if (s) { + this.line += s; + this.cursor += s.length; + this._writeToOutput(s); + } + } +}; // handle a write from the tty Interface.prototype._ttyWrite = function(s, key) { diff --git a/test/pseudo-tty/readline-dumb-tty.js b/test/pseudo-tty/readline-dumb-tty.js new file mode 100644 index 00000000000000..6b9a3b59ad96e8 --- /dev/null +++ b/test/pseudo-tty/readline-dumb-tty.js @@ -0,0 +1,21 @@ +'use strict'; +require('../common'); + +process.env.TERM = 'dumb'; + +const readline = require('readline'); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +rl.write('text'); +rl.write(null, { ctrl: true, name: 'u' }); +rl.write(null, { name: 'return' }); +rl.write('text'); +rl.write(null, { name: 'backspace' }); +rl.write(null, { name: 'escape' }); +rl.write(null, { name: 'enter' }); +rl.write('text'); +rl.write(null, { ctrl: true, name: 'c' }); diff --git a/test/pseudo-tty/readline-dumb-tty.out b/test/pseudo-tty/readline-dumb-tty.out new file mode 100644 index 00000000000000..6841116ad12836 --- /dev/null +++ b/test/pseudo-tty/readline-dumb-tty.out @@ -0,0 +1,3 @@ +text +text +text From 6aa691d17375cedc3713f5299268187209b6c610 Mon Sep 17 00:00:00 2001 From: Vladislav Kaminsky Date: Fri, 22 Feb 2019 16:13:52 +0400 Subject: [PATCH 3/8] repl: don't color output when TERM=dumb Fixes: https://github.com/nodejs/node/issues/26187 --- lib/repl.js | 7 +++++-- test/pseudo-tty/repl-dumb-tty.js | 15 +++++++++++++++ test/pseudo-tty/repl-dumb-tty.out | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 test/pseudo-tty/repl-dumb-tty.js create mode 100644 test/pseudo-tty/repl-dumb-tty.out diff --git a/lib/repl.js b/lib/repl.js index d3363d4bbc6fd3..15605042975c11 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -501,14 +501,17 @@ function REPLServer(prompt, self.writer = options.writer || exports.writer; if (options.useColors === undefined) { - options.useColors = self.terminal; + options.useColors = self.terminal && process.env.TERM !== 'dumb'; } self.useColors = !!options.useColors; if (self.useColors && self.writer === writer) { // Turn on ANSI coloring. self.writer = (obj) => util.inspect(obj, self.writer.options); - self.writer.options = { ...writer.options, colors: true }; + self.writer.options = { + ...writer.options, + colors: self.useColors + }; } function filterInternalStackFrames(structuredStack) { diff --git a/test/pseudo-tty/repl-dumb-tty.js b/test/pseudo-tty/repl-dumb-tty.js new file mode 100644 index 00000000000000..08c63881d38b97 --- /dev/null +++ b/test/pseudo-tty/repl-dumb-tty.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); + +process.env.TERM = 'dumb'; + +const repl = require('repl'); + +repl.start('> '); +process.stdin.push('console.log("foo")\n'); +process.stdin.push('1 + 2\n'); +process.stdin.push('"str"\n'); +process.stdin.push('console.dir({ a: 1 })\n'); +process.stdin.push('{ a: 1 }\n'); +process.stdin.push('\n'); +process.stdin.push('.exit\n'); diff --git a/test/pseudo-tty/repl-dumb-tty.out b/test/pseudo-tty/repl-dumb-tty.out new file mode 100644 index 00000000000000..69eb4e5da6313e --- /dev/null +++ b/test/pseudo-tty/repl-dumb-tty.out @@ -0,0 +1,14 @@ +> console.log("foo") +foo +undefined +> 1 + 2 +3 +> "str" +'str' +> console.dir({ a: 1 }) +{ a: 1 } +undefined +> { a: 1 } +{ a: 1 } +> +> .exit From 826bab427c92096d4b55c214fd358f10b32a9561 Mon Sep 17 00:00:00 2001 From: Vladislav Kaminsky Date: Fri, 22 Feb 2019 20:45:09 +0400 Subject: [PATCH 4/8] repl: fix unnecessary check --- lib/repl.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/repl.js b/lib/repl.js index 15605042975c11..2931c5514749c0 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -508,10 +508,7 @@ function REPLServer(prompt, if (self.useColors && self.writer === writer) { // Turn on ANSI coloring. self.writer = (obj) => util.inspect(obj, self.writer.options); - self.writer.options = { - ...writer.options, - colors: self.useColors - }; + self.writer.options = { ...writer.options, colors: true }; } function filterInternalStackFrames(structuredStack) { From 4113cfef235d93a40d13defc4beff012d4629c67 Mon Sep 17 00:00:00 2001 From: Vladislav Kaminsky Date: Fri, 22 Feb 2019 20:47:27 +0400 Subject: [PATCH 5/8] readline: remove _ttyWriteDumb from prototype --- lib/readline.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index aa394022176127..a160399c930e35 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -161,6 +161,10 @@ function Interface(input, output, completer, terminal) { this.terminal = !!terminal; + if (process.env.TERM === 'dumb') { + this._ttyWrite = _ttyWriteDumb.bind(this); + } + function ondata(data) { self._normalWrite(data); } @@ -181,11 +185,7 @@ function Interface(input, output, completer, terminal) { } function onkeypress(s, key) { - if (process.env.TERM === 'dumb') { - self._ttyWriteDumb(s, key); - } else { - self._ttyWrite(s, key); - } + self._ttyWrite(s, key); if (key && key.sequence) { // If the key.sequence is half of a surrogate pair // (>= 0xd800 and <= 0xdfff), refresh the line so @@ -422,11 +422,7 @@ Interface.prototype.resume = function() { Interface.prototype.write = function(d, key) { if (this.paused) this.resume(); if (this.terminal) { - if (process.env.TERM === 'dumb') { - this._ttyWriteDumb(d, key); - } else { - this._ttyWrite(d, key); - } + this._ttyWrite(d, key); } else { this._normalWrite(d); } @@ -801,7 +797,7 @@ Interface.prototype._moveCursor = function(dx) { } }; -Interface.prototype._ttyWriteDumb = function(s, key) { +function _ttyWriteDumb(s, key) { key = key || {}; if (key.name === 'escape') return; @@ -843,7 +839,7 @@ Interface.prototype._ttyWriteDumb = function(s, key) { this._writeToOutput(s); } } -}; +} // handle a write from the tty Interface.prototype._ttyWrite = function(s, key) { From 1468212357958c232e0a5f3bfdc307cb26a65969 Mon Sep 17 00:00:00 2001 From: Vladislav Kaminsky Date: Fri, 22 Feb 2019 20:49:24 +0400 Subject: [PATCH 6/8] readline: remove unnecessary Buffer check --- lib/readline.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index a160399c930e35..5da446f3c459a2 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -34,7 +34,6 @@ const { const { validateString } = require('internal/validators'); const { debug } = require('util'); const { emitExperimentalWarning } = require('internal/util'); -const { Buffer } = require('buffer'); const EventEmitter = require('events'); const { CSI, @@ -830,9 +829,6 @@ function _ttyWriteDumb(s, key) { break; default: - if (s instanceof Buffer) - s = s.toString('utf-8'); - if (s) { this.line += s; this.cursor += s.length; @@ -1058,9 +1054,6 @@ Interface.prototype._ttyWrite = function(s, key) { // falls through default: - if (s instanceof Buffer) - s = s.toString('utf-8'); - if (s) { var lines = s.split(/\r\n|\n|\r/); for (var i = 0, len = lines.length; i < len; i++) { From 26fcf315888143ef94fae3b9ebee86c49ef2b383 Mon Sep 17 00:00:00 2001 From: Vladislav Kaminsky Date: Fri, 22 Feb 2019 20:56:02 +0400 Subject: [PATCH 7/8] readline: not empty string check in _ttyWrite* --- lib/readline.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index 5da446f3c459a2..4d9519f26a5aeb 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -829,7 +829,7 @@ function _ttyWriteDumb(s, key) { break; default: - if (s) { + if (typeof s === 'string' && s) { this.line += s; this.cursor += s.length; this._writeToOutput(s); @@ -1054,7 +1054,7 @@ Interface.prototype._ttyWrite = function(s, key) { // falls through default: - if (s) { + if (typeof s === 'string' && s) { var lines = s.split(/\r\n|\n|\r/); for (var i = 0, len = lines.length; i < len; i++) { if (i > 0) { From 03994a0ce2e3c3575ab24c1aea03d8f250fd2be5 Mon Sep 17 00:00:00 2001 From: Vladislav Kaminsky Date: Tue, 5 Mar 2019 22:03:58 +0400 Subject: [PATCH 8/8] repl: check colors with .getColorDepth() --- lib/repl.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/repl.js b/lib/repl.js index 2931c5514749c0..d2312f17335dcf 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -501,7 +501,9 @@ function REPLServer(prompt, self.writer = options.writer || exports.writer; if (options.useColors === undefined) { - options.useColors = self.terminal && process.env.TERM !== 'dumb'; + options.useColors = self.terminal && ( + typeof self.outputStream.getColorDepth === 'function' ? + self.outputStream.getColorDepth() > 2 : true); } self.useColors = !!options.useColors;