Revert to including only open files in partial semantic server mode (#40026)

This commit is contained in:
Sheetal Nandi 2020-08-12 20:45:59 -07:00 committed by GitHub
parent 620e260576
commit 03d946d145
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 13 additions and 169 deletions

View File

@ -807,8 +807,6 @@ namespace ts {
let mapFromFileToProjectReferenceRedirects: ESMap<Path, Path> | undefined;
let mapFromToProjectReferenceRedirectSource: ESMap<Path, SourceOfProjectReferenceRedirect> | undefined;
let skippedTrippleSlashReferences: Set<Path> | undefined;
const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() &&
!options.disableSourceOfProjectReferenceRedirect;
const { onProgramCreateComplete, fileExists, directoryExists } = updateHostForUseSourceOfProjectReferenceRedirect({
@ -931,7 +929,6 @@ namespace ts {
getSourceFiles: () => files,
getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217
getRefFileMap: () => refFileMap,
getSkippedTrippleSlashReferences: () => skippedTrippleSlashReferences,
getFilesByNameMap: () => filesByName,
getCompilerOptions: () => options,
getSyntacticDiagnostics,
@ -1275,7 +1272,6 @@ namespace ts {
const oldSourceFiles = oldProgram.getSourceFiles();
const enum SeenPackageName { Exists, Modified }
const seenPackageNames = new Map<string, SeenPackageName>();
const oldSkippedTrippleSlashReferences = oldProgram.getSkippedTrippleSlashReferences();
for (const oldSourceFile of oldSourceFiles) {
let newSourceFile = host.getSourceFileByPath
@ -1348,11 +1344,6 @@ namespace ts {
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
if (oldSkippedTrippleSlashReferences?.has(oldSourceFile.path) && includeTripleslashReferencesFrom(newSourceFile)) {
// tripleslash reference resolution is now allowed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
// check imports and module augmentations
collectExternalModuleReferences(newSourceFile);
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
@ -1440,7 +1431,6 @@ namespace ts {
missingFilePaths = oldProgram.getMissingFilePaths();
refFileMap = oldProgram.getRefFileMap();
skippedTrippleSlashReferences = oldSkippedTrippleSlashReferences;
// update fileName -> file mapping
Debug.assert(newSourceFiles.length === oldProgram.getSourceFiles().length);
@ -2660,15 +2650,7 @@ namespace ts {
return projectReferenceRedirects.get(projectReferencePath) || undefined;
}
function includeTripleslashReferencesFrom(file: SourceFile) {
return !host.includeTripleslashReferencesFrom || host.includeTripleslashReferencesFrom(file.originalFileName);
}
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
if (!includeTripleslashReferencesFrom(file)) {
(skippedTrippleSlashReferences ||= new Set()).add(file.path);
return;
}
forEach(file.referencedFiles, (ref, index) => {
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
processSourceFile(

View File

@ -11,7 +11,6 @@ namespace ts {
invalidateResolutionsOfFailedLookupLocations(): boolean;
invalidateResolutionOfFile(filePath: Path): void;
removeRelativeNoResolveResolutionsOfFile(filePath: Path): boolean;
removeResolutionsOfFile(filePath: Path): void;
removeResolutionsFromProjectReferenceRedirects(filePath: Path): void;
setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: ESMap<Path, readonly string[]>): void;
@ -142,21 +141,7 @@ namespace ts {
type GetResolutionWithResolvedFileName<T extends ResolutionWithFailedLookupLocations = ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName = ResolutionWithResolvedFileName> =
(resolution: T) => R | undefined;
export enum ResolutionKind {
All,
RelativeReferencesInOpenFileOnly
}
const noResolveResolvedModule: ResolvedModuleWithFailedLookupLocations = {
resolvedModule: undefined,
failedLookupLocations: []
};
const noResolveResolvedTypeReferenceDirective: ResolvedTypeReferenceDirectiveWithFailedLookupLocations = {
resolvedTypeReferenceDirective: undefined,
failedLookupLocations: []
};
export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string | undefined, resolutionKind: ResolutionKind, logChangesWhenResolvingModule: boolean): ResolutionCache {
export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string | undefined, logChangesWhenResolvingModule: boolean): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
let filesWithInvalidatedResolutions: Set<Path> | undefined;
let filesWithInvalidatedNonRelativeUnresolvedImports: ReadonlyESMap<Path, readonly string[]> | undefined;
@ -221,7 +206,6 @@ namespace ts {
hasChangedAutomaticTypeDirectiveNames: () => hasChangedAutomaticTypeDirectiveNames,
invalidateResolutionOfFile,
invalidateResolutionsOfFailedLookupLocations,
removeRelativeNoResolveResolutionsOfFile,
setFilesWithInvalidatedNonRelativeUnresolvedImports,
createHasInvalidatedResolution,
updateTypeRootsWatch,
@ -357,12 +341,11 @@ namespace ts {
shouldRetryResolution: (t: T) => boolean;
reusedNames?: readonly string[];
logChanges?: boolean;
noResolveResolution: T;
}
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
names, containingFile, redirectedReference,
cache, perDirectoryCacheWithRedirects,
loader, getResolutionWithResolvedFileName, noResolveResolution,
loader, getResolutionWithResolvedFileName,
shouldRetryResolution, reusedNames, logChanges
}: ResolveNamesWithLocalCacheInput<T, R>): (R | undefined)[] {
const path = resolutionHost.toPath(containingFile);
@ -399,10 +382,7 @@ namespace ts {
resolution = resolutionInDirectory;
}
else {
resolution = resolutionKind === ResolutionKind.All ||
(isExternalModuleNameRelative(name) && resolutionHost.fileIsOpen(path)) ?
loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference) :
noResolveResolution;
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference);
perDirectoryResolution.set(name, resolution);
}
resolutionsInFile.set(name, resolution);
@ -461,7 +441,6 @@ namespace ts {
loader: resolveTypeReferenceDirective,
getResolutionWithResolvedFileName: getResolvedTypeReferenceDirective,
shouldRetryResolution: resolution => resolution.resolvedTypeReferenceDirective === undefined,
noResolveResolution: noResolveResolvedTypeReferenceDirective,
});
}
@ -477,7 +456,6 @@ namespace ts {
shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
reusedNames,
logChanges: logChangesWhenResolvingModule,
noResolveResolution: noResolveResolvedModule,
});
}
@ -763,31 +741,6 @@ namespace ts {
}
}
function removeRelativeNoResolveResolutionsOfFileFromCache<T extends ResolutionWithFailedLookupLocations>(
cache: ESMap<Path, ESMap<string, T>>,
filePath: Path,
noResolveResolution: T,
) {
Debug.assert(resolutionKind === ResolutionKind.RelativeReferencesInOpenFileOnly);
// Deleted file, stop watching failed lookups for all the resolutions in the file
const resolutions = cache.get(filePath);
if (!resolutions) return false;
let invalidated = false;
resolutions.forEach((resolution, name) => {
if (resolution === noResolveResolution && isExternalModuleNameRelative(name)) {
resolutions.delete(name);
invalidated = true;
}
});
return invalidated;
}
function removeRelativeNoResolveResolutionsOfFile(filePath: Path) {
let invalidated = removeRelativeNoResolveResolutionsOfFileFromCache(resolvedModuleNames, filePath, noResolveResolvedModule);
invalidated = removeRelativeNoResolveResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath, noResolveResolvedTypeReferenceDirective) || invalidated;
return invalidated;
}
function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: ReadonlyESMap<Path, readonly string[]>) {
Debug.assert(filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined);
filesWithInvalidatedNonRelativeUnresolvedImports = filesMap;

View File

@ -3687,8 +3687,6 @@ namespace ts {
/* @internal */
getRefFileMap(): MultiMap<Path, RefFile> | undefined;
/* @internal */
getSkippedTrippleSlashReferences(): Set<Path> | undefined;
/* @internal */
getFilesByNameMap(): ESMap<string, SourceFile | false | undefined>;
/**
@ -6232,7 +6230,6 @@ namespace ts {
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
*/
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedTypeReferenceDirective | undefined)[];
/* @internal */ includeTripleslashReferencesFrom?(containingFile: string): boolean;
getEnvironmentVariable?(name: string): string | undefined;
/* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void;
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;

View File

@ -320,7 +320,6 @@ namespace ts {
configFileName ?
getDirectoryPath(getNormalizedAbsolutePath(configFileName, currentDirectory)) :
currentDirectory,
ResolutionKind.All,
/*logChangesWhenResolvingModule*/ false
);
// Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names

View File

@ -3029,15 +3029,7 @@ namespace ts.server {
let retainProjects: ConfiguredProject[] | ConfiguredProject | undefined;
let projectForConfigFileDiag: ConfiguredProject | undefined;
let defaultConfigProjectIsCreated = false;
if (this.serverMode === LanguageServiceMode.PartialSemantic) {
// Invalidate resolutions in the file since this file is now open
info.containingProjects.forEach(project => {
if (project.resolutionCache.removeRelativeNoResolveResolutionsOfFile(info.path)) {
project.markAsDirty();
}
});
}
else if (!project && this.serverMode === LanguageServiceMode.Semantic) { // Checking semantic mode is an optimization
if (!project && this.serverMode === LanguageServiceMode.Semantic) { // Checking semantic mode is an optimization
configFileName = this.getConfigFileNameForFile(info);
if (configFileName) {
project = this.findConfiguredProjectByProjectName(configFileName);
@ -3124,10 +3116,6 @@ namespace ts.server {
Debug.assert(this.openFiles.has(info.path));
this.assignOrphanScriptInfoToInferredProject(info, this.openFiles.get(info.path));
}
else if (this.serverMode === LanguageServiceMode.PartialSemantic && info.cacheSourceFile?.sourceFile.referencedFiles.length) {
// This file was just opened and references in this file will previously not been resolved so schedule update
info.containingProjects.forEach(project => project.markAsDirty());
}
Debug.assert(!info.isOrphan());
return { configFileName, configFileErrors, retainProjects };
}

View File

@ -285,6 +285,7 @@ namespace ts.server {
break;
case LanguageServiceMode.PartialSemantic:
this.languageServiceEnabled = true;
this.compilerOptions.noResolve = true;
this.compilerOptions.types = [];
break;
case LanguageServiceMode.Syntactic:
@ -310,7 +311,6 @@ namespace ts.server {
this.resolutionCache = createResolutionCache(
this,
currentDirectory && this.currentDirectory,
projectService.serverMode === LanguageServiceMode.Semantic ? ResolutionKind.All : ResolutionKind.RelativeReferencesInOpenFileOnly,
/*logChangesWhenResolvingModule*/ true
);
this.languageService = createLanguageService(this, this.documentRegistry, this.projectService.serverMode);
@ -466,20 +466,6 @@ namespace ts.server {
return this.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference);
}
/*@internal*/
includeTripleslashReferencesFrom(containingFile: string) {
switch (this.projectService.serverMode) {
case LanguageServiceMode.Semantic:
return true;
case LanguageServiceMode.PartialSemantic:
return this.fileIsOpen(this.toPath(containingFile));
case LanguageServiceMode.Syntactic:
return false;
default:
Debug.assertNever(this.projectService.serverMode);
}
}
directoryExists(path: string): boolean {
return this.directoryStructureHost.directoryExists!(path); // TODO: GH#18217
}

View File

@ -1344,7 +1344,6 @@ namespace ts {
onReleaseOldSourceFile,
hasInvalidatedResolution,
hasChangedAutomaticTypeDirectiveNames,
includeTripleslashReferencesFrom: maybeBind(host, host.includeTripleslashReferencesFrom),
trace: maybeBind(host, host.trace),
resolveModuleNames: maybeBind(host, host.resolveModuleNames),
resolveTypeReferenceDirectives: maybeBind(host, host.resolveTypeReferenceDirectives),

View File

@ -272,7 +272,6 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedModule | undefined)[];
getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedTypeReferenceDirective | undefined)[];
/* @internal */ includeTripleslashReferencesFrom?(containingFile: string): boolean;
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: HasChangedAutomaticTypeDirectiveNames;
/* @internal */

View File

@ -1,5 +1,5 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: Semantic operations on Approximate Semantic only server", () => {
describe("unittests:: tsserver:: Semantic operations on PartialSemantic server", () => {
function setup() {
const file1: File = {
path: `${tscWatch.projectRoot}/a.ts`,
@ -31,38 +31,18 @@ import { something } from "something";
}
it("open files are added to inferred project even if config file is present and semantic operations succeed", () => {
const { host, session, file1, file2, file3, something } = setup();
const { host, session, file1, file2 } = setup();
const service = session.getProjectService();
openFilesForSession([file1], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
const project = service.inferredProjects[0];
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]); // Relative import from open file is resolves but not non relative
checkProjectActualFiles(project, [libFile.path, file1.path]); // no imports are resolved
verifyCompletions();
verifyGoToDefToB();
openFilesForSession([file2], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, file3.path]);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
verifyCompletions();
verifyGoToDefToB();
verifyGoToDefToC();
openFilesForSession([file3], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, file3.path]);
openFilesForSession([something], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, file3.path, something.path]);
// Close open files and verify resolutions
closeFilesForSession([file3], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, file3.path, something.path]);
closeFilesForSession([file2], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, file3.path, something.path]);
function verifyCompletions() {
assert.isTrue(project.languageServiceEnabled);
@ -93,34 +73,6 @@ import { something } from "something";
source: undefined
};
}
function verifyGoToDefToB() {
const response = session.executeCommandSeq<protocol.DefinitionAndBoundSpanRequest>({
command: protocol.CommandTypes.DefinitionAndBoundSpan,
arguments: protocolFileLocationFromSubstring(file1, "y")
}).response as protocol.DefinitionInfoAndBoundSpan;
assert.deepEqual(response, {
definitions: [{
file: file2.path,
...protocolTextSpanWithContextFromSubstring({ fileText: file2.content, text: "y", contextText: "export const y = 10;" })
}],
textSpan: protocolTextSpanWithContextFromSubstring({ fileText: file1.content, text: "y" })
});
}
function verifyGoToDefToC() {
const response = session.executeCommandSeq<protocol.DefinitionAndBoundSpanRequest>({
command: protocol.CommandTypes.DefinitionAndBoundSpan,
arguments: protocolFileLocationFromSubstring(file1, "cc")
}).response as protocol.DefinitionInfoAndBoundSpan;
assert.deepEqual(response, {
definitions: [{
file: file3.path,
...protocolTextSpanWithContextFromSubstring({ fileText: file3.content, text: "cc", contextText: "export const cc = 10;" })
}],
textSpan: protocolTextSpanWithContextFromSubstring({ fileText: file1.content, text: "cc" })
});
}
});
it("throws on unsupported commands", () => {
@ -156,7 +108,7 @@ import { something } from "something";
});
it("should not include auto type reference directives", () => {
const { host, session, file1, file2 } = setup();
const { host, session, file1 } = setup();
const atTypes: File = {
path: `/node_modules/@types/somemodule/index.d.ts`,
content: "export const something = 10;"
@ -166,7 +118,7 @@ import { something } from "something";
openFilesForSession([file1], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
const project = service.inferredProjects[0];
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]); // Should not contain atTypes
checkProjectActualFiles(project, [libFile.path, file1.path]); // Should not contain atTypes
});
it("should not include referenced files from unopened files", () => {
@ -200,18 +152,7 @@ function fooB() { }`
openFilesForSession([file1], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
const project = service.inferredProjects[0];
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, something.path]); // Should not contains c
openFilesForSession([file2], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
assert.isTrue(project.dirty);
project.updateGraph();
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, file3.path, something.path]);
closeFilesForSession([file2], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
assert.isFalse(project.dirty);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, file3.path, something.path]);
checkProjectActualFiles(project, [libFile.path, file1.path]); // no resolve
});
it("should not crash when external module name resolution is reused", () => {
@ -220,7 +161,7 @@ function fooB() { }`
openFilesForSession([file1], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
const project = service.inferredProjects[0];
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
checkProjectActualFiles(project, [libFile.path, file1.path]);
// Close the file that contains non relative external module name and open some file that doesnt have non relative external module import
closeFilesForSession([file1], session);