@@ -1111,12 +1111,28 @@ REPLServer.prototype.complete = function() {
1111
1111
this . completer . apply ( this , arguments ) ;
1112
1112
} ;
1113
1113
1114
- function gracefulOperation ( fn , args , alternative ) {
1114
+ function gracefulReaddir ( ... args ) {
1115
1115
try {
1116
- return fn ( ...args ) ;
1117
- } catch {
1118
- return alternative ;
1116
+ return fs . readdirSync ( ...args ) ;
1117
+ } catch { }
1118
+ }
1119
+
1120
+ function completeFSFunctions ( line ) {
1121
+ let baseName = '' ;
1122
+ let filePath = line . match ( fsAutoCompleteRE ) [ 1 ] ;
1123
+ let fileList = gracefulReaddir ( filePath , { withFileTypes : true } ) ;
1124
+
1125
+ if ( ! fileList ) {
1126
+ baseName = path . basename ( filePath ) ;
1127
+ filePath = path . dirname ( filePath ) ;
1128
+ fileList = gracefulReaddir ( filePath , { withFileTypes : true } ) || [ ] ;
1119
1129
}
1130
+
1131
+ const completions = fileList
1132
+ . filter ( ( dirent ) => dirent . name . startsWith ( baseName ) )
1133
+ . map ( ( d ) => d . name ) ;
1134
+
1135
+ return [ [ completions ] , baseName ] ;
1120
1136
}
1121
1137
1122
1138
// Provide a list of completions for the given leading text. This is
@@ -1145,8 +1161,6 @@ function complete(line, callback) {
1145
1161
if ( completeOn . length ) {
1146
1162
filter = completeOn ;
1147
1163
}
1148
-
1149
- completionGroupsLoaded ( ) ;
1150
1164
} else if ( requireRE . test ( line ) ) {
1151
1165
// require('...<Tab>')
1152
1166
const extensions = ObjectKeys ( this . context . require . extensions ) ;
@@ -1173,11 +1187,7 @@ function complete(line, callback) {
1173
1187
1174
1188
for ( let dir of paths ) {
1175
1189
dir = path . resolve ( dir , subdir ) ;
1176
- const dirents = gracefulOperation (
1177
- fs . readdirSync ,
1178
- [ dir , { withFileTypes : true } ] ,
1179
- [ ]
1180
- ) ;
1190
+ const dirents = gracefulReaddir ( dir , { withFileTypes : true } ) || [ ] ;
1181
1191
for ( const dirent of dirents ) {
1182
1192
if ( versionedFileNamesRe . test ( dirent . name ) || dirent . name === '.npm' ) {
1183
1193
// Exclude versioned names that 'npm' installs.
@@ -1193,7 +1203,7 @@ function complete(line, callback) {
1193
1203
}
1194
1204
group . push ( `${ subdir } ${ dirent . name } /` ) ;
1195
1205
const absolute = path . resolve ( dir , dirent . name ) ;
1196
- const subfiles = gracefulOperation ( fs . readdirSync , [ absolute ] , [ ] ) ;
1206
+ const subfiles = gracefulReaddir ( absolute ) || [ ] ;
1197
1207
for ( const subfile of subfiles ) {
1198
1208
if ( indexes . includes ( subfile ) ) {
1199
1209
group . push ( `${ subdir } ${ dirent . name } ` ) ;
@@ -1209,31 +1219,8 @@ function complete(line, callback) {
1209
1219
if ( ! subdir ) {
1210
1220
completionGroups . push ( _builtinLibs ) ;
1211
1221
}
1212
-
1213
- completionGroupsLoaded ( ) ;
1214
1222
} else if ( fsAutoCompleteRE . test ( line ) ) {
1215
- filter = '' ;
1216
- let filePath = line . match ( fsAutoCompleteRE ) [ 1 ] ;
1217
- let fileList ;
1218
-
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 { }
1234
- }
1235
-
1236
- completionGroupsLoaded ( ) ;
1223
+ [ completionGroups , completeOn ] = completeFSFunctions ( line ) ;
1237
1224
// Handle variable member lookup.
1238
1225
// We support simple chained expressions like the following (no function
1239
1226
// calls, etc.). That is for simplicity and also because we *eval* that
@@ -1245,25 +1232,22 @@ function complete(line, callback) {
1245
1232
// foo<|> # all scope vars with filter 'foo'
1246
1233
// foo.<|> # completions for 'foo' with filter ''
1247
1234
} else if ( line . length === 0 || / \w | \. | \$ / . test ( line [ line . length - 1 ] ) ) {
1248
- const match = simpleExpressionRE . exec ( line ) ;
1235
+ const [ match ] = simpleExpressionRE . exec ( line ) || [ '' ] ;
1249
1236
if ( line . length !== 0 && ! match ) {
1250
1237
completionGroupsLoaded ( ) ;
1251
1238
return ;
1252
1239
}
1253
- let expr ;
1254
- completeOn = ( match ? match [ 0 ] : '' ) ;
1255
- if ( line . length === 0 ) {
1256
- expr = '' ;
1257
- } else if ( line [ line . length - 1 ] === '.' ) {
1258
- expr = match [ 0 ] . slice ( 0 , match [ 0 ] . length - 1 ) ;
1259
- } else {
1260
- const bits = match [ 0 ] . split ( '.' ) ;
1240
+ let expr = '' ;
1241
+ completeOn = match ;
1242
+ if ( line . endsWith ( '.' ) ) {
1243
+ expr = match . slice ( 0 , - 1 ) ;
1244
+ } else if ( line . length !== 0 ) {
1245
+ const bits = match . split ( '.' ) ;
1261
1246
filter = bits . pop ( ) ;
1262
1247
expr = bits . join ( '.' ) ;
1263
1248
}
1264
1249
1265
1250
// Resolve expr and get its completions.
1266
- const memberGroups = [ ] ;
1267
1251
if ( ! expr ) {
1268
1252
// Get global vars synchronously
1269
1253
completionGroups . push ( getGlobalLexicalScopeNames ( this [ kContextId ] ) ) ;
@@ -1284,39 +1268,34 @@ function complete(line, callback) {
1284
1268
}
1285
1269
1286
1270
let chaining = '.' ;
1287
- if ( expr [ expr . length - 1 ] === '?' ) {
1271
+ if ( expr . endsWith ( '?' ) ) {
1288
1272
expr = expr . slice ( 0 , - 1 ) ;
1289
1273
chaining = '?.' ;
1290
1274
}
1291
1275
1276
+ const memberGroups = [ ] ;
1292
1277
const evalExpr = `try { ${ expr } } catch {}` ;
1293
1278
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
- }
1279
+ try {
1280
+ let p ;
1281
+ if ( ( typeof obj === 'object' && obj !== null ) ||
1282
+ typeof obj === 'function' ) {
1283
+ memberGroups . push ( filteredOwnPropertyNames ( obj ) ) ;
1284
+ p = ObjectGetPrototypeOf ( obj ) ;
1285
+ } else {
1286
+ p = obj . constructor ? obj . constructor . prototype : null ;
1304
1287
}
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 { }
1288
+ // Circular refs possible? Let's guard against that.
1289
+ let sentinel = 5 ;
1290
+ while ( p !== null && sentinel -- !== 0 ) {
1291
+ memberGroups . push ( filteredOwnPropertyNames ( p ) ) ;
1292
+ p = ObjectGetPrototypeOf ( p ) ;
1293
+ }
1294
+ } catch {
1295
+ // Maybe a Proxy object without `getOwnPropertyNames` trap.
1296
+ // We simply ignore it here, as we don't want to break the
1297
+ // autocompletion. Fixes the bug
1298
+ // https://github.com/nodejs/node/issues/2119
1320
1299
}
1321
1300
1322
1301
if ( memberGroups . length ) {
@@ -1331,21 +1310,21 @@ function complete(line, callback) {
1331
1310
1332
1311
completionGroupsLoaded ( ) ;
1333
1312
} ) ;
1334
- } else {
1335
- completionGroupsLoaded ( ) ;
1313
+ return ;
1336
1314
}
1337
1315
1316
+ return completionGroupsLoaded ( ) ;
1317
+
1338
1318
// Will be called when all completionGroups are in place
1339
1319
// Useful for async autocompletion
1340
1320
function completionGroupsLoaded ( ) {
1341
1321
// Filter, sort (within each group), uniq and merge the completion groups.
1342
1322
if ( completionGroups . length && filter ) {
1343
1323
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 ) ;
1324
+ for ( const group of completionGroups ) {
1325
+ const filteredGroup = group . filter ( ( str ) => str . startsWith ( filter ) ) ;
1326
+ if ( filteredGroup . length ) {
1327
+ newCompletionGroups . push ( filteredGroup ) ;
1349
1328
}
1350
1329
}
1351
1330
completionGroups = newCompletionGroups ;
0 commit comments