repl: support optional chaining during autocompletion

This makes sure the autocompletion is able to handle optional
chaining notations.

Signed-off-by: Ruben Bridgewater <ruben@bridgewater.de>

PR-URL: https://github.com/nodejs/node/pull/33450
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Ruben Bridgewater
2020-05-17 05:20:08 +02:00
parent 9de08f773e
commit 76c5dc995e
2 changed files with 25 additions and 8 deletions

View File

@@ -1066,7 +1066,7 @@ REPLServer.prototype.turnOffEditorMode = deprecate(
const requireRE = /\brequire\s*\(\s*['"`](([\w@./-]+\/)?(?:[\w@./-]*))(?![^'"`])$/;
const fsAutoCompleteRE = /fs(?:\.promises)?\.\s*[a-z][a-zA-Z]+\(\s*["'](.*)/;
const simpleExpressionRE =
/(?:[a-zA-Z_$](?:\w|\$)*\.)*[a-zA-Z_$](?:\w|\$)*\.?$/;
/(?:[a-zA-Z_$](?:\w|\$)*\??\.)*[a-zA-Z_$](?:\w|\$)*\??\.?$/;
function isIdentifier(str) {
if (str === '') {
@@ -1138,7 +1138,7 @@ function complete(line, callback) {
line = line.trimLeft();
// REPL commands (e.g. ".break").
let filter;
let filter = '';
if (/^\s*\.(\w*)$/.test(line)) {
completionGroups.push(ObjectKeys(this.commands));
completeOn = line.match(/^\s*\.(\w*)$/)[1];
@@ -1253,10 +1253,8 @@ function complete(line, callback) {
let expr;
completeOn = (match ? match[0] : '');
if (line.length === 0) {
filter = '';
expr = '';
} else if (line[line.length - 1] === '.') {
filter = '';
expr = match[0].slice(0, match[0].length - 1);
} else {
const bits = match[0].split('.');
@@ -1285,6 +1283,12 @@ function complete(line, callback) {
return;
}
let chaining = '.';
if (expr[expr.length - 1] === '?') {
expr = expr.slice(0, -1);
chaining = '?.';
}
const evalExpr = `try { ${expr} } catch {}`;
this.eval(evalExpr, this.context, 'repl', (e, obj) => {
if (obj != null) {
@@ -1316,12 +1320,12 @@ function complete(line, callback) {
}
if (memberGroups.length) {
for (let i = 0; i < memberGroups.length; i++) {
completionGroups.push(
memberGroups[i].map((member) => `${expr}.${member}`));
expr += chaining;
for (const group of memberGroups) {
completionGroups.push(group.map((member) => `${expr}${member}`));
}
if (filter) {
filter = `${expr}.${filter}`;
filter = `${expr}${filter}`;
}
}

View File

@@ -68,6 +68,19 @@ testMe.complete('console.lo', common.mustCall(function(error, data) {
assert.deepStrictEqual(data, [['console.log'], 'console.lo']);
}));
testMe.complete('console?.lo', common.mustCall((error, data) => {
assert.deepStrictEqual(data, [['console?.log'], 'console?.lo']);
}));
testMe.complete('console?.zzz', common.mustCall((error, data) => {
assert.deepStrictEqual(data, [[], 'console?.zzz']);
}));
testMe.complete('console?.', common.mustCall((error, data) => {
assert(data[0].includes('console?.log'));
assert.strictEqual(data[1], 'console?.');
}));
// Tab Complete will return globally scoped variables
putIn.run(['};']);
testMe.complete('inner.o', common.mustCall(function(error, data) {