Skip to content

Commit

Permalink
repl: fix and extend require/import tab complete
Browse files Browse the repository at this point in the history
PR-URL: #40216
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
Mesteery authored and danielleadams committed Jan 30, 2022
1 parent d227ed1 commit 1d8a496
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 114 deletions.
214 changes: 106 additions & 108 deletions lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const { Console } = require('console');
const CJSModule = require('internal/modules/cjs/loader').Module;
let _builtinLibs = ArrayPrototypeFilter(
CJSModule.builtinModules,
(e) => !StringPrototypeStartsWith(e, '_') && !StringPrototypeIncludes(e, '/')
(e) => !StringPrototypeStartsWith(e, '_'),
);
const nodeSchemeBuiltinLibs = ArrayPrototypeMap(
_builtinLibs, (lib) => `node:${lib}`);
Expand Down Expand Up @@ -1285,135 +1285,133 @@ function complete(line, callback) {
if (completeOn.length) {
filter = completeOn;
}
} else if (RegExpPrototypeTest(requireRE, line) &&
this.allowBlockingCompletions) {
} else if (RegExpPrototypeTest(requireRE, line)) {
// require('...<Tab>')
const extensions = ObjectKeys(this.context.require.extensions);
const indexes = ArrayPrototypeMap(extensions,
(extension) => `index${extension}`);
ArrayPrototypePush(indexes, 'package.json', 'index');

const match = StringPrototypeMatch(line, requireRE);
completeOn = match[1];
const subdir = match[2] || '';
filter = completeOn;
group = [];
let paths = [];

if (completeOn === '.') {
group = ['./', '../'];
} else if (completeOn === '..') {
group = ['../'];
} else if (RegExpPrototypeTest(/^\.\.?\//, completeOn)) {
paths = [process.cwd()];
} else {
paths = ArrayPrototypeConcat(module.paths, CJSModule.globalPaths);
}
if (this.allowBlockingCompletions) {
const subdir = match[2] || '';
const extensions = ObjectKeys(this.context.require.extensions);
const indexes = ArrayPrototypeMap(extensions,
(extension) => `index${extension}`);
ArrayPrototypePush(indexes, 'package.json', 'index');

group = [];
let paths = [];

if (completeOn === '.') {
group = ['./', '../'];
} else if (completeOn === '..') {
group = ['../'];
} else if (RegExpPrototypeTest(/^\.\.?\//, completeOn)) {
paths = [process.cwd()];
} else {
paths = ArrayPrototypeConcat(module.paths, CJSModule.globalPaths);
}

ArrayPrototypeForEach(paths, (dir) => {
dir = path.resolve(dir, subdir);
const dirents = gracefulReaddir(dir, { withFileTypes: true }) || [];
ArrayPrototypeForEach(dirents, (dirent) => {
if (RegExpPrototypeTest(versionedFileNamesRe, dirent.name) ||
dirent.name === '.npm') {
// Exclude versioned names that 'npm' installs.
return;
}
const extension = path.extname(dirent.name);
const base = StringPrototypeSlice(dirent.name, 0, -extension.length);
if (!dirent.isDirectory()) {
if (StringPrototypeIncludes(extensions, extension) &&
(!subdir || base !== 'index')) {
ArrayPrototypePush(group, `${subdir}${base}`);
ArrayPrototypeForEach(paths, (dir) => {
dir = path.resolve(dir, subdir);
const dirents = gracefulReaddir(dir, { withFileTypes: true }) || [];
ArrayPrototypeForEach(dirents, (dirent) => {
if (RegExpPrototypeTest(versionedFileNamesRe, dirent.name) ||
dirent.name === '.npm') {
// Exclude versioned names that 'npm' installs.
return;
}
return;
}
ArrayPrototypePush(group, `${subdir}${dirent.name}/`);
const absolute = path.resolve(dir, dirent.name);
if (ArrayPrototypeSome(
gracefulReaddir(absolute) || [],
(subfile) => ArrayPrototypeIncludes(indexes, subfile)
)) {
ArrayPrototypePush(group, `${subdir}${dirent.name}`);
}
const extension = path.extname(dirent.name);
const base = StringPrototypeSlice(dirent.name, 0, -extension.length);
if (!dirent.isDirectory()) {
if (StringPrototypeIncludes(extensions, extension) &&
(!subdir || base !== 'index')) {
ArrayPrototypePush(group, `${subdir}${base}`);
}
return;
}
ArrayPrototypePush(group, `${subdir}${dirent.name}/`);
const absolute = path.resolve(dir, dirent.name);
if (ArrayPrototypeSome(
gracefulReaddir(absolute) || [],
(subfile) => ArrayPrototypeIncludes(indexes, subfile)
)) {
ArrayPrototypePush(group, `${subdir}${dirent.name}`);
}
});
});
});
if (group.length) {
ArrayPrototypePush(completionGroups, group);
if (group.length) {
ArrayPrototypePush(completionGroups, group);
}
}

if (!subdir) {
ArrayPrototypePush(completionGroups, _builtinLibs, nodeSchemeBuiltinLibs);
}
} else if (RegExpPrototypeTest(importRE, line) &&
this.allowBlockingCompletions) {
ArrayPrototypePush(completionGroups, _builtinLibs, nodeSchemeBuiltinLibs);
} else if (RegExpPrototypeTest(importRE, line)) {
// import('...<Tab>')
// File extensions that can be imported:
const extensions = ObjectKeys(
getOptionValue('--experimental-specifier-resolution') === 'node' ?
legacyExtensionFormatMap :
extensionFormatMap);

// Only used when loading bare module specifiers from `node_modules`:
const indexes = ArrayPrototypeMap(extensions, (ext) => `index${ext}`);
ArrayPrototypePush(indexes, 'package.json');

const match = StringPrototypeMatch(line, importRE);
completeOn = match[1];
const subdir = match[2] || '';
filter = completeOn;
group = [];
let paths = [];
if (completeOn === '.') {
group = ['./', '../'];
} else if (completeOn === '..') {
group = ['../'];
} else if (RegExpPrototypeTest(/^\.\.?\//, completeOn)) {
paths = [process.cwd()];
} else {
paths = ArrayPrototypeSlice(module.paths);
}
if (this.allowBlockingCompletions) {
const subdir = match[2] || '';
// File extensions that can be imported:
const extensions = ObjectKeys(
getOptionValue('--experimental-specifier-resolution') === 'node' ?
legacyExtensionFormatMap :
extensionFormatMap);

// Only used when loading bare module specifiers from `node_modules`:
const indexes = ArrayPrototypeMap(extensions, (ext) => `index${ext}`);
ArrayPrototypePush(indexes, 'package.json');

group = [];
let paths = [];
if (completeOn === '.') {
group = ['./', '../'];
} else if (completeOn === '..') {
group = ['../'];
} else if (RegExpPrototypeTest(/^\.\.?\//, completeOn)) {
paths = [process.cwd()];
} else {
paths = ArrayPrototypeSlice(module.paths);
}

ArrayPrototypeForEach(paths, (dir) => {
dir = path.resolve(dir, subdir);
const isInNodeModules = path.basename(dir) === 'node_modules';
const dirents = gracefulReaddir(dir, { withFileTypes: true }) || [];
ArrayPrototypeForEach(dirents, (dirent) => {
const { name } = dirent;
if (RegExpPrototypeTest(versionedFileNamesRe, name) ||
name === '.npm') {
// Exclude versioned names that 'npm' installs.
return;
}
ArrayPrototypeForEach(paths, (dir) => {
dir = path.resolve(dir, subdir);
const isInNodeModules = path.basename(dir) === 'node_modules';
const dirents = gracefulReaddir(dir, { withFileTypes: true }) || [];
ArrayPrototypeForEach(dirents, (dirent) => {
const { name } = dirent;
if (RegExpPrototypeTest(versionedFileNamesRe, name) ||
name === '.npm') {
// Exclude versioned names that 'npm' installs.
return;
}

if (!dirent.isDirectory()) {
const extension = path.extname(name);
if (StringPrototypeIncludes(extensions, extension)) {
ArrayPrototypePush(group, `${subdir}${name}`);
if (!dirent.isDirectory()) {
const extension = path.extname(name);
if (StringPrototypeIncludes(extensions, extension)) {
ArrayPrototypePush(group, `${subdir}${name}`);
}
return;
}
return;
}

ArrayPrototypePush(group, `${subdir}${name}/`);
if (!subdir && isInNodeModules) {
const absolute = path.resolve(dir, name);
const subfiles = gracefulReaddir(absolute) || [];
if (ArrayPrototypeSome(subfiles, (subfile) => {
return ArrayPrototypeIncludes(indexes, subfile);
})) {
ArrayPrototypePush(group, `${subdir}${name}`);
ArrayPrototypePush(group, `${subdir}${name}/`);
if (!subdir && isInNodeModules) {
const absolute = path.resolve(dir, name);
const subfiles = gracefulReaddir(absolute) || [];
if (ArrayPrototypeSome(subfiles, (subfile) => {
return ArrayPrototypeIncludes(indexes, subfile);
})) {
ArrayPrototypePush(group, `${subdir}${name}`);
}
}
}
});
});
});

if (group.length) {
ArrayPrototypePush(completionGroups, group);
if (group.length) {
ArrayPrototypePush(completionGroups, group);
}
}

if (!subdir) {
ArrayPrototypePush(completionGroups, _builtinLibs, nodeSchemeBuiltinLibs);
}
ArrayPrototypePush(completionGroups, _builtinLibs, nodeSchemeBuiltinLibs);
} else if (RegExpPrototypeTest(fsAutoCompleteRE, line) &&
this.allowBlockingCompletions) {
({ 0: completionGroups, 1: completeOn } = completeFSFunctions(line));
Expand Down
4 changes: 1 addition & 3 deletions test/parallel/test-repl-tab-complete-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ const ArrayStream = require('../common/arraystream');
const fixtures = require('../common/fixtures');
const assert = require('assert');
const { builtinModules } = require('module');
const publicModules = builtinModules.filter(
(lib) => !lib.startsWith('_') && !lib.includes('/'),
);
const publicModules = builtinModules.filter((lib) => !lib.startsWith('_'));

if (!common.isMainThread)
common.skip('process.chdir is not available in Workers');
Expand Down
4 changes: 1 addition & 3 deletions test/parallel/test-repl-tab-complete.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ const assert = require('assert');
const path = require('path');
const fixtures = require('../common/fixtures');
const { builtinModules } = require('module');
const publicModules = builtinModules.filter(
(lib) => !lib.startsWith('_') && !lib.includes('/'),
);
const publicModules = builtinModules.filter((lib) => !lib.startsWith('_'));

const hasInspector = process.features.inspector;

Expand Down

0 comments on commit 1d8a496

Please sign in to comment.