From b5df1c5113e018d01360cbd35de8586c485d503a Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Tue, 25 May 2021 12:46:55 -0700 Subject: [PATCH 01/11] Fix discovery of more pnpm symlinks --- src/compiler/checker.ts | 1 + src/compiler/moduleSpecifiers.ts | 8 +++++--- src/compiler/program.ts | 7 +++---- src/compiler/types.ts | 5 +++-- src/compiler/utilities.ts | 19 ++++++++++--------- src/server/project.ts | 14 +++++++++----- src/services/types.ts | 2 +- src/services/utilities.ts | 1 + 8 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f784c44beb3c..1e24daf252b14 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4485,6 +4485,7 @@ namespace ts { getSourceFiles: () => host.getSourceFiles(), getCurrentDirectory: () => host.getCurrentDirectory(), getSymlinkCache: maybeBind(host, host.getSymlinkCache), + getResolvedTypeReferenceDirectives: maybeBind(host, host.getResolvedTypeReferenceDirectives), useCaseSensitiveFileNames: maybeBind(host, host.useCaseSensitiveFileNames), redirectTargetsMap: host.redirectTargetsMap, getProjectReferenceRedirect: fileName => host.getProjectReferenceRedirect(fileName), diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 45b31154344c8..aaa62034ffe20 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -294,10 +294,12 @@ namespace ts.moduleSpecifiers { const result = forEach(targets, p => !(shouldFilterIgnoredPaths && containsIgnoredPath(p)) && cb(p, referenceRedirect === p)); if (result) return result; } - const links = host.getSymlinkCache - ? host.getSymlinkCache() - : discoverProbableSymlinks(host.getSourceFiles(), getCanonicalFileName, cwd); + const links = host.getSymlinkCache?.() || discoverProbableSymlinks( + host.getSourceFiles(), + host.getResolvedTypeReferenceDirectives ? arrayFrom(host.getResolvedTypeReferenceDirectives?.().values()) : emptyArray, + getCanonicalFileName, + cwd); const symlinkedDirectories = links.getSymlinkedDirectoriesByRealpath(); const fullImportedFileName = getNormalizedAbsolutePath(importedFileName, cwd); const result = symlinkedDirectories && forEachAncestorDirectory(getDirectoryPath(fullImportedFileName), realPathDirectory => { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 102517c4ce6fd..4dff0ffd8eed7 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1650,6 +1650,7 @@ namespace ts { getLibFileFromReference: program.getLibFileFromReference, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect, + getResolvedTypeReferenceDirectives: program.getResolvedTypeReferenceDirectives, getProjectReferenceRedirect, isSourceOfProjectReferenceRedirect, getSymlinkCache, @@ -3660,11 +3661,9 @@ namespace ts { } function getSymlinkCache(): SymlinkCache { - if (host.getSymlinkCache) { - return host.getSymlinkCache(); - } - return symlinks || (symlinks = discoverProbableSymlinks( + return host.getSymlinkCache?.() || (symlinks ||= discoverProbableSymlinks( files, + arrayFrom(resolvedTypeReferenceDirectives.values()), getCanonicalFileName, host.getCurrentDirectory())); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f395014baad1d..f11e2724b9df6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6559,7 +6559,7 @@ namespace ts { // TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesn't use compilerHost as base /*@internal*/createDirectory?(directory: string): void; - /*@internal*/getSymlinkCache?(): SymlinkCache; + /*@internal*/getSymlinkCache?(): SymlinkCache | undefined; // For testing: /*@internal*/ disableUseFileVersionAsSignature?: boolean; @@ -8132,7 +8132,8 @@ namespace ts { directoryExists?(path: string): boolean; readFile?(path: string): string | undefined; realpath?(path: string): string; - getSymlinkCache?(): SymlinkCache; + getSymlinkCache?(): SymlinkCache | undefined; + getResolvedTypeReferenceDirectives?(): ReadonlyESMap<string, ResolvedTypeReferenceDirective | undefined>; getModuleSpecifierCache?(): ModuleSpecifierCache; getGlobalTypingsCacheLocation?(): string | undefined; getNearestAncestorDirectoryWithPackageJson?(fileName: string, rootDir?: string): string | undefined; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8271e6adfc22f..dd497f68a6353 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6227,18 +6227,19 @@ namespace ts { }; } - export function discoverProbableSymlinks(files: readonly SourceFile[], getCanonicalFileName: GetCanonicalFileName, cwd: string): SymlinkCache { + export function discoverProbableSymlinks(files: readonly SourceFile[], typeReferenceDirectives: readonly (ResolvedTypeReferenceDirective | undefined)[], getCanonicalFileName: GetCanonicalFileName, cwd: string): SymlinkCache { const cache = createSymlinkCache(cwd, getCanonicalFileName); - const symlinks = flatMap(files, sf => { - const pairs = sf.resolvedModules && arrayFrom(mapDefinedIterator(sf.resolvedModules.values(), res => - res?.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined)); - return concatenate(pairs, sf.resolvedTypeReferenceDirectiveNames && arrayFrom(mapDefinedIterator(sf.resolvedTypeReferenceDirectiveNames.values(), res => - res?.originalPath && res.resolvedFileName ? [res.resolvedFileName, res.originalPath] as const : undefined))); + const symlinksFromFiles: readonly (ResolvedModuleFull | ResolvedTypeReferenceDirective | undefined)[] = flatMap(files, sf => { + return sf.resolvedModules && arrayFrom(mapDefinedIterator(sf.resolvedModules.values(), res => + res?.originalPath ? res : undefined)); }); - for (const [resolvedPath, originalPath] of symlinks) { - cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedPath); - const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedPath, originalPath, cwd, getCanonicalFileName) || emptyArray; + const resolutions = symlinksFromFiles.concat(typeReferenceDirectives); + for (const resolution of resolutions) { + if (!resolution || !resolution.originalPath || !resolution.resolvedFileName) continue; + const { resolvedFileName, originalPath } = resolution; + cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedFileName); + const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedFileName, originalPath, cwd, getCanonicalFileName) || emptyArray; if (commonResolved && commonOriginal) { cache.setSymlinkedDirectory( commonOriginal, diff --git a/src/server/project.ts b/src/server/project.ts index ef48ebf530bc2..6457c0ec75283 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -351,10 +351,14 @@ namespace ts.server { return this.projectService.typingsCache; } - /*@internal*/ - getSymlinkCache(): SymlinkCache { - return this.symlinks || (this.symlinks = discoverProbableSymlinks( - this.program?.getSourceFiles() || emptyArray, + /** + * @internal + * Returns undefined prior to program creation. + */ + getSymlinkCache(): SymlinkCache | undefined { + return this.symlinks || this.program && (this.symlinks = discoverProbableSymlinks( + this.program.getSourceFiles() || emptyArray, + arrayFrom(this.program.getResolvedTypeReferenceDirectives().values()), this.getCanonicalFileName, this.getCurrentDirectory())); } @@ -1902,7 +1906,7 @@ namespace ts.server { moduleResolutionHost)); const program = hostProject.getCurrentProgram()!; - const symlinkCache = hostProject.getSymlinkCache(); + const symlinkCache = hostProject.getSymlinkCache()!; // Guaranteed to be set when program is set for (const resolution of resolutions) { if (!resolution.resolvedTypeReferenceDirective?.resolvedFileName) continue; const { resolvedFileName, originalPath } = resolution.resolvedTypeReferenceDirective; diff --git a/src/services/types.ts b/src/services/types.ts index be1d09ff69c57..75c696d35137a 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -277,7 +277,7 @@ namespace ts { /* @internal */ getGlobalTypingsCacheLocation?(): string | undefined; /* @internal */ - getSymlinkCache?(files?: readonly SourceFile[]): SymlinkCache; + getSymlinkCache?(files?: readonly SourceFile[]): SymlinkCache | undefined; /* * Required for full import and type reference completions. diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 6ffa3607c8ccb..a2431e7fe3411 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1847,6 +1847,7 @@ namespace ts { getGlobalTypingsCacheLocation: maybeBind(host, host.getGlobalTypingsCacheLocation), getSourceFiles: () => program.getSourceFiles(), redirectTargetsMap: program.redirectTargetsMap, + getResolvedTypeReferenceDirectives: program.getResolvedTypeReferenceDirectives, getProjectReferenceRedirect: fileName => program.getProjectReferenceRedirect(fileName), isSourceOfProjectReferenceRedirect: fileName => program.isSourceOfProjectReferenceRedirect(fileName), getNearestAncestorDirectoryWithPackageJson: maybeBind(host, host.getNearestAncestorDirectoryWithPackageJson), From 7c72d49ec6cb39b39cbdd07f876c080c3b360957 Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Tue, 25 May 2021 15:31:26 -0700 Subject: [PATCH 02/11] Add some tests --- src/harness/client.ts | 10 +++---- tests/cases/fourslash/fourslash.ts | 2 ++ .../server/importNameCodeFix_pnpm1.ts | 15 ++++++++++ .../importStatementCompletions_pnpm1.ts | 30 +++++++++++++++++++ 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/server/importNameCodeFix_pnpm1.ts create mode 100644 tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts diff --git a/src/harness/client.ts b/src/harness/client.ts index 97e05c563b914..c7d432a0c1dcb 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -196,18 +196,16 @@ namespace ts.server { // Not passing along 'preferences' because server should already have those from the 'configure' command const args: protocol.CompletionsRequestArgs = this.createFileLocationRequestArgs(fileName, position); - const request = this.processRequest<protocol.CompletionsRequest>(CommandNames.Completions, args); - const response = this.processResponse<protocol.CompletionsResponse>(request); + const request = this.processRequest<protocol.CompletionsRequest>(CommandNames.CompletionInfo, args); + const response = this.processResponse<protocol.CompletionInfoResponse>(request); return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, - entries: response.body!.map<CompletionEntry>(entry => { // TODO: GH#18217 + entries: response.body!.entries.map<CompletionEntry>(entry => { // TODO: GH#18217 if (entry.replacementSpan !== undefined) { - const { name, kind, kindModifiers, sortText, replacementSpan, hasAction, source, data, isRecommended } = entry; - // TODO: GH#241 - const res: CompletionEntry = { name, kind, kindModifiers, sortText, replacementSpan: this.decodeSpan(replacementSpan, fileName), hasAction, source, data: data as any, isRecommended }; + const res: CompletionEntry = { ...entry, data: entry.data as any, replacementSpan: this.decodeSpan(entry.replacementSpan, fileName) }; return res; } diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 2ceb0eb0897ab..26c3c220b7243 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -625,6 +625,8 @@ declare namespace FourSlashInterface { readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsForImportStatements?: boolean; readonly includeCompletionsWithSnippetText?: boolean; + readonly includeCompletionsWithInsertText?: boolean; + /** @deprecated use `includeCompletionsWithInsertText` */ readonly includeInsertTextCompletions?: boolean; readonly includeAutomaticOptionalChainCompletions?: boolean; readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative"; diff --git a/tests/cases/fourslash/server/importNameCodeFix_pnpm1.ts b/tests/cases/fourslash/server/importNameCodeFix_pnpm1.ts new file mode 100644 index 0000000000000..61eb984c7ced5 --- /dev/null +++ b/tests/cases/fourslash/server/importNameCodeFix_pnpm1.ts @@ -0,0 +1,15 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /project/tsconfig.json +//// { "compilerOptions": { "module": "commonjs" } } + +// @Filename: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react/index.d.ts +//// export declare function Component(): void; + +// @Filename: /project/index.ts +//// Component/**/ + +// @link: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react -> /project/node_modules/@types/react + +goTo.marker(""); +verify.importFixAtPosition([`import { Component } from "react";\r\n\r\nComponent`]); diff --git a/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts b/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts new file mode 100644 index 0000000000000..b57b846805dcd --- /dev/null +++ b/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts @@ -0,0 +1,30 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /project/tsconfig.json +//// { "compilerOptions": { "module": "commonjs" } } + +// @Filename: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react/index.d.ts +//// export declare function Component(): void; + +// @Filename: /project/index.ts +//// [|import Com/**/|] + +// @link: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react -> /project/node_modules/@types/react + +goTo.marker(""); +verify.completions({ + marker: "", + exact: [{ + name: "Component", + source: "react", + insertText: `import { Component$1 } from "react";`, + isSnippet: true, + replacementSpan: test.ranges()[0], + sourceDisplay: "react", + }], + preferences: { + includeCompletionsForImportStatements: true, + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: true, + } +}); From 16e40e2c55bc37176d0d94c8ecad0f2312175e7a Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Tue, 25 May 2021 16:32:05 -0700 Subject: [PATCH 03/11] =?UTF-8?q?Never=20show=20pnpm=20paths=20in=20auto?= =?UTF-8?q?=20imports,=20even=20if=20there=E2=80=99s=20no=20other=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/compiler/moduleSpecifiers.ts | 13 +++++++-- src/compiler/types.ts | 1 + src/services/codefixes/importFixes.ts | 11 +++++--- src/services/completions.ts | 2 +- src/services/utilities.ts | 1 + .../importStatementCompletions_pnpm2.ts | 28 +++++++++++++++++++ 6 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index aaa62034ffe20..bb9d04333e58a 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -163,7 +163,16 @@ namespace ts.moduleSpecifiers { return pathsSpecifiers?.length ? pathsSpecifiers : nodeModulesSpecifiers?.length ? nodeModulesSpecifiers : - Debug.checkDefined(relativeSpecifiers); + fallback(); + + // Unless 'preferFailureToResultsWithIgnoredPaths' is set, we *must* generate a relative path fallback. + // (The declaration emitter must write something, but auto-imports may exclude bad suggestions.) + function fallback() { + if (host.preferFailureToResultsWithIgnoredPaths?.()) { + return relativeSpecifiers || emptyArray; + } + return Debug.checkDefined(relativeSpecifiers); + } } interface Info { @@ -286,7 +295,7 @@ namespace ts.moduleSpecifiers { const redirects = host.redirectTargetsMap.get(importedPath) || emptyArray; const importedFileNames = [...(referenceRedirect ? [referenceRedirect] : emptyArray), importedFileName, ...redirects]; const targets = importedFileNames.map(f => getNormalizedAbsolutePath(f, cwd)); - let shouldFilterIgnoredPaths = !every(targets, containsIgnoredPath); + let shouldFilterIgnoredPaths = host.preferFailureToResultsWithIgnoredPaths?.() || !every(targets, containsIgnoredPath); if (!preferSymlinks) { // Symlinks inside ignored paths are already filtered out of the symlink cache, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f11e2724b9df6..daf7ccdc90dac 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -8137,6 +8137,7 @@ namespace ts { getModuleSpecifierCache?(): ModuleSpecifierCache; getGlobalTypingsCacheLocation?(): string | undefined; getNearestAncestorDirectoryWithPackageJson?(fileName: string, rootDir?: string): string | undefined; + preferFailureToResultsWithIgnoredPaths?(): boolean; getSourceFiles(): readonly SourceFile[]; readonly redirectTargetsMap: RedirectTargetsMap; diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 061aeaaca7733..a1d5692324ee8 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -66,7 +66,9 @@ namespace ts.codefix { const preferTypeOnlyImport = !!usageIsTypeOnly && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error; const useRequire = shouldUseRequire(sourceFile, program); const fix = getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, /*position*/ undefined, preferTypeOnlyImport, useRequire, host, preferences); - addImport({ fixes: [fix], symbolName }); + if (fix) { + addImport({ fixes: [fix], symbolName }); + } } function addImport(info: FixesInfo) { @@ -203,7 +205,7 @@ namespace ts.codefix { : getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, /*useAutoImportProvider*/ true); const useRequire = shouldUseRequire(sourceFile, program); const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && !isSourceFileJS(sourceFile) && isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position)); - const fix = getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, position, preferTypeOnlyImport, useRequire, host, preferences); + const fix = Debug.checkDefined(getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, position, preferTypeOnlyImport, useRequire, host, preferences)); return { moduleSpecifier: fix.moduleSpecifier, codeAction: codeFixActionToCodeAction(codeActionForFix({ host, formatContext, preferences }, sourceFile, symbolName, fix, getQuotePreference(sourceFile, preferences))) }; } @@ -274,7 +276,7 @@ namespace ts.codefix { program: Program, host: LanguageServiceHost, preferences: UserPreferences - ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string } { + ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string } | undefined { return getBestFix(getNewImportFixes(program, importingFile, /*position*/ undefined, /*preferTypeOnlyImport*/ false, /*useRequire*/ false, exportInfo, host, preferences), importingFile, host); } @@ -529,7 +531,8 @@ namespace ts.codefix { return sort(fixes, (a, b) => compareValues(a.kind, b.kind) || compareModuleSpecifiers(a, b, allowsImportingSpecifier)); } - function getBestFix<T extends ImportFix>(fixes: readonly T[], sourceFile: SourceFile, host: LanguageServiceHost): T { + function getBestFix<T extends ImportFix>(fixes: readonly T[], sourceFile: SourceFile, host: LanguageServiceHost): T | undefined { + if (!some(fixes)) return; // These will always be placed first if available, and are better than other kinds if (fixes[0].kind === ImportFixKind.UseNamespace || fixes[0].kind === ImportFixKind.AddToExisting) { return fixes[0]; diff --git a/src/services/completions.ts b/src/services/completions.ts index e97753908f7c4..0ce98c3f0d26a 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1762,7 +1762,7 @@ namespace ts.Completions { // If we don't need to resolve module specifiers, we can use any re-export that is importable at all // (We need to ensure that at least one is importable to show a completion.) const { moduleSpecifier, exportInfo } = resolveModuleSpecifiers - ? codefix.getModuleSpecifierForBestExportInfo(info, sourceFile, program, host, preferences) + ? codefix.getModuleSpecifierForBestExportInfo(info, sourceFile, program, host, preferences) || {} : { moduleSpecifier: undefined, exportInfo: find(info, isImportableExportInfo) }; if (!exportInfo) return; const moduleFile = tryCast(exportInfo.moduleSymbol.valueDeclaration, isSourceFile); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index a2431e7fe3411..0f9dd7f202ff8 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1852,6 +1852,7 @@ namespace ts { isSourceOfProjectReferenceRedirect: fileName => program.isSourceOfProjectReferenceRedirect(fileName), getNearestAncestorDirectoryWithPackageJson: maybeBind(host, host.getNearestAncestorDirectoryWithPackageJson), getFileIncludeReasons: () => program.getFileIncludeReasons(), + preferFailureToResultsWithIgnoredPaths: returnTrue, }; } diff --git a/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts b/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts new file mode 100644 index 0000000000000..3b4939b470da9 --- /dev/null +++ b/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts @@ -0,0 +1,28 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /project/tsconfig.json +//// { "compilerOptions": { "module": "commonjs" } } + +// @Filename: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react/index.d.ts +//// import "csstype"; +//// export declare function Component(): void; + +// @Filename: /project/node_modules/.pnpm/csstype@3.0.8/node_modules/csstype/index.d.ts +//// export interface SvgProperties {} + +// @Filename: /project/index.ts +//// [|import SvgProp/**/|] + +// @link: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react -> /project/node_modules/@types/react +// @link: /project/node_modules/.pnpm/csstype@3.0.8/node_modules/csstype -> /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/csstype + +goTo.marker(""); +verify.completions({ + marker: "", + exact: [], + preferences: { + includeCompletionsForImportStatements: true, + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: true, + } +}); From 3794e048369cc0e7087922ae2775b077d684be9a Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Tue, 25 May 2021 17:21:41 -0700 Subject: [PATCH 04/11] Import statement completions can return none --- src/services/completions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/completions.ts b/src/services/completions.ts index 0ce98c3f0d26a..fb1fb0fcfad52 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1594,6 +1594,7 @@ namespace ts.Completions { function tryGetImportCompletionSymbols(): GlobalsSearch { if (!importCompletionNode) return GlobalsSearch.Continue; + isNewIdentifierLocation = true; collectAutoImports(/*resolveModuleSpecifiers*/ true); return GlobalsSearch.Success; } From a522672aee395e538c851e1332ed5500980c7b81 Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Wed, 26 May 2021 08:53:35 -0700 Subject: [PATCH 05/11] Fix tests --- src/harness/client.ts | 6 +++--- tests/cases/fourslash/importStatementCompletions1.ts | 2 ++ .../importStatementCompletions_esModuleInterop1.ts | 1 + .../importStatementCompletions_esModuleInterop2.ts | 1 + .../importStatementCompletions_noPatternAmbient.ts | 1 + .../cases/fourslash/importStatementCompletions_noSnippet.ts | 1 + tests/cases/fourslash/importStatementCompletions_quotes.ts | 1 + .../fourslash/importStatementCompletions_semicolons.ts | 1 + .../fourslash/server/importStatementCompletions_pnpm1.ts | 1 + .../fourslash/server/importStatementCompletions_pnpm2.ts | 1 + 10 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/harness/client.ts b/src/harness/client.ts index c7d432a0c1dcb..46c90b94b3c81 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -200,9 +200,9 @@ namespace ts.server { const response = this.processResponse<protocol.CompletionInfoResponse>(request); return { - isGlobalCompletion: false, - isMemberCompletion: false, - isNewIdentifierLocation: false, + isGlobalCompletion: response.body!.isGlobalCompletion, + isMemberCompletion: response.body!.isMemberCompletion, + isNewIdentifierLocation: response.body!.isNewIdentifierLocation, entries: response.body!.entries.map<CompletionEntry>(entry => { // TODO: GH#18217 if (entry.replacementSpan !== undefined) { const res: CompletionEntry = { ...entry, data: entry.data as any, replacementSpan: this.decodeSpan(entry.replacementSpan, fileName) }; diff --git a/tests/cases/fourslash/importStatementCompletions1.ts b/tests/cases/fourslash/importStatementCompletions1.ts index c28aa81c7922b..6e08d5d3d46b9 100644 --- a/tests/cases/fourslash/importStatementCompletions1.ts +++ b/tests/cases/fourslash/importStatementCompletions1.ts @@ -23,6 +23,7 @@ [0, 1, 2, 3, 4, 5].forEach(marker => { verify.completions({ + isNewIdentifierLocation: true, marker: "" + marker, exact: [{ name: "foo", @@ -65,6 +66,7 @@ [6, 7, 8, 9, 10, 11, 12].forEach(marker => { verify.completions({ + isNewIdentifierLocation: true, marker: "" + marker, exact: [], preferences: { diff --git a/tests/cases/fourslash/importStatementCompletions_esModuleInterop1.ts b/tests/cases/fourslash/importStatementCompletions_esModuleInterop1.ts index a6d2083489b89..4d41ee5702119 100644 --- a/tests/cases/fourslash/importStatementCompletions_esModuleInterop1.ts +++ b/tests/cases/fourslash/importStatementCompletions_esModuleInterop1.ts @@ -10,6 +10,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/importStatementCompletions_esModuleInterop2.ts b/tests/cases/fourslash/importStatementCompletions_esModuleInterop2.ts index c6a5b8e25e1f6..6d0d6ecd4dbf1 100644 --- a/tests/cases/fourslash/importStatementCompletions_esModuleInterop2.ts +++ b/tests/cases/fourslash/importStatementCompletions_esModuleInterop2.ts @@ -10,6 +10,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/importStatementCompletions_noPatternAmbient.ts b/tests/cases/fourslash/importStatementCompletions_noPatternAmbient.ts index 78bad2241b0c8..f92eecec4937c 100644 --- a/tests/cases/fourslash/importStatementCompletions_noPatternAmbient.ts +++ b/tests/cases/fourslash/importStatementCompletions_noPatternAmbient.ts @@ -10,6 +10,7 @@ //// import style/**/ verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [], preferences: { diff --git a/tests/cases/fourslash/importStatementCompletions_noSnippet.ts b/tests/cases/fourslash/importStatementCompletions_noSnippet.ts index 8f87dba53bb39..7545caf97a537 100644 --- a/tests/cases/fourslash/importStatementCompletions_noSnippet.ts +++ b/tests/cases/fourslash/importStatementCompletions_noSnippet.ts @@ -7,6 +7,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/importStatementCompletions_quotes.ts b/tests/cases/fourslash/importStatementCompletions_quotes.ts index 845dd0a205caf..e39b96a530266 100644 --- a/tests/cases/fourslash/importStatementCompletions_quotes.ts +++ b/tests/cases/fourslash/importStatementCompletions_quotes.ts @@ -8,6 +8,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/importStatementCompletions_semicolons.ts b/tests/cases/fourslash/importStatementCompletions_semicolons.ts index fb61be82d790a..6674caee82965 100644 --- a/tests/cases/fourslash/importStatementCompletions_semicolons.ts +++ b/tests/cases/fourslash/importStatementCompletions_semicolons.ts @@ -8,6 +8,7 @@ //// [|import f/**/|] verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "foo", diff --git a/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts b/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts index b57b846805dcd..9e6d16ced2eab 100644 --- a/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts +++ b/tests/cases/fourslash/server/importStatementCompletions_pnpm1.ts @@ -13,6 +13,7 @@ goTo.marker(""); verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [{ name: "Component", diff --git a/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts b/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts index 3b4939b470da9..5649c2447686b 100644 --- a/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts +++ b/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts @@ -18,6 +18,7 @@ goTo.marker(""); verify.completions({ + isNewIdentifierLocation: true, marker: "", exact: [], preferences: { From e066ba4a826158b7693dda1d36f6843f141e29a1 Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Thu, 27 May 2021 12:31:33 -0700 Subject: [PATCH 06/11] Add failing test showing poor symlink cache reuse --- src/testRunner/tsconfig.json | 1 + .../unittests/tsserver/symlinkCache.ts | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/testRunner/unittests/tsserver/symlinkCache.ts diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 439b447d6396f..b93ec82423462 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -206,6 +206,7 @@ "unittests/tsserver/session.ts", "unittests/tsserver/skipLibCheck.ts", "unittests/tsserver/smartSelection.ts", + "unittests/tsserver/symlinkCache.ts", "unittests/tsserver/symLinks.ts", "unittests/tsserver/syntacticServer.ts", "unittests/tsserver/syntaxOperations.ts", diff --git a/src/testRunner/unittests/tsserver/symlinkCache.ts b/src/testRunner/unittests/tsserver/symlinkCache.ts new file mode 100644 index 0000000000000..80d996eee4ff2 --- /dev/null +++ b/src/testRunner/unittests/tsserver/symlinkCache.ts @@ -0,0 +1,81 @@ +namespace ts.projectSystem { + const appTsconfigJson: File = { + path: "/packages/app/tsconfig.json", + content: ` + { + "compilerOptions": { + "module": "commonjs", + "outDir": "dist", + "rootDir": "src", + "baseUrl": "." + } + "references": [{ "path": "../dep" }] + }` + }; + + const appSrcIndexTs: File = { + path: "/packages/app/src/index.ts", + content: `import "dep";` + }; + + const depPackageJson: File = { + path: "/packages/dep/package.json", + content: `{ "name": "dep", "main": "dist/index.js", "types": "dist/index.d.ts" }` + }; + + const depTsconfigJson: File = { + path: "/packages/dep/tsconfig.json", + content: ` + { + "compilerOptions": { "outDir": "dist", "rootDir": "src", "module": "commonjs" } + }` + }; + + const depSrcIndexTs: File = { + path: "/packages/dep/src/index.ts", + content: ` + import "./sub/folder";` + }; + + const depSrcSubFolderIndexTs: File = { + path: "/packages/dep/src/sub/folder/index.ts", + content: `export const dep = 0;` + }; + + const link: SymLink = { + path: "/packages/dep", + symLink: "/packages/app/node_modules/dep", + }; + + + describe("unittests:: tsserver:: symlinkCache", () => { + it("contains symlinks discovered by project references resolution after program creation", () => { + const { session, projectService } = setup(); + openFilesForSession([appSrcIndexTs], session); + const project = projectService.configuredProjects.get(appTsconfigJson.path)!; + assert.deepEqual( + project.getSymlinkCache()?.getSymlinkedDirectories()?.get(link.symLink as Path), + { real: link.path, realPath: link.path as Path } + ); + }); + }); + + function setup() { + const host = createServerHost([ + appTsconfigJson, + appSrcIndexTs, + depPackageJson, + depTsconfigJson, + depSrcIndexTs, + depSrcSubFolderIndexTs, + link, + ]); + const session = createSession(host); + const projectService = session.getProjectService(); + return { + host, + projectService, + session, + }; + } +} From 6cc27a06d1466bdde8159e59755c3899a04f9961 Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Thu, 27 May 2021 15:33:31 -0700 Subject: [PATCH 07/11] Fix test, fails for right reasons now --- src/testRunner/unittests/tsserver/symlinkCache.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/testRunner/unittests/tsserver/symlinkCache.ts b/src/testRunner/unittests/tsserver/symlinkCache.ts index 80d996eee4ff2..b0c4f06bba805 100644 --- a/src/testRunner/unittests/tsserver/symlinkCache.ts +++ b/src/testRunner/unittests/tsserver/symlinkCache.ts @@ -15,7 +15,7 @@ namespace ts.projectSystem { const appSrcIndexTs: File = { path: "/packages/app/src/index.ts", - content: `import "dep";` + content: `import "dep/does/not/exist";` }; const depPackageJson: File = { @@ -43,19 +43,18 @@ namespace ts.projectSystem { }; const link: SymLink = { - path: "/packages/dep", - symLink: "/packages/app/node_modules/dep", + path: "/packages/app/node_modules/dep", + symLink: "../../dep", }; - describe("unittests:: tsserver:: symlinkCache", () => { it("contains symlinks discovered by project references resolution after program creation", () => { const { session, projectService } = setup(); openFilesForSession([appSrcIndexTs], session); const project = projectService.configuredProjects.get(appTsconfigJson.path)!; assert.deepEqual( - project.getSymlinkCache()?.getSymlinkedDirectories()?.get(link.symLink as Path), - { real: link.path, realPath: link.path as Path } + project.getSymlinkCache()?.getSymlinkedDirectories()?.get(link.path + "/" as Path), + { real: "/packages/dep", realPath: "/packages/dep" as Path } ); }); }); From e221f9ecb4aebf42f21ba2d91ed07ddcd570f742 Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Thu, 27 May 2021 16:00:18 -0700 Subject: [PATCH 08/11] Preserve cache built up during program creation, then fill in with program resolutions --- src/compiler/checker.ts | 2 -- src/compiler/moduleSpecifiers.ts | 7 +--- src/compiler/program.ts | 16 +++++---- src/compiler/types.ts | 6 ++-- src/compiler/utilities.ts | 36 ++++++++++++------- src/server/project.ts | 16 +++++---- src/services/types.ts | 2 +- src/services/utilities.ts | 2 -- .../unittests/tsserver/symlinkCache.ts | 2 +- 9 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1e24daf252b14..92cc28d328668 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4482,10 +4482,8 @@ namespace ts { // If no full tracker is provided, fake up a dummy one with a basic limited-functionality moduleResolverHost tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: () => false, moduleResolverHost: flags! & NodeBuilderFlags.DoNotIncludeSymbolChain ? { getCommonSourceDirectory: !!(host as Program).getCommonSourceDirectory ? () => (host as Program).getCommonSourceDirectory() : () => "", - getSourceFiles: () => host.getSourceFiles(), getCurrentDirectory: () => host.getCurrentDirectory(), getSymlinkCache: maybeBind(host, host.getSymlinkCache), - getResolvedTypeReferenceDirectives: maybeBind(host, host.getResolvedTypeReferenceDirectives), useCaseSensitiveFileNames: maybeBind(host, host.useCaseSensitiveFileNames), redirectTargetsMap: host.redirectTargetsMap, getProjectReferenceRedirect: fileName => host.getProjectReferenceRedirect(fileName), diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index bb9d04333e58a..6e53588e56258 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -304,12 +304,7 @@ namespace ts.moduleSpecifiers { if (result) return result; } - const links = host.getSymlinkCache?.() || discoverProbableSymlinks( - host.getSourceFiles(), - host.getResolvedTypeReferenceDirectives ? arrayFrom(host.getResolvedTypeReferenceDirectives?.().values()) : emptyArray, - getCanonicalFileName, - cwd); - const symlinkedDirectories = links.getSymlinkedDirectoriesByRealpath(); + const symlinkedDirectories = host.getSymlinkCache?.().getSymlinkedDirectoriesByRealpath(); const fullImportedFileName = getNormalizedAbsolutePath(importedFileName, cwd); const result = symlinkedDirectories && forEachAncestorDirectory(getDirectoryPath(fullImportedFileName), realPathDirectory => { const symlinkDirectories = symlinkedDirectories.get(ensureTrailingDirectorySeparator(toPath(realPathDirectory, cwd, getCanonicalFileName))); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4dff0ffd8eed7..6b13178589653 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1650,7 +1650,6 @@ namespace ts { getLibFileFromReference: program.getLibFileFromReference, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect, - getResolvedTypeReferenceDirectives: program.getResolvedTypeReferenceDirectives, getProjectReferenceRedirect, isSourceOfProjectReferenceRedirect, getSymlinkCache, @@ -3661,11 +3660,16 @@ namespace ts { } function getSymlinkCache(): SymlinkCache { - return host.getSymlinkCache?.() || (symlinks ||= discoverProbableSymlinks( - files, - arrayFrom(resolvedTypeReferenceDirectives.values()), - getCanonicalFileName, - host.getCurrentDirectory())); + if (host.getSymlinkCache) { + return host.getSymlinkCache(); + } + if (!symlinks) { + symlinks = createSymlinkCache(currentDirectory, getCanonicalFileName); + } + if (files && resolvedTypeReferenceDirectives && !symlinks.hasProcessedResolutions()) { + symlinks.setSymlinksFromResolutions(files, resolvedTypeReferenceDirectives); + } + return symlinks; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index daf7ccdc90dac..11e7cecd225c2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6559,7 +6559,7 @@ namespace ts { // TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesn't use compilerHost as base /*@internal*/createDirectory?(directory: string): void; - /*@internal*/getSymlinkCache?(): SymlinkCache | undefined; + /*@internal*/getSymlinkCache?(): SymlinkCache; // For testing: /*@internal*/ disableUseFileVersionAsSignature?: boolean; @@ -8132,14 +8132,12 @@ namespace ts { directoryExists?(path: string): boolean; readFile?(path: string): string | undefined; realpath?(path: string): string; - getSymlinkCache?(): SymlinkCache | undefined; - getResolvedTypeReferenceDirectives?(): ReadonlyESMap<string, ResolvedTypeReferenceDirective | undefined>; + getSymlinkCache?(): SymlinkCache; getModuleSpecifierCache?(): ModuleSpecifierCache; getGlobalTypingsCacheLocation?(): string | undefined; getNearestAncestorDirectoryWithPackageJson?(fileName: string, rootDir?: string): string | undefined; preferFailureToResultsWithIgnoredPaths?(): boolean; - getSourceFiles(): readonly SourceFile[]; readonly redirectTargetsMap: RedirectTargetsMap; getProjectReferenceRedirect(fileName: string): string | undefined; isSourceOfProjectReferenceRedirect(fileName: string): boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index dd497f68a6353..c3a7cb9318622 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6190,12 +6190,25 @@ namespace ts { setSymlinkedFile(symlinkPath: Path, real: string): void; /*@internal*/ setSymlinkedDirectoryFromSymlinkedFile(symlink: string, real: string): void; + /** + * @internal + * Uses resolvedTypeReferenceDirectives from program instead of from files, since files + * don't include automatic type reference directives. Must be called only when + * `hasProcessedResolutions` returns false (once per cache instance). + */ + setSymlinksFromResolutions(files: readonly SourceFile[], typeReferenceDirectives: ReadonlyESMap<string, ResolvedTypeReferenceDirective | undefined> | undefined): void; + /** + * @internal + * Whether `setSymlinksFromResolutions` has already been called. + */ + hasProcessedResolutions(): boolean; } export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonicalFileName): SymlinkCache { let symlinkedDirectories: ESMap<Path, SymlinkedDirectory | false> | undefined; let symlinkedDirectoriesByRealpath: MultiMap<Path, string> | undefined; let symlinkedFiles: ESMap<Path, string> | undefined; + let hasProcessedResolutions = false; return { getSymlinkedFiles: () => symlinkedFiles, getSymlinkedDirectories: () => symlinkedDirectories, @@ -6224,19 +6237,19 @@ namespace ts { }); } }, + setSymlinksFromResolutions(files, typeReferenceDirectives) { + Debug.assert(!hasProcessedResolutions); + hasProcessedResolutions = true; + for (const file of files) { + file.resolvedModules?.forEach(resolution => processResolution(this, resolution)); + } + typeReferenceDirectives?.forEach(resolution => processResolution(this, resolution)); + }, + hasProcessedResolutions: () => hasProcessedResolutions, }; - } - - export function discoverProbableSymlinks(files: readonly SourceFile[], typeReferenceDirectives: readonly (ResolvedTypeReferenceDirective | undefined)[], getCanonicalFileName: GetCanonicalFileName, cwd: string): SymlinkCache { - const cache = createSymlinkCache(cwd, getCanonicalFileName); - const symlinksFromFiles: readonly (ResolvedModuleFull | ResolvedTypeReferenceDirective | undefined)[] = flatMap(files, sf => { - return sf.resolvedModules && arrayFrom(mapDefinedIterator(sf.resolvedModules.values(), res => - res?.originalPath ? res : undefined)); - }); - const resolutions = symlinksFromFiles.concat(typeReferenceDirectives); - for (const resolution of resolutions) { - if (!resolution || !resolution.originalPath || !resolution.resolvedFileName) continue; + function processResolution(cache: SymlinkCache, resolution: ResolvedModuleFull | ResolvedTypeReferenceDirective | undefined) { + if (!resolution || !resolution.originalPath || !resolution.resolvedFileName) return; const { resolvedFileName, originalPath } = resolution; cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedFileName); const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedFileName, originalPath, cwd, getCanonicalFileName) || emptyArray; @@ -6246,7 +6259,6 @@ namespace ts { { real: commonResolved, realPath: toPath(commonResolved, cwd, getCanonicalFileName) }); } } - return cache; } function guessDirectorySymlink(a: string, b: string, cwd: string, getCanonicalFileName: GetCanonicalFileName): [string, string] | undefined { diff --git a/src/server/project.ts b/src/server/project.ts index 6457c0ec75283..12377af72d91b 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -355,12 +355,16 @@ namespace ts.server { * @internal * Returns undefined prior to program creation. */ - getSymlinkCache(): SymlinkCache | undefined { - return this.symlinks || this.program && (this.symlinks = discoverProbableSymlinks( - this.program.getSourceFiles() || emptyArray, - arrayFrom(this.program.getResolvedTypeReferenceDirectives().values()), - this.getCanonicalFileName, - this.getCurrentDirectory())); + getSymlinkCache(): SymlinkCache { + if (!this.symlinks) { + this.symlinks = createSymlinkCache(this.getCurrentDirectory(), this.getCanonicalFileName); + } + if (this.program && !this.symlinks.hasProcessedResolutions()) { + this.symlinks.setSymlinksFromResolutions( + this.program.getSourceFiles(), + this.program.getResolvedTypeReferenceDirectives()); + } + return this.symlinks; } // Method of LanguageServiceHost diff --git a/src/services/types.ts b/src/services/types.ts index 75c696d35137a..be1d09ff69c57 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -277,7 +277,7 @@ namespace ts { /* @internal */ getGlobalTypingsCacheLocation?(): string | undefined; /* @internal */ - getSymlinkCache?(files?: readonly SourceFile[]): SymlinkCache | undefined; + getSymlinkCache?(files?: readonly SourceFile[]): SymlinkCache; /* * Required for full import and type reference completions. diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 0f9dd7f202ff8..9b4395db32c64 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1845,9 +1845,7 @@ namespace ts { getSymlinkCache: maybeBind(host, host.getSymlinkCache) || program.getSymlinkCache, getModuleSpecifierCache: maybeBind(host, host.getModuleSpecifierCache), getGlobalTypingsCacheLocation: maybeBind(host, host.getGlobalTypingsCacheLocation), - getSourceFiles: () => program.getSourceFiles(), redirectTargetsMap: program.redirectTargetsMap, - getResolvedTypeReferenceDirectives: program.getResolvedTypeReferenceDirectives, getProjectReferenceRedirect: fileName => program.getProjectReferenceRedirect(fileName), isSourceOfProjectReferenceRedirect: fileName => program.isSourceOfProjectReferenceRedirect(fileName), getNearestAncestorDirectoryWithPackageJson: maybeBind(host, host.getNearestAncestorDirectoryWithPackageJson), diff --git a/src/testRunner/unittests/tsserver/symlinkCache.ts b/src/testRunner/unittests/tsserver/symlinkCache.ts index b0c4f06bba805..39160c5399f23 100644 --- a/src/testRunner/unittests/tsserver/symlinkCache.ts +++ b/src/testRunner/unittests/tsserver/symlinkCache.ts @@ -54,7 +54,7 @@ namespace ts.projectSystem { const project = projectService.configuredProjects.get(appTsconfigJson.path)!; assert.deepEqual( project.getSymlinkCache()?.getSymlinkedDirectories()?.get(link.path + "/" as Path), - { real: "/packages/dep", realPath: "/packages/dep" as Path } + { real: "/packages/dep/", realPath: "/packages/dep/" as Path } ); }); }); From 287dfe3821a9c7b8d5e43f702f40b4cb56b7bbbc Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Thu, 27 May 2021 16:02:54 -0700 Subject: [PATCH 09/11] Remove obsolete comment --- src/server/project.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/server/project.ts b/src/server/project.ts index 12377af72d91b..8f76436838b7a 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -351,10 +351,7 @@ namespace ts.server { return this.projectService.typingsCache; } - /** - * @internal - * Returns undefined prior to program creation. - */ + /*@internal*/ getSymlinkCache(): SymlinkCache { if (!this.symlinks) { this.symlinks = createSymlinkCache(this.getCurrentDirectory(), this.getCanonicalFileName); From db6508441a1e56f13a6a5d8ff1dd1d0ea1232e12 Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Thu, 27 May 2021 16:03:31 -0700 Subject: [PATCH 10/11] Remove obsolete type assertion --- src/server/project.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/project.ts b/src/server/project.ts index 8f76436838b7a..e61bbe4582536 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1907,7 +1907,7 @@ namespace ts.server { moduleResolutionHost)); const program = hostProject.getCurrentProgram()!; - const symlinkCache = hostProject.getSymlinkCache()!; // Guaranteed to be set when program is set + const symlinkCache = hostProject.getSymlinkCache(); for (const resolution of resolutions) { if (!resolution.resolvedTypeReferenceDirective?.resolvedFileName) continue; const { resolvedFileName, originalPath } = resolution.resolvedTypeReferenceDirective; From 8c439fab4b3c1274ed931f4edf84accc5507518a Mon Sep 17 00:00:00 2001 From: Andrew Branch <andrew@wheream.io> Date: Tue, 8 Jun 2021 10:33:31 -0500 Subject: [PATCH 11/11] Revert fully filtering out ignored paths --- src/compiler/moduleSpecifiers.ts | 13 ++------- src/compiler/types.ts | 1 - src/services/utilities.ts | 1 - .../importStatementCompletions_pnpm2.ts | 29 ------------------- 4 files changed, 2 insertions(+), 42 deletions(-) delete mode 100644 tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 6e53588e56258..4aa77d573863d 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -163,16 +163,7 @@ namespace ts.moduleSpecifiers { return pathsSpecifiers?.length ? pathsSpecifiers : nodeModulesSpecifiers?.length ? nodeModulesSpecifiers : - fallback(); - - // Unless 'preferFailureToResultsWithIgnoredPaths' is set, we *must* generate a relative path fallback. - // (The declaration emitter must write something, but auto-imports may exclude bad suggestions.) - function fallback() { - if (host.preferFailureToResultsWithIgnoredPaths?.()) { - return relativeSpecifiers || emptyArray; - } - return Debug.checkDefined(relativeSpecifiers); - } + Debug.checkDefined(relativeSpecifiers); } interface Info { @@ -295,7 +286,7 @@ namespace ts.moduleSpecifiers { const redirects = host.redirectTargetsMap.get(importedPath) || emptyArray; const importedFileNames = [...(referenceRedirect ? [referenceRedirect] : emptyArray), importedFileName, ...redirects]; const targets = importedFileNames.map(f => getNormalizedAbsolutePath(f, cwd)); - let shouldFilterIgnoredPaths = host.preferFailureToResultsWithIgnoredPaths?.() || !every(targets, containsIgnoredPath); + let shouldFilterIgnoredPaths = !every(targets, containsIgnoredPath); if (!preferSymlinks) { // Symlinks inside ignored paths are already filtered out of the symlink cache, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 11e7cecd225c2..d575631ca8b01 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -8136,7 +8136,6 @@ namespace ts { getModuleSpecifierCache?(): ModuleSpecifierCache; getGlobalTypingsCacheLocation?(): string | undefined; getNearestAncestorDirectoryWithPackageJson?(fileName: string, rootDir?: string): string | undefined; - preferFailureToResultsWithIgnoredPaths?(): boolean; readonly redirectTargetsMap: RedirectTargetsMap; getProjectReferenceRedirect(fileName: string): string | undefined; diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 9b4395db32c64..2468ff0836fe5 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1850,7 +1850,6 @@ namespace ts { isSourceOfProjectReferenceRedirect: fileName => program.isSourceOfProjectReferenceRedirect(fileName), getNearestAncestorDirectoryWithPackageJson: maybeBind(host, host.getNearestAncestorDirectoryWithPackageJson), getFileIncludeReasons: () => program.getFileIncludeReasons(), - preferFailureToResultsWithIgnoredPaths: returnTrue, }; } diff --git a/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts b/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts deleted file mode 100644 index 5649c2447686b..0000000000000 --- a/tests/cases/fourslash/server/importStatementCompletions_pnpm2.ts +++ /dev/null @@ -1,29 +0,0 @@ -/// <reference path="../fourslash.ts" /> - -// @Filename: /project/tsconfig.json -//// { "compilerOptions": { "module": "commonjs" } } - -// @Filename: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react/index.d.ts -//// import "csstype"; -//// export declare function Component(): void; - -// @Filename: /project/node_modules/.pnpm/csstype@3.0.8/node_modules/csstype/index.d.ts -//// export interface SvgProperties {} - -// @Filename: /project/index.ts -//// [|import SvgProp/**/|] - -// @link: /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/@types/react -> /project/node_modules/@types/react -// @link: /project/node_modules/.pnpm/csstype@3.0.8/node_modules/csstype -> /project/node_modules/.pnpm/@types+react@17.0.7/node_modules/csstype - -goTo.marker(""); -verify.completions({ - isNewIdentifierLocation: true, - marker: "", - exact: [], - preferences: { - includeCompletionsForImportStatements: true, - includeCompletionsWithInsertText: true, - includeCompletionsWithSnippetText: true, - } -});