Skip to content

Commit

Permalink
repl: support optional chaining during autocompletion
Browse files Browse the repository at this point in the history
This makes sure the autocompletion is able to handle optional
chaining notations.

Signed-off-by: Ruben Bridgewater <[email protected]>

PR-URL: nodejs#33450
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
  • Loading branch information
BridgeAR committed May 20, 2020
1 parent 9de08f7 commit 76c5dc9
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 8 deletions.
20 changes: 12 additions & 8 deletions lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 === '') {
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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('.');
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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}`;
}
}

Expand Down
13 changes: 13 additions & 0 deletions test/parallel/test-repl-tab-complete.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 76c5dc9

Please sign in to comment.