@@ -16,6 +16,7 @@ var sys = require('sys');
1616var Script = process . binding ( 'evals' ) . Script ;
1717var evalcx = Script . runInContext ;
1818var path = require ( "path" ) ;
19+ var fs = require ( "fs" ) ;
1920var rl = require ( 'readline' ) ;
2021var context ;
2122
@@ -162,7 +163,7 @@ REPLServer.prototype.complete = function (line) {
162163 completeOn ,
163164 match , filter , i , j , group , c ;
164165
165- // REPL comments (e.g. ".break").
166+ // REPL commands (e.g. ".break").
166167 var match = null ;
167168 match = line . match ( / ^ \s * ( \. \w * ) $ / ) ;
168169 if ( match ) {
@@ -173,6 +174,72 @@ REPLServer.prototype.complete = function (line) {
173174 }
174175 }
175176
177+ // require('...<Tab>')
178+ else if ( match = line . match ( / \b r e q u i r e \s * \( [ ' " ] ( ( [ \w \. \/ - ] + \/ ) ? ( [ \w \. \/ - ] * ) ) / ) ) {
179+ //TODO: suggest require.exts be exposed to be introspec registered extensions?
180+ //TODO: suggest include the '.' in exts in internal repr: parity with `path.extname`.
181+ var exts = [ ".js" , ".node" ] ;
182+ var indexRe = new RegExp ( '^index(' + exts . map ( regexpEscape ) . join ( '|' ) + ')$' ) ;
183+
184+ completeOn = match [ 1 ] ;
185+ var subdir = match [ 2 ] || "" ;
186+ var filter = match [ 1 ] ;
187+ var dir , files , f , name , base , ext , abs , subfiles , s ;
188+ group = [ ] ;
189+ for ( i = 0 ; i < require . paths . length ; i ++ ) {
190+ dir = require . paths [ i ] ;
191+ if ( subdir && subdir [ 0 ] === '/' ) {
192+ dir = subdir ;
193+ } else if ( subdir ) {
194+ dir = path . join ( dir , subdir ) ;
195+ }
196+ try {
197+ files = fs . readdirSync ( dir ) ;
198+ } catch ( e ) {
199+ continue ;
200+ }
201+ for ( f = 0 ; f < files . length ; f ++ ) {
202+ name = files [ f ] ;
203+ ext = path . extname ( name ) ;
204+ base = name . slice ( 0 , - ext . length ) ;
205+ if ( base . match ( / - \d + \. \d + ( \. \d + ) ? / ) || name === ".npm" ) {
206+ // Exclude versioned names that 'npm' installs.
207+ continue ;
208+ }
209+ if ( exts . indexOf ( ext ) !== - 1 ) {
210+ if ( ! subdir || base !== "index" ) {
211+ group . push ( subdir + base ) ;
212+ }
213+ } else {
214+ abs = path . join ( dir , name ) ;
215+ try {
216+ if ( fs . statSync ( abs ) . isDirectory ( ) ) {
217+ group . push ( subdir + name + '/' ) ;
218+ subfiles = fs . readdirSync ( abs ) ;
219+ for ( s = 0 ; s < subfiles . length ; s ++ ) {
220+ if ( indexRe . test ( subfiles [ s ] ) ) {
221+ group . push ( subdir + name ) ;
222+ }
223+ }
224+ }
225+ } catch ( e ) { }
226+ }
227+ }
228+ }
229+ if ( group . length ) {
230+ completionGroups . push ( group ) ;
231+ }
232+
233+ if ( ! subdir ) {
234+ // Kind of lame that this needs to be updated manually.
235+ // Intentionally excluding moved modules: posix, utils.
236+ var builtinLibs = [ 'assert' , 'buffer' , 'child_process' , 'crypto' , 'dgram' ,
237+ 'dns' , 'events' , 'file' , 'freelist' , 'fs' , 'http' , 'net' , 'path' ,
238+ 'querystring' , 'readline' , 'repl' , 'string_decoder' , 'sys' , 'tcp' , 'url' ] ;
239+ completionGroups . push ( builtinLibs ) ;
240+ }
241+ }
242+
176243 // Handle variable member lookup.
177244 // We support simple chained expressions like the following (no function
178245 // calls, etc.). That is for simplicity and also because we *eval* that
@@ -348,6 +415,10 @@ function trimWhitespace (cmd) {
348415 }
349416}
350417
418+ function regexpEscape ( s ) {
419+ return s . replace ( / [ - [ \] { } ( ) * + ? . , \\ ^ $ | # \s ] / g, "\\$&" ) ;
420+ }
421+
351422/**
352423 * Converts commands that use var and function <name>() to use the
353424 * local exports.context when evaled. This provides a local context
0 commit comments