mirror of
https://github.com/nodejs/node.git
synced 2025-12-28 07:50:41 +00:00
repl: do not preview while pasting code
This makes sure no previews are triggered while pasting code. The very last character is allowed to trigger the preview. The output should be completely identical to the user. PR-URL: https://github.com/nodejs/node/pull/31315 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
This commit is contained in:
parent
a67c5dc064
commit
c3b702f9b4
@ -298,10 +298,9 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
|
||||
}, () => callback(new ERR_INSPECTOR_NOT_AVAILABLE()));
|
||||
}
|
||||
|
||||
// TODO(BridgeAR): Prevent previews while pasting code.
|
||||
const showPreview = () => {
|
||||
// Prevent duplicated previews after a refresh.
|
||||
if (inputPreview !== null) {
|
||||
if (inputPreview !== null || !repl.isCompletionEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -97,6 +97,8 @@ function Interface(input, output, completer, terminal) {
|
||||
}
|
||||
|
||||
this._sawReturnAt = 0;
|
||||
// TODO(BridgeAR): Document this property. The name is not ideal, so we might
|
||||
// want to expose an alias and document that instead.
|
||||
this.isCompletionEnabled = true;
|
||||
this._sawKeyPress = false;
|
||||
this._previousKey = null;
|
||||
@ -1044,8 +1046,7 @@ Interface.prototype._ttyWrite = function(s, key) {
|
||||
this._tabComplete(lastKeypressWasTab);
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
|
||||
// falls through
|
||||
default:
|
||||
if (typeof s === 'string' && s) {
|
||||
const lines = s.split(/\r\n|\n|\r/);
|
||||
|
||||
@ -296,11 +296,13 @@ function REPLServer(prompt,
|
||||
if (!paused) return;
|
||||
paused = false;
|
||||
let entry;
|
||||
const tmpCompletionEnabled = self.isCompletionEnabled;
|
||||
while (entry = pausedBuffer.shift()) {
|
||||
const [type, payload] = entry;
|
||||
const [type, payload, isCompletionEnabled] = entry;
|
||||
switch (type) {
|
||||
case 'key': {
|
||||
const [d, key] = payload;
|
||||
self.isCompletionEnabled = isCompletionEnabled;
|
||||
self._ttyWrite(d, key);
|
||||
break;
|
||||
}
|
||||
@ -312,6 +314,7 @@ function REPLServer(prompt,
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.isCompletionEnabled = tmpCompletionEnabled;
|
||||
}
|
||||
|
||||
function defaultEval(code, context, file, cb) {
|
||||
@ -837,7 +840,7 @@ function REPLServer(prompt,
|
||||
self._ttyWrite = (d, key) => {
|
||||
key = key || {};
|
||||
if (paused && !(self.breakEvalOnSigint && key.ctrl && key.name === 'c')) {
|
||||
pausedBuffer.push(['key', [d, key]]);
|
||||
pausedBuffer.push(['key', [d, key], self.isCompletionEnabled]);
|
||||
return;
|
||||
}
|
||||
if (!self.editorMode || !self.terminal) {
|
||||
|
||||
@ -9,7 +9,6 @@ const ArrayStream = require('../common/arraystream');
|
||||
// \u001b[0J - Clear screen
|
||||
// \u001b[0K - Clear to line end
|
||||
const terminalCode = '\u001b[1G\u001b[0J> \u001b[3G';
|
||||
const previewCode = (str, n) => ` // ${str}\x1B[${n}G\x1B[0K`;
|
||||
const terminalCodeRegex = new RegExp(terminalCode.replace(/\[/g, '\\['), 'g');
|
||||
|
||||
function run({ input, output, event, checkTerminalCodes = true }) {
|
||||
@ -18,9 +17,7 @@ function run({ input, output, event, checkTerminalCodes = true }) {
|
||||
|
||||
stream.write = (msg) => found += msg.replace('\r', '');
|
||||
|
||||
let expected = `${terminalCode}.ed${previewCode('itor', 6)}i` +
|
||||
`${previewCode('tor', 7)}t${previewCode('or', 8)}o` +
|
||||
`${previewCode('r', 9)}r\n` +
|
||||
let expected = `${terminalCode}.editor\n` +
|
||||
'// Entering editor mode (^D to finish, ^C to cancel)\n' +
|
||||
`${input}${output}\n${terminalCode}`;
|
||||
|
||||
|
||||
@ -321,7 +321,8 @@ const tests = [
|
||||
showEscapeCodes: true,
|
||||
skip: !process.features.inspector,
|
||||
test: [
|
||||
'fun',
|
||||
'fu',
|
||||
'n',
|
||||
RIGHT,
|
||||
BACKSPACE,
|
||||
LEFT,
|
||||
@ -419,8 +420,8 @@ const tests = [
|
||||
'[', ' ', ']',
|
||||
'\n// []', '\n// []', '\n// []',
|
||||
'> util.inspect.replDefaults.showHidden',
|
||||
'\n// false', ' ', '\n// false',
|
||||
'=', ' ', 't', 'r', 'u', ' // e', 'e',
|
||||
'\n// false',
|
||||
' ', '=', ' ', 't', 'r', 'u', 'e',
|
||||
'true\n',
|
||||
'> ', '[', ' ', ']',
|
||||
'\n// [ [length]: 0 ]',
|
||||
|
||||
@ -23,28 +23,14 @@ function run({ useColors }) {
|
||||
r.on('exit', common.mustCall(() => {
|
||||
const actual = output.split('\n');
|
||||
|
||||
const firstLine = useColors ?
|
||||
'\x1B[1G\x1B[0J \x1B[1Gco\x1B[90mn\x1B[39m\x1B[3G\x1B[0Knst ' +
|
||||
'fo\x1B[90mr\x1B[39m\x1B[9G\x1B[0Ko = {' :
|
||||
'\x1B[1G\x1B[0J \x1B[1Gco // n\x1B[3G\x1B[0Knst ' +
|
||||
'fo // r\x1B[9G\x1B[0Ko = {';
|
||||
|
||||
// Validate the output, which contains terminal escape codes.
|
||||
assert.strictEqual(actual.length, 6 + process.features.inspector);
|
||||
assert.strictEqual(actual[0], firstLine);
|
||||
assert.strictEqual(actual.length, 6);
|
||||
assert.ok(actual[0].endsWith(input[0]));
|
||||
assert.ok(actual[1].includes('... '));
|
||||
assert.ok(actual[1].endsWith(input[1]));
|
||||
assert.ok(actual[2].includes('undefined'));
|
||||
if (process.features.inspector) {
|
||||
assert.ok(
|
||||
actual[3].endsWith(input[2]),
|
||||
`"${actual[3]}" should end with "${input[2]}"`
|
||||
);
|
||||
assert.ok(actual[4].includes(actual[5]));
|
||||
assert.strictEqual(actual[4].includes('//'), !useColors);
|
||||
}
|
||||
assert.strictEqual(actual[4 + process.features.inspector], '{}');
|
||||
// Ignore the last line, which is nothing but escape codes.
|
||||
assert.ok(actual[3].endsWith(input[2]));
|
||||
assert.strictEqual(actual[4], '{}');
|
||||
}));
|
||||
|
||||
inputStream.run(input);
|
||||
|
||||
@ -1,19 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const ArrayStream = require('../common/arraystream');
|
||||
const assert = require('assert');
|
||||
const { REPLServer } = require('repl');
|
||||
const { Stream } = require('stream');
|
||||
|
||||
common.skipIfInspectorDisabled();
|
||||
|
||||
const PROMPT = 'repl > ';
|
||||
|
||||
class REPLStream extends ArrayStream {
|
||||
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];
|
||||
@ -41,12 +50,14 @@ class REPLStream extends ArrayStream {
|
||||
this.on('line', onLine);
|
||||
});
|
||||
}
|
||||
pause() {}
|
||||
resume() {}
|
||||
}
|
||||
|
||||
function runAndWait(cmds, repl) {
|
||||
const promise = repl.inputStream.wait();
|
||||
for (const cmd of cmds) {
|
||||
repl.inputStream.run([cmd]);
|
||||
repl.inputStream.run(cmd);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
@ -93,9 +104,9 @@ async function tests(options) {
|
||||
'\x1B[33mtrue\x1B[39m',
|
||||
'\x1B[1G\x1B[0Jrepl > \x1B[8G'],
|
||||
[' \t { a: true};', [2, 5], '\x1B[33mtrue\x1B[39m',
|
||||
' \t { a: tru\x1B[90me\x1B[39m\x1B[26G\x1B[0Ke}',
|
||||
'\x1B[90m{ a: true }\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;',
|
||||
'\x1B[90mtrue\x1B[39m\x1B[29G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
|
||||
' { a: tru\x1B[90me\x1B[39m\x1B[18G\x1B[0Ke}',
|
||||
'\x1B[90m{ a: true }\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;',
|
||||
'\x1B[90mtrue\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
|
||||
'\x1B[33mtrue\x1B[39m',
|
||||
'\x1B[1G\x1B[0Jrepl > \x1B[8G']
|
||||
];
|
||||
|
||||
@ -32,7 +32,7 @@ class REPLStream extends ArrayStream {
|
||||
return true;
|
||||
}
|
||||
|
||||
wait(lookFor = PROMPT) {
|
||||
wait() {
|
||||
if (this.waitingForResponse) {
|
||||
throw new Error('Currently waiting for response to another command');
|
||||
}
|
||||
@ -43,7 +43,7 @@ class REPLStream extends ArrayStream {
|
||||
reject(err);
|
||||
};
|
||||
const onLine = () => {
|
||||
if (this.lines[this.lines.length - 1].includes(lookFor)) {
|
||||
if (this.lines[this.lines.length - 1].includes(PROMPT)) {
|
||||
this.removeListener('error', onError);
|
||||
this.removeListener('line', onLine);
|
||||
resolve(this.lines);
|
||||
@ -64,8 +64,8 @@ const testMe = repl.start({
|
||||
breakEvalOnSigint: true
|
||||
});
|
||||
|
||||
function runAndWait(cmds, lookFor) {
|
||||
const promise = putIn.wait(lookFor);
|
||||
function runAndWait(cmds) {
|
||||
const promise = putIn.wait();
|
||||
for (const cmd of cmds) {
|
||||
if (typeof cmd === 'string') {
|
||||
putIn.run([cmd]);
|
||||
@ -84,108 +84,67 @@ async function ordinaryTests() {
|
||||
'function koo() { return Promise.resolve(4); }'
|
||||
]);
|
||||
const testCases = [
|
||||
[ 'await Promise.resolve(0)',
|
||||
// Auto completion preview with colors stripped.
|
||||
['awaitaititt Proroomiseisesee.resolveolvelvevee(0)\r', '0']
|
||||
],
|
||||
[ '{ a: await Promise.resolve(1) }',
|
||||
// Auto completion preview with colors stripped.
|
||||
['{ a: awaitaititt Proroomiseisesee.resolveolvelvevee(1) }\r',
|
||||
'{ a: 1 }']
|
||||
],
|
||||
[ '_', '{ a: 1 }\r', { line: 0 } ],
|
||||
[ 'let { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;',
|
||||
[
|
||||
'letett { aa, bb } = awaitaititt Proroomiseisesee.resolveolvelvevee' +
|
||||
'({ aa: 1, bb: 2 }), f = 5;\r'
|
||||
]
|
||||
],
|
||||
[ 'aa', ['1\r', '1'] ],
|
||||
[ 'bb', ['2\r', '2'] ],
|
||||
[ 'f', ['5\r', '5'] ],
|
||||
[ 'let cc = await Promise.resolve(2)',
|
||||
['letett cc = awaitaititt Proroomiseisesee.resolveolvelvevee(2)\r']
|
||||
],
|
||||
[ 'cc', ['2\r', '2'] ],
|
||||
[ 'let dd;', ['letett dd;\r'] ],
|
||||
[ 'dd', ['undefined\r'] ],
|
||||
[ 'let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];',
|
||||
['letett [ii, { abc: { kook } }] = [0, { abc: { kook: 1 } }];\r'] ],
|
||||
[ 'ii', ['0\r', '0'] ],
|
||||
[ 'kk', ['1\r', '1'] ],
|
||||
[ 'var ll = await Promise.resolve(2);',
|
||||
['var letl = awaitaititt Proroomiseisesee.resolveolvelvevee(2);\r']
|
||||
],
|
||||
[ 'll', ['2\r', '2'] ],
|
||||
[ 'foo(await koo())',
|
||||
['f', '5oo', '[Function: foo](awaitaititt kooo())\r', '4'] ],
|
||||
[ '_', ['4\r', '4'] ],
|
||||
[ 'const m = foo(await koo());',
|
||||
['connst module = foo(awaitaititt kooo());\r'] ],
|
||||
[ 'm', ['4\r', '4' ] ],
|
||||
[ 'const n = foo(await\nkoo());',
|
||||
['connst n = foo(awaitaititt\r', '... kooo());\r', 'undefined'] ],
|
||||
[ 'n', ['4\r', '4'] ],
|
||||
['await Promise.resolve(0)', '0'],
|
||||
['{ a: await Promise.resolve(1) }', '{ a: 1 }'],
|
||||
['_', '{ a: 1 }'],
|
||||
['let { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;'],
|
||||
['aa', '1'],
|
||||
['bb', '2'],
|
||||
['f', '5'],
|
||||
['let cc = await Promise.resolve(2)'],
|
||||
['cc', '2'],
|
||||
['let dd;'],
|
||||
['dd'],
|
||||
['let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];'],
|
||||
['ii', '0'],
|
||||
['kk', '1'],
|
||||
['var ll = await Promise.resolve(2);'],
|
||||
['ll', '2'],
|
||||
['foo(await koo())', '4'],
|
||||
['_', '4'],
|
||||
['const m = foo(await koo());'],
|
||||
['m', '4'],
|
||||
['const n = foo(await\nkoo());',
|
||||
['const n = foo(await\r', '... koo());\r', 'undefined']],
|
||||
['n', '4'],
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
[ '`status: ${(await Promise.resolve({ status: 200 })).status}`',
|
||||
[
|
||||
'`stratus: ${(awaitaititt Proroomiseisesee.resolveolvelvevee' +
|
||||
'({ stratus: 200 })).stratus}`\r',
|
||||
"'status: 200'"
|
||||
]
|
||||
],
|
||||
[ 'for (let i = 0; i < 2; ++i) await i',
|
||||
['f', '5or (lett i = 0; i < 2; ++i) awaitaititt i\r', 'undefined'] ],
|
||||
[ 'for (let i = 0; i < 2; ++i) { await i }',
|
||||
['f', '5or (lett i = 0; i < 2; ++i) { awaitaititt i }\r', 'undefined']
|
||||
],
|
||||
[ 'await 0', ['awaitaititt 0\r', '0'] ],
|
||||
[ 'await 0; function foo() {}',
|
||||
['awaitaititt 0; functionnctionctiontioniononn foo() {}\r']
|
||||
],
|
||||
[ 'foo',
|
||||
['f', '5oo', '[Function: foo]\r', '[Function: foo]'] ],
|
||||
[ 'class Foo {}; await 1;', ['class Foo {}; awaitaititt 1;\r', '1'] ],
|
||||
[ 'Foo', ['Fooo', '[Function: Foo]\r', '[Function: Foo]'] ],
|
||||
[ 'if (await true) { function bar() {}; }',
|
||||
['if (awaitaititt truee) { functionnctionctiontioniononn bar() {}; }\r']
|
||||
],
|
||||
[ 'bar', ['barr', '[Function: bar]\r', '[Function: bar]'] ],
|
||||
[ 'if (await true) { class Bar {}; }',
|
||||
['if (awaitaititt truee) { class Bar {}; }\r']
|
||||
],
|
||||
[ 'Bar', 'Uncaught ReferenceError: Bar is not defined' ],
|
||||
[ 'await 0; function* gen(){}',
|
||||
['awaitaititt 0; functionnctionctiontioniononn* globalen(){}\r']
|
||||
],
|
||||
[ 'for (var i = 0; i < 10; ++i) { await i; }',
|
||||
['f', '5or (var i = 0; i < 10; ++i) { awaitaititt i; }\r', 'undefined'] ],
|
||||
[ 'i', ['10\r', '10'] ],
|
||||
[ 'for (let j = 0; j < 5; ++j) { await j; }',
|
||||
['f', '5or (lett j = 0; j < 5; ++j) { awaitaititt j; }\r', 'undefined'] ],
|
||||
[ 'j', 'Uncaught ReferenceError: j is not defined', { line: 0 } ],
|
||||
[ 'gen',
|
||||
['genn', '[GeneratorFunction: gen]\r', '[GeneratorFunction: gen]']
|
||||
],
|
||||
[ 'return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement',
|
||||
{ line: 3 } ],
|
||||
[ 'let o = await 1, p', ['lett os = awaitaititt 1, p\r'] ],
|
||||
[ 'p', ['undefined\r'] ],
|
||||
[ 'let q = 1, s = await 2', ['lett que = 1, s = awaitaititt 2\r'] ],
|
||||
[ 's', ['2\r', '2'] ],
|
||||
[ 'for await (let i of [1,2,3]) console.log(i)',
|
||||
[
|
||||
'f',
|
||||
'5or awaitaititt (lett i of [1,2,3]) connsolelee.logogg(i)\r',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'undefined'
|
||||
]
|
||||
['`status: ${(await Promise.resolve({ status: 200 })).status}`',
|
||||
"'status: 200'"],
|
||||
['for (let i = 0; i < 2; ++i) await i'],
|
||||
['for (let i = 0; i < 2; ++i) { await i }'],
|
||||
['await 0', '0'],
|
||||
['await 0; function foo() {}'],
|
||||
['foo', '[Function: foo]'],
|
||||
['class Foo {}; await 1;', '1'],
|
||||
['Foo', '[Function: Foo]'],
|
||||
['if (await true) { function bar() {}; }'],
|
||||
['bar', '[Function: bar]'],
|
||||
['if (await true) { class Bar {}; }'],
|
||||
['Bar', 'Uncaught ReferenceError: Bar is not defined'],
|
||||
['await 0; function* gen(){}'],
|
||||
['for (var i = 0; i < 10; ++i) { await i; }'],
|
||||
['i', '10'],
|
||||
['for (let j = 0; j < 5; ++j) { await j; }'],
|
||||
['j', 'Uncaught ReferenceError: j is not defined', { line: 0 }],
|
||||
['gen', '[GeneratorFunction: gen]'],
|
||||
['return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement',
|
||||
{ line: 3 }],
|
||||
['let o = await 1, p'],
|
||||
['p'],
|
||||
['let q = 1, s = await 2'],
|
||||
['s', '2'],
|
||||
['for await (let i of [1,2,3]) console.log(i)',
|
||||
[
|
||||
'for await (let i of [1,2,3]) console.log(i)\r',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'undefined'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
for (const [input, expected, options = {}] of testCases) {
|
||||
for (const [input, expected = [`${input}\r`], options = {}] of testCases) {
|
||||
console.log(`Testing ${input}`);
|
||||
const toBeRun = input.split('\n');
|
||||
const lines = await runAndWait(toBeRun);
|
||||
@ -216,7 +175,7 @@ async function ctrlCTest() {
|
||||
'await timeout(100000)',
|
||||
{ ctrl: true, name: 'c' }
|
||||
]), [
|
||||
'awaitaititt timeoutmeouteoutoututt(100000)\r',
|
||||
'await timeout(100000)\r',
|
||||
'Uncaught:',
|
||||
'[Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' +
|
||||
'Script execution was interrupted by `SIGINT`] {',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user