repl: fix tab completion not working with computer string properties

PR-URL: https://github.com/nodejs/node/pull/58709
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
Dario Piotrowicz 2025-06-17 01:39:15 +01:00 committed by GitHub
parent ea5d37ecbe
commit 07220230d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 131 additions and 1 deletions

View File

@ -1226,7 +1226,7 @@ const importRE = /\bimport\s*\(\s*['"`](([\w@./:-]+\/)?(?:[\w@./:-]*))(?![^'"`])
const requireRE = /\brequire\s*\(\s*['"`](([\w@./:-]+\/)?(?:[\w@./:-]*))(?![^'"`])$/;
const fsAutoCompleteRE = /fs(?:\.promises)?\.\s*[a-z][a-zA-Z]+\(\s*["'](.*)/;
const simpleExpressionRE =
/(?:[\w$'"`[{(](?:\w|\$|['"`\]})])*\??\.)*[a-zA-Z_$](?:\w|\$)*\??\.?$/;
/(?:[\w$'"`[{(](?:(\w| |\t)*?['"`]|\$|['"`\]})])*\??(?:\.|])?)*?(?:[a-zA-Z_$])?(?:\w|\$)*\??\.?$/;
const versionedFileNamesRe = /-\d+\.\d+/;
function isIdentifier(str) {

View File

@ -0,0 +1,130 @@
'use strict';
const common = require('../common');
const ArrayStream = require('../common/arraystream');
const { describe, it, before, after } = require('node:test');
const assert = require('assert');
const repl = require('repl');
function prepareREPL() {
const input = new ArrayStream();
const replServer = repl.start({
prompt: '',
input,
output: process.stdout,
allowBlockingCompletions: true,
});
// Some errors are passed to the domain, but do not callback
replServer._domain.on('error', assert.ifError);
return { replServer, input };
}
function testCompletion(replServer, { input, expectedCompletions }) {
replServer.complete(
input,
common.mustCall((_error, data) => {
assert.deepStrictEqual(data, [expectedCompletions, input]);
}),
);
};
describe('REPL tab object completion on computed properties', () => {
describe('simple string cases', () => {
let replServer;
before(() => {
const { replServer: server, input } = prepareREPL();
replServer = server;
input.run([
`
const obj = {
one: 1,
innerObj: { two: 2 },
'inner object': { three: 3 },
};
const oneStr = 'one';
`,
]);
});
after(() => {
replServer.close();
});
it('works with double quoted strings', () => testCompletion(replServer, {
input: 'obj["one"].toFi',
expectedCompletions: ['obj["one"].toFixed'],
}));
it('works with single quoted strings', () => testCompletion(replServer, {
input: "obj['one'].toFi",
expectedCompletions: ["obj['one'].toFixed"],
}));
it('works with template strings', () => testCompletion(replServer, {
input: 'obj[`one`].toFi',
expectedCompletions: ['obj[`one`].toFixed'],
}));
it('works with nested objects', () => {
testCompletion(replServer, {
input: 'obj["innerObj"].tw',
expectedCompletions: ['obj["innerObj"].two'],
});
testCompletion(replServer, {
input: 'obj["innerObj"].two.tofi',
expectedCompletions: ['obj["innerObj"].two.toFixed'],
});
});
it('works with nested objects combining different type of strings', () => testCompletion(replServer, {
input: 'obj["innerObj"][`two`].tofi',
expectedCompletions: ['obj["innerObj"][`two`].toFixed'],
}));
it('works with strings with spaces', () => testCompletion(replServer, {
input: 'obj["inner object"].th',
expectedCompletions: ['obj["inner object"].three'],
}));
});
describe('variables as indexes', () => {
let replServer;
before(() => {
const { replServer: server, input } = prepareREPL();
replServer = server;
input.run([
`
const oneStr = 'One';
const helloWorldStr = 'Hello' + ' ' + 'World';
const obj = {
[oneStr]: 1,
['Hello World']: 'hello world!',
};
`,
]);
});
after(() => {
replServer.close();
});
it('works with a simple variable', () => testCompletion(replServer, {
input: 'obj[oneStr].toFi',
expectedCompletions: ['obj[oneStr].toFixed'],
}));
it('works with a computed variable', () => testCompletion(replServer, {
input: 'obj[helloWorldStr].tolocaleup',
expectedCompletions: ['obj[helloWorldStr].toLocaleUpperCase'],
}));
});
});