Skip to content

Commit 5c1ffa1

Browse files
trentmry
authored andcommitted
repl completion: completion for arguments to "require"
1 parent 2134982 commit 5c1ffa1

File tree

1 file changed

+72
-1
lines changed

1 file changed

+72
-1
lines changed

lib/repl.js

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var sys = require('sys');
1616
var Script = process.binding('evals').Script;
1717
var evalcx = Script.runInContext;
1818
var path = require("path");
19+
var fs = require("fs");
1920
var rl = require('readline');
2021
var 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(/\brequire\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

Comments
 (0)