Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 10147f1

Browse filesBrowse files
aduh95targos
authored andcommitted
readline: move utilities to internal modules
PR-URL: #38466 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
1 parent e3b75cb commit 10147f1
Copy full SHA for 10147f1

File tree

Expand file treeCollapse file tree

6 files changed

+243
-196
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

6 files changed

+243
-196
lines changed
Open diff view settings
Collapse file

‎lib/internal/console/constructor.js‎

Copy file name to clipboardExpand all lines: lib/internal/console/constructor.js
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,10 @@ const consoleMethods = {
429429
if (this._stdout.isTTY && process.env.TERM !== 'dumb') {
430430
// The require is here intentionally to avoid readline being
431431
// 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');
433436
cursorTo(this._stdout, 0, 0);
434437
clearScreenDown(this._stdout);
435438
}
Collapse file

‎lib/internal/readline/callbacks.js‎

Copy file name to clipboard
+132Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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
+96Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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

‎lib/internal/repl/utils.js‎

Copy file name to clipboardExpand all lines: lib/internal/repl/utils.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const {
4141
clearScreenDown,
4242
cursorTo,
4343
moveCursor,
44-
} = require('readline');
44+
} = require('internal/readline/callbacks');
4545

4646
const {
4747
commonPrefix,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.