diff --git a/lib/internal/readline.js b/lib/internal/readline.js index 4474b0234575d5..96da5cc82f2af5 100644 --- a/lib/internal/readline.js +++ b/lib/internal/readline.js @@ -7,9 +7,27 @@ const ansi = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; +const kEscape = '\x1b'; + var getStringWidth; var isFullWidthCodePoint; +function CSI(strings, ...args) { + let ret = `${kEscape}[`; + for (var n = 0; n < strings.length; n++) { + ret += strings[n]; + if (n < args.length) + ret += args[n]; + } + return ret; +} + +CSI.kEscape = kEscape; +CSI.kClearToBeginning = CSI`1K`; +CSI.kClearToEnd = CSI`0K`; +CSI.kClearLine = CSI`2K`; +CSI.kClearScreenDown = CSI`0J`; + if (process.binding('config').hasIntl) { const icu = process.binding('icu'); getStringWidth = function getStringWidth(str, options) { @@ -151,11 +169,11 @@ function* emitKeys(stream) { shift: false }; - if (ch === '\x1b') { + if (ch === kEscape) { escaped = true; s += (ch = yield); - if (ch === '\x1b') { + if (ch === kEscape) { s += (ch = yield); } } @@ -370,7 +388,7 @@ function* emitKeys(stream) { // backspace or ctrl+h key.name = 'backspace'; key.meta = escaped; - } else if (ch === '\x1b') { + } else if (ch === kEscape) { // escape key key.name = 'escape'; key.meta = escaped; @@ -409,5 +427,6 @@ module.exports = { emitKeys, getStringWidth, isFullWidthCodePoint, - stripVTControlCharacters + stripVTControlCharacters, + CSI }; diff --git a/lib/readline.js b/lib/readline.js index f46fc0d59cd0ed..60864f40afdbc1 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -31,12 +31,21 @@ const { debug, inherits } = require('util'); const Buffer = require('buffer').Buffer; const EventEmitter = require('events'); const { + CSI, emitKeys, getStringWidth, isFullWidthCodePoint, stripVTControlCharacters } = require('internal/readline'); +const { + kEscape, + kClearToBeginning, + kClearToEnd, + kClearLine, + kClearScreenDown +} = CSI; + const kHistorySize = 30; const kMincrlfDelay = 100; const kMaxcrlfDelay = 2000; @@ -995,7 +1004,7 @@ function emitKeypressEvents(stream, iface) { try { stream[ESCAPE_DECODER].next(r[i]); // Escape letter at the tail position - if (r[i] === '\x1b' && i + 1 === r.length) { + if (r[i] === kEscape && i + 1 === r.length) { timeoutId = setTimeout(escapeCodeTimeout, ESCAPE_CODE_TIMEOUT); } } catch (err) { @@ -1047,9 +1056,9 @@ function cursorTo(stream, x, y) { throw new Error('Can\'t set cursor row without also setting it\'s column'); if (typeof y !== 'number') { - stream.write('\x1b[' + (x + 1) + 'G'); + stream.write(CSI`${x + 1}G`); } else { - stream.write('\x1b[' + (y + 1) + ';' + (x + 1) + 'H'); + stream.write(CSI`${y + 1};${x + 1}H`); } } @@ -1062,15 +1071,15 @@ function moveCursor(stream, dx, dy) { return; if (dx < 0) { - stream.write('\x1b[' + (-dx) + 'D'); + stream.write(CSI`${-dx}D`); } else if (dx > 0) { - stream.write('\x1b[' + dx + 'C'); + stream.write(CSI`${dx}C`); } if (dy < 0) { - stream.write('\x1b[' + (-dy) + 'A'); + stream.write(CSI`${-dy}A`); } else if (dy > 0) { - stream.write('\x1b[' + dy + 'B'); + stream.write(CSI`${dy}B`); } } @@ -1087,13 +1096,13 @@ function clearLine(stream, dir) { if (dir < 0) { // to the beginning - stream.write('\x1b[1K'); + stream.write(kClearToBeginning); } else if (dir > 0) { // to the end - stream.write('\x1b[0K'); + stream.write(kClearToEnd); } else { // entire line - stream.write('\x1b[2K'); + stream.write(kClearLine); } } @@ -1105,7 +1114,7 @@ function clearScreenDown(stream) { if (stream === null || stream === undefined) return; - stream.write('\x1b[0J'); + stream.write(kClearScreenDown); } module.exports = {