mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
repl: fix disruptive autocomplete without inspector
Fix an issue where the autocomplete wrongly autocompletes a value from a correct value to an undefined value when `node` is built without an inspector by disabling the preview view. fixes: https://github.com/nodejs/node/issues/40635 PR-URL: https://github.com/nodejs/node/pull/40661 Fixes: https://github.com/nodejs/node/issues/40635 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
aba4a00c8d
commit
12232946c0
@ -365,7 +365,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
|
||||
|
||||
const showPreview = (showCompletion = true) => {
|
||||
// Prevent duplicated previews after a refresh.
|
||||
if (inputPreview !== null || !repl.isCompletionEnabled) {
|
||||
if (inputPreview !== null || !repl.isCompletionEnabled || !process.features.inspector) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -585,7 +585,7 @@ const tests = [
|
||||
prompt, ...'const util = {}',
|
||||
'undefined\n',
|
||||
prompt, ...'ut', ...(prev ? [' // il', '\n// {}',
|
||||
'il', '\n// {}'] : [' // il', 'il']),
|
||||
'il', '\n// {}'] : ['il']),
|
||||
'{}\n',
|
||||
prompt,
|
||||
],
|
||||
@ -605,7 +605,7 @@ const tests = [
|
||||
'undefined\n',
|
||||
prompt, ...'globalThis.util = {}',
|
||||
'{}\n',
|
||||
prompt, ...'ut', ' // il', 'il',
|
||||
prompt, ...'ut', ...(prev ? [' // il', 'il' ] : ['il']),
|
||||
'{}\n',
|
||||
prompt, ...'Reflect.defineProperty(globalThis, "util", utilDesc)',
|
||||
'true\n',
|
||||
|
161
test/parallel/test-repl-preview-without-inspector.js
Normal file
161
test/parallel/test-repl-preview-without-inspector.js
Normal file
@ -0,0 +1,161 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const { REPLServer } = require('repl');
|
||||
const { Stream } = require('stream');
|
||||
|
||||
if (process.features.inspector)
|
||||
common.skip('test is for node compiled with --without-inspector only');
|
||||
|
||||
// Ignore terminal settings. This is so the test can be run intact if TERM=dumb.
|
||||
process.env.TERM = '';
|
||||
const PROMPT = 'repl > ';
|
||||
|
||||
class REPLStream extends Stream {
|
||||
readable = true;
|
||||
writable = true;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.lines = [''];
|
||||
}
|
||||
run(data) {
|
||||
for (const entry of data) {
|
||||
this.emit('data', entry);
|
||||
}
|
||||
this.emit('data', '\n');
|
||||
}
|
||||
write(chunk) {
|
||||
const chunkLines = chunk.toString('utf8').split('\n');
|
||||
this.lines[this.lines.length - 1] += chunkLines[0];
|
||||
if (chunkLines.length > 1) {
|
||||
this.lines.push(...chunkLines.slice(1));
|
||||
}
|
||||
this.emit('line');
|
||||
return true;
|
||||
}
|
||||
wait() {
|
||||
this.lines = [''];
|
||||
return new Promise((resolve, reject) => {
|
||||
const onError = (err) => {
|
||||
this.removeListener('line', onLine);
|
||||
reject(err);
|
||||
};
|
||||
const onLine = () => {
|
||||
if (this.lines[this.lines.length - 1].includes(PROMPT)) {
|
||||
this.removeListener('error', onError);
|
||||
this.removeListener('line', onLine);
|
||||
resolve(this.lines);
|
||||
}
|
||||
};
|
||||
this.once('error', onError);
|
||||
this.on('line', onLine);
|
||||
});
|
||||
}
|
||||
pause() { }
|
||||
resume() { }
|
||||
}
|
||||
|
||||
function runAndWait(cmds, repl) {
|
||||
const promise = repl.inputStream.wait();
|
||||
for (const cmd of cmds) {
|
||||
repl.inputStream.run(cmd);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
const repl = REPLServer({
|
||||
prompt: PROMPT,
|
||||
stream: new REPLStream(),
|
||||
ignoreUndefined: true,
|
||||
useColors: true,
|
||||
terminal: true,
|
||||
});
|
||||
|
||||
repl.inputStream.run([
|
||||
'function foo(x) { return x; }',
|
||||
'function koo() { console.log("abc"); }',
|
||||
'a = undefined;',
|
||||
'const r = 5;',
|
||||
]);
|
||||
|
||||
const testCases = [{
|
||||
input: 'foo',
|
||||
preview: [
|
||||
'foo\r',
|
||||
'\x1B[36m[Function: foo]\x1B[39m',
|
||||
]
|
||||
}, {
|
||||
input: 'r',
|
||||
preview: [
|
||||
'r\r',
|
||||
'\x1B[33m5\x1B[39m',
|
||||
]
|
||||
}, {
|
||||
input: 'koo',
|
||||
preview: [
|
||||
'koo\r',
|
||||
'\x1B[36m[Function: koo]\x1B[39m',
|
||||
]
|
||||
}, {
|
||||
input: 'a',
|
||||
preview: ['a\r'] // No "undefined" preview.
|
||||
}, {
|
||||
input: " { b: 1 }['b'] === 1",
|
||||
preview: [
|
||||
" { b: 1 }['b'] === 1\r",
|
||||
'\x1B[33mtrue\x1B[39m',
|
||||
]
|
||||
}, {
|
||||
input: "{ b: 1 }['b'] === 1;",
|
||||
preview: [
|
||||
"{ b: 1 }['b'] === 1;\r",
|
||||
'\x1B[33mfalse\x1B[39m',
|
||||
]
|
||||
}, {
|
||||
input: '{ a: true }',
|
||||
preview: [
|
||||
'{ a: true }\r',
|
||||
'{ a: \x1B[33mtrue\x1B[39m }',
|
||||
]
|
||||
}, {
|
||||
input: '{ a: true };',
|
||||
preview: [
|
||||
'{ a: true };\r',
|
||||
'\x1B[33mtrue\x1B[39m',
|
||||
]
|
||||
}, {
|
||||
input: ' \t { a: true};',
|
||||
preview: [
|
||||
' { a: true};\r',
|
||||
'\x1B[33mtrue\x1B[39m',
|
||||
]
|
||||
}, {
|
||||
input: '1n + 2n',
|
||||
preview: [
|
||||
'1n + 2n\r',
|
||||
'\x1B[33m3n\x1B[39m',
|
||||
]
|
||||
}, {
|
||||
input: '{};1',
|
||||
preview: [
|
||||
'{};1\r',
|
||||
'\x1B[33m1\x1B[39m',
|
||||
],
|
||||
}];
|
||||
|
||||
async function runTest() {
|
||||
for (const { input, preview } of testCases) {
|
||||
const toBeRun = input.split('\n');
|
||||
let lines = await runAndWait(toBeRun, repl);
|
||||
// Remove error messages. That allows the code to run in different
|
||||
// engines.
|
||||
// eslint-disable-next-line no-control-regex
|
||||
lines = lines.map((line) => line.replace(/Error: .+?\x1B/, ''));
|
||||
assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G');
|
||||
assert.deepStrictEqual(lines, preview);
|
||||
}
|
||||
}
|
||||
|
||||
runTest().then(common.mustCall());
|
Loading…
Reference in New Issue
Block a user