GeezerVison is showing github.com — readline: move utilities to internal modules · nodejs/node@10147f1
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/console/constructor.js +4-1Lines changed: 4 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -429,7 +429,10 @@ const consoleMethods = { | 429 | 429 | if (this._stdout.isTTY && process.env.TERM !== 'dumb') { | 430 | 430 | // The require is here intentionally to avoid readline being | 431 | 431 | // required too early when console is first loaded. | 432 | | - const { cursorTo, clearScreenDown } = require('readline'); | | 432 | + const { | | 433 | + cursorTo, | | 434 | + clearScreenDown, | | 435 | + } = require('internal/readline/callbacks'); | 433 | 436 | cursorTo(this._stdout, 0, 0); | 434 | 437 | clearScreenDown(this._stdout); | 435 | 438 | } | |
Collapse file Copy file name to clipboard +132Lines changed: 132 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | + | | 3 | +const { | | 4 | + NumberIsNaN, | | 5 | +} = primordials; | | 6 | + | | 7 | +const { | | 8 | + codes: { | | 9 | + ERR_INVALID_ARG_VALUE, | | 10 | + ERR_INVALID_CURSOR_POS, | | 11 | + }, | | 12 | +} = require('internal/errors'); | | 13 | + | | 14 | +const { | | 15 | + validateCallback, | | 16 | +} = require('internal/validators'); | | 17 | +const { | | 18 | + CSI, | | 19 | +} = require('internal/readline/utils'); | | 20 | + | | 21 | +const { | | 22 | + kClearLine, | | 23 | + kClearScreenDown, | | 24 | + kClearToLineBeginning, | | 25 | + kClearToLineEnd, | | 26 | +} = CSI; | | 27 | + | | 28 | + | | 29 | +/** | | 30 | + * moves the cursor to the x and y coordinate on the given stream | | 31 | + */ | | 32 | + | | 33 | +function cursorTo(stream, x, y, callback) { | | 34 | + if (callback !== undefined) { | | 35 | + validateCallback(callback); | | 36 | + } | | 37 | + | | 38 | + if (typeof y === 'function') { | | 39 | + callback = y; | | 40 | + y = undefined; | | 41 | + } | | 42 | + | | 43 | + if (NumberIsNaN(x)) throw new ERR_INVALID_ARG_VALUE('x', x); | | 44 | + if (NumberIsNaN(y)) throw new ERR_INVALID_ARG_VALUE('y', y); | | 45 | + | | 46 | + if (stream == null || (typeof x !== 'number' && typeof y !== 'number')) { | | 47 | + if (typeof callback === 'function') process.nextTick(callback, null); | | 48 | + return true; | | 49 | + } | | 50 | + | | 51 | + if (typeof x !== 'number') throw new ERR_INVALID_CURSOR_POS(); | | 52 | + | | 53 | + const data = typeof y !== 'number' ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; | | 54 | + return stream.write(data, callback); | | 55 | +} | | 56 | + | | 57 | +/** | | 58 | + * moves the cursor relative to its current location | | 59 | + */ | | 60 | + | | 61 | +function moveCursor(stream, dx, dy, callback) { | | 62 | + if (callback !== undefined) { | | 63 | + validateCallback(callback); | | 64 | + } | | 65 | + | | 66 | + if (stream == null || !(dx || dy)) { | | 67 | + if (typeof callback === 'function') process.nextTick(callback, null); | | 68 | + return true; | | 69 | + } | | 70 | + | | 71 | + let data = ''; | | 72 | + | | 73 | + if (dx < 0) { | | 74 | + data += CSI`${-dx}D`; | | 75 | + } else if (dx > 0) { | | 76 | + data += CSI`${dx}C`; | | 77 | + } | | 78 | + | | 79 | + if (dy < 0) { | | 80 | + data += CSI`${-dy}A`; | | 81 | + } else if (dy > 0) { | | 82 | + data += CSI`${dy}B`; | | 83 | + } | | 84 | + | | 85 | + return stream.write(data, callback); | | 86 | +} | | 87 | + | | 88 | +/** | | 89 | + * clears the current line the cursor is on: | | 90 | + * -1 for left of the cursor | | 91 | + * +1 for right of the cursor | | 92 | + * 0 for the entire line | | 93 | + */ | | 94 | + | | 95 | +function clearLine(stream, dir, callback) { | | 96 | + if (callback !== undefined) { | | 97 | + validateCallback(callback); | | 98 | + } | | 99 | + | | 100 | + if (stream === null || stream === undefined) { | | 101 | + if (typeof callback === 'function') process.nextTick(callback, null); | | 102 | + return true; | | 103 | + } | | 104 | + | | 105 | + const type = | | 106 | + dir < 0 ? kClearToLineBeginning : dir > 0 ? kClearToLineEnd : kClearLine; | | 107 | + return stream.write(type, callback); | | 108 | +} | | 109 | + | | 110 | +/** | | 111 | + * clears the screen from the current position of the cursor down | | 112 | + */ | | 113 | + | | 114 | +function clearScreenDown(stream, callback) { | | 115 | + if (callback !== undefined) { | | 116 | + validateCallback(callback); | | 117 | + } | | 118 | + | | 119 | + if (stream === null || stream === undefined) { | | 120 | + if (typeof callback === 'function') process.nextTick(callback, null); | | 121 | + return true; | | 122 | + } | | 123 | + | | 124 | + return stream.write(kClearScreenDown, callback); | | 125 | +} | | 126 | + | | 127 | +module.exports = { | | 128 | + clearLine, | | 129 | + clearScreenDown, | | 130 | + cursorTo, | | 131 | + moveCursor, | | 132 | +}; |
Collapse file Copy file name to clipboard +96Lines changed: 96 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | + | | 3 | +const { | | 4 | + SafeStringIterator, | | 5 | + Symbol, | | 6 | +} = primordials; | | 7 | + | | 8 | +const { | | 9 | + charLengthAt, | | 10 | + CSI, | | 11 | + emitKeys, | | 12 | +} = require('internal/readline/utils'); | | 13 | + | | 14 | +const { clearTimeout, setTimeout } = require('timers'); | | 15 | +const { | | 16 | + kEscape, | | 17 | +} = CSI; | | 18 | + | | 19 | +const { StringDecoder } = require('string_decoder'); | | 20 | + | | 21 | +const KEYPRESS_DECODER = Symbol('keypress-decoder'); | | 22 | +const ESCAPE_DECODER = Symbol('escape-decoder'); | | 23 | + | | 24 | +// GNU readline library - keyseq-timeout is 500ms (default) | | 25 | +const ESCAPE_CODE_TIMEOUT = 500; | | 26 | + | | 27 | +/** | | 28 | + * accepts a readable Stream instance and makes it emit "keypress" events | | 29 | + */ | | 30 | + | | 31 | +function emitKeypressEvents(stream, iface = {}) { | | 32 | + if (stream[KEYPRESS_DECODER]) return; | | 33 | + | | 34 | + stream[KEYPRESS_DECODER] = new StringDecoder('utf8'); | | 35 | + | | 36 | + stream[ESCAPE_DECODER] = emitKeys(stream); | | 37 | + stream[ESCAPE_DECODER].next(); | | 38 | + | | 39 | + const triggerEscape = () => stream[ESCAPE_DECODER].next(''); | | 40 | + const { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT } = iface; | | 41 | + let timeoutId; | | 42 | + | | 43 | + function onData(input) { | | 44 | + if (stream.listenerCount('keypress') > 0) { | | 45 | + const string = stream[KEYPRESS_DECODER].write(input); | | 46 | + if (string) { | | 47 | + clearTimeout(timeoutId); | | 48 | + | | 49 | + // This supports characters of length 2. | | 50 | + iface._sawKeyPress = charLengthAt(string, 0) === string.length; | | 51 | + iface.isCompletionEnabled = false; | | 52 | + | | 53 | + let length = 0; | | 54 | + for (const character of new SafeStringIterator(string)) { | | 55 | + length += character.length; | | 56 | + if (length === string.length) { | | 57 | + iface.isCompletionEnabled = true; | | 58 | + } | | 59 | + | | 60 | + try { | | 61 | + stream[ESCAPE_DECODER].next(character); | | 62 | + // Escape letter at the tail position | | 63 | + if (length === string.length && character === kEscape) { | | 64 | + timeoutId = setTimeout(triggerEscape, escapeCodeTimeout); | | 65 | + } | | 66 | + } catch (err) { | | 67 | + // If the generator throws (it could happen in the `keypress` | | 68 | + // event), we need to restart it. | | 69 | + stream[ESCAPE_DECODER] = emitKeys(stream); | | 70 | + stream[ESCAPE_DECODER].next(); | | 71 | + throw err; | | 72 | + } | | 73 | + } | | 74 | + } | | 75 | + } else { | | 76 | + // Nobody's watching anyway | | 77 | + stream.removeListener('data', onData); | | 78 | + stream.on('newListener', onNewListener); | | 79 | + } | | 80 | + } | | 81 | + | | 82 | + function onNewListener(event) { | | 83 | + if (event === 'keypress') { | | 84 | + stream.on('data', onData); | | 85 | + stream.removeListener('newListener', onNewListener); | | 86 | + } | | 87 | + } | | 88 | + | | 89 | + if (stream.listenerCount('keypress') > 0) { | | 90 | + stream.on('data', onData); | | 91 | + } else { | | 92 | + stream.on('newListener', onNewListener); | | 93 | + } | | 94 | +} | | 95 | + | | 96 | +module.exports = emitKeypressEvents; |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/repl/utils.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -41,7 +41,7 @@ const { | 41 | 41 | clearScreenDown, | 42 | 42 | cursorTo, | 43 | 43 | moveCursor, | 44 | | -} = require('readline'); | | 44 | +} = require('internal/readline/callbacks'); | 45 | 45 |
| 46 | 46 | const { | 47 | 47 | commonPrefix, | |
|