@@ -1111,12 +1111,28 @@ 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 ;
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 } ) || [ ] ;
11191129 }
1130+
1131+ const completions = fileList
1132+ . filter ( ( dirent ) => dirent . name . startsWith ( baseName ) )
1133+ . map ( ( d ) => d . name ) ;
1134+
1135+ return [ [ completions ] , baseName ] ;
11201136}
11211137
11221138// Provide a list of completions for the given leading text. This is
@@ -1145,8 +1161,6 @@ function complete(line, callback) {
11451161 if ( completeOn . length ) {
11461162 filter = completeOn ;
11471163 }
1148-
1149- completionGroupsLoaded ( ) ;
11501164 } else if ( requireRE . test ( line ) ) {
11511165 // require('...<Tab>')
11521166 const extensions = ObjectKeys ( this . context . require . extensions ) ;
@@ -1173,11 +1187,7 @@ function complete(line, callback) {
11731187
11741188 for ( let dir of paths ) {
11751189 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 } ) || [ ] ;
11811191 for ( const dirent of dirents ) {
11821192 if ( versionedFileNamesRe . test ( dirent . name ) || dirent . name === '.npm' ) {
11831193 // Exclude versioned names that 'npm' installs.
@@ -1193,7 +1203,7 @@ function complete(line, callback) {
11931203 }
11941204 group . push ( `${ subdir } ${ dirent . name } /` ) ;
11951205 const absolute = path . resolve ( dir , dirent . name ) ;
1196- const subfiles = gracefulOperation ( fs . readdirSync , [ absolute ] , [ ] ) ;
1206+ const subfiles = gracefulReaddir ( absolute ) || [ ] ;
11971207 for ( const subfile of subfiles ) {
11981208 if ( indexes . includes ( subfile ) ) {
11991209 group . push ( `${ subdir } ${ dirent . name } ` ) ;
@@ -1209,31 +1219,8 @@ function complete(line, callback) {
12091219 if ( ! subdir ) {
12101220 completionGroups . push ( _builtinLibs ) ;
12111221 }
1212-
1213- completionGroupsLoaded ( ) ;
12141222 } 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 ) ;
12371224 // Handle variable member lookup.
12381225 // We support simple chained expressions like the following (no function
12391226 // calls, etc.). That is for simplicity and also because we *eval* that
@@ -1245,25 +1232,22 @@ function complete(line, callback) {
12451232 // foo<|> # all scope vars with filter 'foo'
12461233 // foo.<|> # completions for 'foo' with filter ''
12471234 } else if ( line . length === 0 || / \w | \. | \$ / . test ( line [ line . length - 1 ] ) ) {
1248- const match = simpleExpressionRE . exec ( line ) ;
1235+ const [ match ] = simpleExpressionRE . exec ( line ) || [ '' ] ;
12491236 if ( line . length !== 0 && ! match ) {
12501237 completionGroupsLoaded ( ) ;
12511238 return ;
12521239 }
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 ( '.' ) ;
12611246 filter = bits . pop ( ) ;
12621247 expr = bits . join ( '.' ) ;
12631248 }
12641249
12651250 // Resolve expr and get its completions.
1266- const memberGroups = [ ] ;
12671251 if ( ! expr ) {
12681252 // Get global vars synchronously
12691253 completionGroups . push ( getGlobalLexicalScopeNames ( this [ kContextId ] ) ) ;
@@ -1284,39 +1268,34 @@ function complete(line, callback) {
12841268 }
12851269
12861270 let chaining = '.' ;
1287- if ( expr [ expr . length - 1 ] === '?' ) {
1271+ if ( expr . endsWith ( '?' ) ) {
12881272 expr = expr . slice ( 0 , - 1 ) ;
12891273 chaining = '?.' ;
12901274 }
12911275
1276+ const memberGroups = [ ] ;
12921277 const evalExpr = `try { ${ expr } } catch {}` ;
12931278 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 ;
13041287 }
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
13201299 }
13211300
13221301 if ( memberGroups . length ) {
@@ -1331,21 +1310,21 @@ function complete(line, callback) {
13311310
13321311 completionGroupsLoaded ( ) ;
13331312 } ) ;
1334- } else {
1335- completionGroupsLoaded ( ) ;
1313+ return ;
13361314 }
13371315
1316+ return completionGroupsLoaded ( ) ;
1317+
13381318 // Will be called when all completionGroups are in place
13391319 // Useful for async autocompletion
13401320 function completionGroupsLoaded ( ) {
13411321 // Filter, sort (within each group), uniq and merge the completion groups.
13421322 if ( completionGroups . length && filter ) {
13431323 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 ) ;
13491328 }
13501329 }
13511330 completionGroups = newCompletionGroups ;
0 commit comments