Skip to content

Commit 1a9771a

Browse files
BridgeARcodebytere
authored andcommitted
repl: improve repl preview
This aligns the REPL preview with the one used in the Chrome DevTools console. It will now preview the output for the input including the completion suffix as input. When pressing enter while previewing such data, it will automatically insert the suffix before evaluating the input. When pressing escape, that behavior is deactivated until the input is changed. Signed-off-by: Ruben Bridgewater <[email protected]> PR-URL: #33282 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michaël Zasso <[email protected]>
1 parent 0257386 commit 1a9771a

File tree

4 files changed

+77
-15
lines changed

4 files changed

+77
-15
lines changed

lib/internal/repl/utils.js

+34-10
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
138138

139139
let wrapped = false;
140140

141+
let escaped = null;
142+
141143
function getPreviewPos() {
142144
const displayPos = repl._getDisplayPos(`${repl._prompt}${repl.line}`);
143145
const cursorPos = repl.line.length !== repl.cursor ?
@@ -146,7 +148,13 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
146148
return { displayPos, cursorPos };
147149
}
148150

149-
const clearPreview = () => {
151+
function isCursorAtInputEnd() {
152+
const { cursorPos, displayPos } = getPreviewPos();
153+
return cursorPos.rows === displayPos.rows &&
154+
cursorPos.cols === displayPos.cols;
155+
}
156+
157+
const clearPreview = (key) => {
150158
if (inputPreview !== null) {
151159
const { displayPos, cursorPos } = getPreviewPos();
152160
const rows = displayPos.rows - cursorPos.rows + 1;
@@ -179,8 +187,23 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
179187
cursorTo(repl.output, pos.cursorPos.cols);
180188
moveCursor(repl.output, 0, -rows);
181189
}
190+
if (!key.ctrl && !key.shift) {
191+
if (key.name === 'escape') {
192+
if (escaped === null && key.meta) {
193+
escaped = repl.line;
194+
}
195+
} else if ((key.name === 'return' || key.name === 'enter') &&
196+
!key.meta &&
197+
escaped !== repl.line &&
198+
isCursorAtInputEnd()) {
199+
repl._insertString(completionPreview);
200+
}
201+
}
182202
completionPreview = null;
183203
}
204+
if (escaped !== repl.line) {
205+
escaped = null;
206+
}
184207
};
185208

186209
function showCompletionPreview(line, insertPreview) {
@@ -317,13 +340,6 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
317340
}
318341

319342
// Add the autocompletion preview.
320-
// TODO(BridgeAR): Trigger the input preview after the completion preview.
321-
// That way it's possible to trigger the input prefix including the
322-
// potential completion suffix. To do so, we also have to change the
323-
// behavior of `enter` and `escape`:
324-
// Enter should automatically add the suffix to the current line as long as
325-
// escape was not pressed. We might even remove the preview in case any
326-
// cursor movement is triggered.
327343
const insertPreview = false;
328344
showCompletionPreview(repl.line, insertPreview);
329345

@@ -397,9 +413,17 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
397413
moveCursor(repl.output, 0, -rows - 1);
398414
};
399415

400-
getInputPreview(line, inputPreviewCallback);
416+
let previewLine = line;
417+
418+
if (completionPreview !== null &&
419+
isCursorAtInputEnd() &&
420+
escaped !== repl.line) {
421+
previewLine += completionPreview;
422+
}
423+
424+
getInputPreview(previewLine, inputPreviewCallback);
401425
if (wrapped) {
402-
getInputPreview(line, inputPreviewCallback);
426+
getInputPreview(previewLine, inputPreviewCallback);
403427
}
404428
wrapped = false;
405429
};

lib/repl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ function REPLServer(prompt,
849849
self.cursor === 0 && self.line.length === 0) {
850850
self.clearLine();
851851
}
852-
clearPreview();
852+
clearPreview(key);
853853
if (!reverseSearch(d, key)) {
854854
ttyWrite(d, key);
855855
showPreview();

test/parallel/test-repl-history-navigation.js

+37-3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const WORD_RIGHT = { name: 'right', ctrl: true };
5757
const GO_TO_END = { name: 'end' };
5858
const DELETE_WORD_LEFT = { name: 'backspace', ctrl: true };
5959
const SIGINT = { name: 'c', ctrl: true };
60+
const ESCAPE = { name: 'escape', meta: true };
6061

6162
const prompt = '> ';
6263
const WAIT = '€';
@@ -182,8 +183,10 @@ const tests = [
182183
'veryLongName'.repeat(30),
183184
ENTER,
184185
`${'\x1B[90m \x1B[39m'.repeat(235)} fun`,
186+
ESCAPE,
185187
ENTER,
186188
`${' '.repeat(236)} fun`,
189+
ESCAPE,
187190
ENTER
188191
],
189192
expected: [],
@@ -318,6 +321,7 @@ const tests = [
318321
env: { NODE_REPL_HISTORY: defaultHistoryPath },
319322
showEscapeCodes: true,
320323
skip: !process.features.inspector,
324+
checkTotal: true,
321325
test: [
322326
'fu',
323327
'n',
@@ -331,6 +335,12 @@ const tests = [
331335
BACKSPACE,
332336
WORD_LEFT,
333337
WORD_RIGHT,
338+
ESCAPE,
339+
ENTER,
340+
UP,
341+
LEFT,
342+
ENTER,
343+
UP,
334344
ENTER
335345
],
336346
// C = Cursor n forward
@@ -379,12 +389,36 @@ const tests = [
379389
'\x1B[0K', '\x1B[7D', '\x1B[10G', ' // n', '\x1B[3G', '\x1B[10G',
380390
// 10. Word right. Cleanup
381391
'\x1B[0K', '\x1B[3G', '\x1B[7C', ' // n', '\x1B[10G',
382-
'\x1B[0K',
383-
// 11. ENTER
392+
// 11. ESCAPE
393+
'\x1B[0K', ' // n', '\x1B[10G', '\x1B[0K',
394+
// 12. ENTER
384395
'\r\n',
385396
'Uncaught ReferenceError: functio is not defined\n',
386397
'\x1B[1G', '\x1B[0J',
387-
prompt, '\x1B[3G', '\r\n'
398+
// 13. UP
399+
prompt, '\x1B[3G', '\x1B[1G', '\x1B[0J',
400+
`${prompt}functio`, '\x1B[10G',
401+
' // n', '\x1B[10G',
402+
' // n', '\x1B[10G',
403+
// 14. LEFT
404+
'\x1B[0K', '\x1B[1D',
405+
'\x1B[10G', ' // n', '\x1B[9G', '\x1B[10G',
406+
// 15. ENTER
407+
'\x1B[0K', '\x1B[9G', '\x1B[1C',
408+
'\r\n',
409+
'Uncaught ReferenceError: functio is not defined\n',
410+
'\x1B[1G', '\x1B[0J',
411+
'> ', '\x1B[3G',
412+
// 16. UP
413+
'\x1B[1G', '\x1B[0J',
414+
'> functio', '\x1B[10G',
415+
' // n', '\x1B[10G',
416+
' // n', '\x1B[10G', '\x1B[0K',
417+
// 17. ENTER
418+
'n', '\r\n',
419+
'\x1B[1G', '\x1B[0J',
420+
'... ', '\x1B[5G',
421+
'\r\n'
388422
],
389423
clean: true
390424
},

test/parallel/test-repl-preview.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,11 @@ async function tests(options) {
9191
input: 'koo',
9292
noPreview: '[Function: koo]',
9393
preview: [
94-
'k\x1B[90moo\x1B[39m\x1B[9G\x1B[0Ko\x1B[90mo\x1B[39m\x1B[10G\x1B[0Ko',
94+
'k\x1B[90moo\x1B[39m\x1B[9G',
95+
'\x1B[90m[Function: koo]\x1B[39m\x1B[9G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' +
96+
'\x1B[0Ko\x1B[90mo\x1B[39m\x1B[10G',
97+
'\x1B[90m[Function: koo]\x1B[39m\x1B[10G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' +
98+
'\x1B[0Ko',
9599
'\x1B[90m[Function: koo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
96100
'\x1B[36m[Function: koo]\x1B[39m'
97101
]

0 commit comments

Comments
 (0)