@@ -16,6 +16,7 @@ var sys = require('sys');
16
16
var Script = process . binding ( 'evals' ) . Script ;
17
17
var evalcx = Script . runInContext ;
18
18
var path = require ( "path" ) ;
19
+ var fs = require ( "fs" ) ;
19
20
var rl = require ( 'readline' ) ;
20
21
var context ;
21
22
@@ -162,7 +163,7 @@ REPLServer.prototype.complete = function (line) {
162
163
completeOn ,
163
164
match , filter , i , j , group , c ;
164
165
165
- // REPL comments (e.g. ".break").
166
+ // REPL commands (e.g. ".break").
166
167
var match = null ;
167
168
match = line . match ( / ^ \s * ( \. \w * ) $ / ) ;
168
169
if ( match ) {
@@ -173,6 +174,72 @@ REPLServer.prototype.complete = function (line) {
173
174
}
174
175
}
175
176
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
+
176
243
// Handle variable member lookup.
177
244
// We support simple chained expressions like the following (no function
178
245
// calls, etc.). That is for simplicity and also because we *eval* that
@@ -348,6 +415,10 @@ function trimWhitespace (cmd) {
348
415
}
349
416
}
350
417
418
+ function regexpEscape ( s ) {
419
+ return s . replace ( / [ - [ \] { } ( ) * + ? . , \\ ^ $ | # \s ] / g, "\\$&" ) ;
420
+ }
421
+
351
422
/**
352
423
* Converts commands that use var and function <name>() to use the
353
424
* local exports.context when evaled. This provides a local context
0 commit comments