Skip to content

Commit 65d40f8

Browse files
authored
Merge pull request microsoft#10888 from Microsoft/safe_import_completions
Safe import completions
2 parents 7e33955 + 6ea5c22 commit 65d40f8

1 file changed

Lines changed: 83 additions & 35 deletions

File tree

src/services/completions.ts

Lines changed: 83 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,11 @@ namespace ts.Completions {
335335
const baseDirectory = getDirectoryPath(absolutePath);
336336
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
337337

338-
if (directoryProbablyExists(baseDirectory, host)) {
339-
if (host.readDirectory) {
340-
// Enumerate the available files if possible
341-
const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]);
338+
if (tryDirectoryExists(host, baseDirectory)) {
339+
// Enumerate the available files if possible
340+
const files = tryReadDirectory(host, baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]);
341+
342+
if (files) {
342343
const foundFiles = createMap<boolean>();
343344
for (let filePath of files) {
344345
filePath = normalizePath(filePath);
@@ -359,8 +360,9 @@ namespace ts.Completions {
359360
}
360361

361362
// If possible, get folder completion as well
362-
if (host.getDirectories) {
363-
const directories = host.getDirectories(baseDirectory);
363+
const directories = tryGetDirectories(host, baseDirectory);
364+
365+
if (directories) {
364366
for (const directory of directories) {
365367
const directoryName = getBaseFileName(normalizePath(directory));
366368

@@ -449,22 +451,24 @@ namespace ts.Completions {
449451
// doesn't support. For now, this is safer but slower
450452
const includeGlob = normalizedSuffix ? "**/*" : "./*";
451453

452-
const matches = host.readDirectory(baseDirectory, fileExtensions, undefined, [includeGlob]);
453-
const result: string[] = [];
454+
const matches = tryReadDirectory(host, baseDirectory, fileExtensions, undefined, [includeGlob]);
455+
if (matches) {
456+
const result: string[] = [];
454457

455-
// Trim away prefix and suffix
456-
for (const match of matches) {
457-
const normalizedMatch = normalizePath(match);
458-
if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) {
459-
continue;
460-
}
458+
// Trim away prefix and suffix
459+
for (const match of matches) {
460+
const normalizedMatch = normalizePath(match);
461+
if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) {
462+
continue;
463+
}
461464

462-
const start = completePrefix.length;
463-
const length = normalizedMatch.length - start - normalizedSuffix.length;
465+
const start = completePrefix.length;
466+
const length = normalizedMatch.length - start - normalizedSuffix.length;
464467

465-
result.push(removeFileExtension(normalizedMatch.substr(start, length)));
468+
result.push(removeFileExtension(normalizedMatch.substr(start, length)));
469+
}
470+
return result;
466471
}
467-
return result;
468472
}
469473
}
470474

@@ -499,13 +503,14 @@ namespace ts.Completions {
499503
if (!isNestedModule) {
500504
nonRelativeModules.push(visibleModule.moduleName);
501505
}
502-
else if (host.readDirectory && startsWith(visibleModule.moduleName, moduleNameFragment)) {
503-
const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]);
504-
505-
for (let f of nestedFiles) {
506-
f = normalizePath(f);
507-
const nestedModule = removeFileExtension(getBaseFileName(f));
508-
nonRelativeModules.push(nestedModule);
506+
else if (startsWith(visibleModule.moduleName, moduleNameFragment)) {
507+
const nestedFiles = tryReadDirectory(host, visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]);
508+
if (nestedFiles) {
509+
for (let f of nestedFiles) {
510+
f = normalizePath(f);
511+
const nestedModule = removeFileExtension(getBaseFileName(f));
512+
nonRelativeModules.push(nestedModule);
513+
}
509514
}
510515
}
511516
}
@@ -570,9 +575,17 @@ namespace ts.Completions {
570575
}
571576
}
572577
else if (host.getDirectories) {
573-
const typeRoots = getEffectiveTypeRoots(options, host);
574-
for (const root of typeRoots) {
575-
getCompletionEntriesFromDirectories(host, options, root, span, result);
578+
let typeRoots: string[];
579+
try {
580+
// Wrap in try catch because getEffectiveTypeRoots touches the filesystem
581+
typeRoots = getEffectiveTypeRoots(options, host);
582+
}
583+
catch (e) {}
584+
585+
if (typeRoots) {
586+
for (const root of typeRoots) {
587+
getCompletionEntriesFromDirectories(host, options, root, span, result);
588+
}
576589
}
577590
}
578591

@@ -588,10 +601,13 @@ namespace ts.Completions {
588601
}
589602

590603
function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, span: TextSpan, result: CompletionEntry[]) {
591-
if (host.getDirectories && directoryProbablyExists(directory, host)) {
592-
for (let typeDirectory of host.getDirectories(directory)) {
593-
typeDirectory = normalizePath(typeDirectory);
594-
result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span));
604+
if (host.getDirectories && tryDirectoryExists(host, directory)) {
605+
const directories = tryGetDirectories(host, directory);
606+
if (directories) {
607+
for (let typeDirectory of directories) {
608+
typeDirectory = normalizePath(typeDirectory);
609+
result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span));
610+
}
595611
}
596612
}
597613
}
@@ -600,7 +616,7 @@ namespace ts.Completions {
600616
const paths: string[] = [];
601617
let currentConfigPath: string;
602618
while (true) {
603-
currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json");
619+
currentConfigPath = findConfigFile(currentDir, (f) => tryFileExists(host, f), "package.json");
604620
if (currentConfigPath) {
605621
paths.push(currentConfigPath);
606622

@@ -652,8 +668,8 @@ namespace ts.Completions {
652668

653669
function tryReadingPackageJson(filePath: string) {
654670
try {
655-
const fileText = host.readFile(filePath);
656-
return JSON.parse(fileText);
671+
const fileText = tryReadFile(host, filePath);
672+
return fileText ? JSON.parse(fileText) : undefined;
657673
}
658674
catch (e) {
659675
return undefined;
@@ -1660,4 +1676,36 @@ namespace ts.Completions {
16601676
}
16611677

16621678
const nodeModulesDependencyKeys = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
1679+
1680+
function tryGetDirectories(host: LanguageServiceHost, directoryName: string): string[] {
1681+
return tryIOAndConsumeErrors(host, host.getDirectories, directoryName);
1682+
}
1683+
1684+
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
1685+
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include);
1686+
}
1687+
1688+
function tryReadFile(host: LanguageServiceHost, path: string): string {
1689+
return tryIOAndConsumeErrors(host, host.readFile, path);
1690+
}
1691+
1692+
function tryFileExists(host: LanguageServiceHost, path: string): boolean {
1693+
return tryIOAndConsumeErrors(host, host.fileExists, path);
1694+
}
1695+
1696+
function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean {
1697+
try {
1698+
return directoryProbablyExists(path, host);
1699+
}
1700+
catch (e) {}
1701+
return undefined;
1702+
}
1703+
1704+
function tryIOAndConsumeErrors<T>(host: LanguageServiceHost, toApply: (...a: any[]) => T, ...args: any[]) {
1705+
try {
1706+
return toApply && toApply.apply(host, args);
1707+
}
1708+
catch (e) {}
1709+
return undefined;
1710+
}
16631711
}

0 commit comments

Comments
 (0)