Skip to content

Commit 8c487ad

Browse files
committed
repl: simplify repl autocompletion
This refactors the repl autocompletion code for simplicity and readability. Signed-off-by: Ruben Bridgewater <[email protected]>
1 parent 0acc704 commit 8c487ad

File tree

1 file changed

+42
-65
lines changed

1 file changed

+42
-65
lines changed

lib/repl.js

Lines changed: 42 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,12 +1111,10 @@ REPLServer.prototype.complete = function() {
11111111
this.completer.apply(this, arguments);
11121112
};
11131113

1114-
function gracefulOperation(fn, args, alternative) {
1114+
function gracefulReaddir(...args) {
11151115
try {
1116-
return fn(...args);
1117-
} catch {
1118-
return alternative;
1119-
}
1116+
return fs.readdirSync(...args);
1117+
} catch {}
11201118
}
11211119

11221120
// Provide a list of completions for the given leading text. This is
@@ -1145,8 +1143,6 @@ function complete(line, callback) {
11451143
if (completeOn.length) {
11461144
filter = completeOn;
11471145
}
1148-
1149-
completionGroupsLoaded();
11501146
} else if (requireRE.test(line)) {
11511147
// require('...<Tab>')
11521148
const extensions = ObjectKeys(this.context.require.extensions);
@@ -1173,11 +1169,7 @@ function complete(line, callback) {
11731169

11741170
for (let dir of paths) {
11751171
dir = path.resolve(dir, subdir);
1176-
const dirents = gracefulOperation(
1177-
fs.readdirSync,
1178-
[dir, { withFileTypes: true }],
1179-
[]
1180-
);
1172+
const dirents = gracefulReaddir(dir, { withFileTypes: true }) || [];
11811173
for (const dirent of dirents) {
11821174
if (versionedFileNamesRe.test(dirent.name) || dirent.name === '.npm') {
11831175
// Exclude versioned names that 'npm' installs.
@@ -1193,7 +1185,7 @@ function complete(line, callback) {
11931185
}
11941186
group.push(`${subdir}${dirent.name}/`);
11951187
const absolute = path.resolve(dir, dirent.name);
1196-
const subfiles = gracefulOperation(fs.readdirSync, [absolute], []);
1188+
const subfiles = gracefulReaddir(absolute) || [];
11971189
for (const subfile of subfiles) {
11981190
if (indexes.includes(subfile)) {
11991191
group.push(`${subdir}${dirent.name}`);
@@ -1209,31 +1201,22 @@ function complete(line, callback) {
12091201
if (!subdir) {
12101202
completionGroups.push(_builtinLibs);
12111203
}
1212-
1213-
completionGroupsLoaded();
12141204
} else if (fsAutoCompleteRE.test(line)) {
1215-
filter = '';
1205+
let baseName = '';
12161206
let filePath = line.match(fsAutoCompleteRE)[1];
1217-
let fileList;
1207+
let fileList = gracefulReaddir(filePath, { withFileTypes: true });
12181208

1219-
try {
1220-
fileList = fs.readdirSync(filePath, { withFileTypes: true });
1221-
completionGroups.push(fileList.map((dirent) => dirent.name));
1222-
completeOn = '';
1223-
} catch {
1224-
try {
1225-
const baseName = path.basename(filePath);
1226-
filePath = path.dirname(filePath);
1227-
fileList = fs.readdirSync(filePath, { withFileTypes: true });
1228-
const filteredValue = fileList.filter((d) =>
1229-
d.name.startsWith(baseName))
1230-
.map((d) => d.name);
1231-
completionGroups.push(filteredValue);
1232-
completeOn = baseName;
1233-
} catch {}
1209+
if (!fileList) {
1210+
baseName = path.basename(filePath);
1211+
filePath = path.dirname(filePath);
1212+
fileList = gracefulReaddir(filePath, { withFileTypes: true }) || [];
12341213
}
12351214

1236-
completionGroupsLoaded();
1215+
const filteredValue = fileList
1216+
.filter((dirent) => dirent.name.startsWith(baseName))
1217+
.map((d) => d.name);
1218+
completionGroups.push(filteredValue);
1219+
completeOn = baseName;
12371220
// Handle variable member lookup.
12381221
// We support simple chained expressions like the following (no function
12391222
// calls, etc.). That is for simplicity and also because we *eval* that
@@ -1291,32 +1274,26 @@ function complete(line, callback) {
12911274

12921275
const evalExpr = `try { ${expr} } catch {}`;
12931276
this.eval(evalExpr, this.context, 'repl', (e, obj) => {
1294-
if (obj != null) {
1295-
if (typeof obj === 'object' || typeof obj === 'function') {
1296-
try {
1297-
memberGroups.push(filteredOwnPropertyNames(obj));
1298-
} catch {
1299-
// Probably a Proxy object without `getOwnPropertyNames` trap.
1300-
// We simply ignore it here, as we don't want to break the
1301-
// autocompletion. Fixes the bug
1302-
// https://github.com/nodejs/node/issues/2119
1303-
}
1277+
try {
1278+
let p;
1279+
if ((typeof obj === 'object' && obj !== null) ||
1280+
typeof obj === 'function') {
1281+
memberGroups.push(filteredOwnPropertyNames(obj));
1282+
p = ObjectGetPrototypeOf(obj);
1283+
} else {
1284+
p = obj.constructor ? obj.constructor.prototype : null;
13041285
}
1305-
// Works for non-objects
1306-
try {
1307-
let p;
1308-
if (typeof obj === 'object' || typeof obj === 'function') {
1309-
p = ObjectGetPrototypeOf(obj);
1310-
} else {
1311-
p = obj.constructor ? obj.constructor.prototype : null;
1312-
}
1313-
// Circular refs possible? Let's guard against that.
1314-
let sentinel = 5;
1315-
while (p !== null && sentinel-- !== 0) {
1316-
memberGroups.push(filteredOwnPropertyNames(p));
1317-
p = ObjectGetPrototypeOf(p);
1318-
}
1319-
} catch {}
1286+
// Circular refs possible? Let's guard against that.
1287+
let sentinel = 5;
1288+
while (p !== null && sentinel-- !== 0) {
1289+
memberGroups.push(filteredOwnPropertyNames(p));
1290+
p = ObjectGetPrototypeOf(p);
1291+
}
1292+
} catch {
1293+
// Maybe a Proxy object without `getOwnPropertyNames` trap.
1294+
// We simply ignore it here, as we don't want to break the
1295+
// autocompletion. Fixes the bug
1296+
// https://github.com/nodejs/node/issues/2119
13201297
}
13211298

13221299
if (memberGroups.length) {
@@ -1331,21 +1308,21 @@ function complete(line, callback) {
13311308

13321309
completionGroupsLoaded();
13331310
});
1334-
} else {
1335-
completionGroupsLoaded();
1311+
return;
13361312
}
13371313

1314+
return completionGroupsLoaded();
1315+
13381316
// Will be called when all completionGroups are in place
13391317
// Useful for async autocompletion
13401318
function completionGroupsLoaded() {
13411319
// Filter, sort (within each group), uniq and merge the completion groups.
13421320
if (completionGroups.length && filter) {
13431321
const newCompletionGroups = [];
1344-
for (let i = 0; i < completionGroups.length; i++) {
1345-
group = completionGroups[i]
1346-
.filter((elem) => elem.indexOf(filter) === 0);
1347-
if (group.length) {
1348-
newCompletionGroups.push(group);
1322+
for (const group of completionGroups) {
1323+
const filteredGroup = group.filter((e) => e.indexOf(filter) === 0);
1324+
if (filteredGroup.length) {
1325+
newCompletionGroups.push(filteredGroup);
13491326
}
13501327
}
13511328
completionGroups = newCompletionGroups;

0 commit comments

Comments
 (0)