From e22e7cc09a6077277b28ac5f6e5a6d89f56dec68 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 31 May 2016 21:10:06 -0700 Subject: [PATCH] drop Timestamped, create separate classes for different project types --- src/server/editorServices.ts | 113 ++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 97cefce53c0..cb74605f468 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -123,25 +123,15 @@ namespace ts.server { } } - interface Timestamped { - lastCheckTime?: number; - } - - interface TimestampedResolvedModule extends ResolvedModuleWithFailedLookupLocations, Timestamped { - } - - interface TimestampedResolvedTypeReferenceDirective extends ResolvedTypeReferenceDirectiveWithFailedLookupLocations, Timestamped { - } - export class LSHost implements ts.LanguageServiceHost { private compilationSettings: ts.CompilerOptions; - private resolvedModuleNames: ts.FileMap>; - private resolvedTypeReferenceDirectives: ts.FileMap>; + private resolvedModuleNames: ts.FileMap>; + private resolvedTypeReferenceDirectives: ts.FileMap>; private moduleResolutionHost: ts.ModuleResolutionHost; constructor(private host: ServerHost, private project: Project) { - this.resolvedModuleNames = createFileMap>(); - this.resolvedTypeReferenceDirectives = createFileMap>(); + this.resolvedModuleNames = createFileMap>(); + this.resolvedTypeReferenceDirectives = createFileMap>(); this.moduleResolutionHost = { fileExists: fileName => this.fileExists(fileName), readFile: fileName => this.host.readFile(fileName), @@ -149,7 +139,7 @@ namespace ts.server { }; } - private resolveNamesWithLocalCache( + private resolveNamesWithLocalCache( names: string[], containingFile: string, cache: ts.FileMap>, @@ -173,9 +163,7 @@ namespace ts.server { resolution = existingResolution; } else { - resolution = loader(name, containingFile, compilerOptions, this.moduleResolutionHost); - resolution.lastCheckTime = Date.now(); - newResolutions[name] = resolution; + newResolutions[name] = resolution = loader(name, containingFile, compilerOptions, this.moduleResolutionHost); } } @@ -195,7 +183,6 @@ namespace ts.server { if (getResult(resolution)) { // TODO: consider checking failedLookupLocations - // TODO: use lastCheckTime to track expiration for module name resolution return true; } @@ -292,31 +279,31 @@ namespace ts.server { compilerOptions?: ts.CompilerOptions; } - export class Project { + export abstract class Project { private rootFiles: ScriptInfo[] = []; private pathToScriptInfo: ts.FileMap; private readonly lsHost: LSHost; + private filenameToSourceFile: ts.Map = {}; readonly languageService: LanguageService; readonly getCanonicalFileName: (fileName: string) => string; - projectFileWatcher: FileWatcher; - directoryWatcher: FileWatcher; - // Used to keep track of what directories are watched for this project - directoriesWatchedForTsconfig: string[] = []; - program: ts.Program; - filenameToSourceFile: ts.Map = {}; - updateGraphSeq = 0; - /** Used for configured projects which may have multiple open roots */ - openRefCount = 0; + private program: ts.Program; + + constructor( + readonly projectFilename: string, + public readonly projectService: ProjectService, + documentRegistry: ts.DocumentRegistry, + public projectOptions?: ProjectOptions) { - constructor(readonly projectFilename: string, public projectService: ProjectService, documentRegistry: ts.DocumentRegistry, public projectOptions?: ProjectOptions) { this.pathToScriptInfo = ts.createFileMap(); this.getCanonicalFileName = ts.createGetCanonicalFileName(this.projectService.host.useCaseSensitiveFileNames); + if (projectOptions && projectOptions.files) { // If files are listed explicitly, allow all extensions projectOptions.compilerOptions.allowNonTsExtensions = true; } + this.lsHost = new LSHost(this.projectService.host, this); if (projectOptions && projectOptions.compilerOptions) { this.lsHost.setCompilationSettings(projectOptions.compilerOptions); @@ -330,15 +317,6 @@ namespace ts.server { this.languageService = ts.createLanguageService(this.lsHost, documentRegistry); } - addOpenRef() { - this.openRefCount++; - } - - deleteOpenRef() { - this.openRefCount--; - return this.openRefCount; - } - getRootFiles() { return this.rootFiles.map(info => info.fileName); } @@ -466,6 +444,35 @@ namespace ts.server { } } + class InferredProject extends Project { + // Used to keep track of what directories are watched for this project + directoriesWatchedForTsconfig: string[] = []; + constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry) { + super(/*projectFilename*/ undefined, projectService, documentRegistry); + } + } + + class ConfiguredProject extends Project { + projectFileWatcher: FileWatcher; + directoryWatcher: FileWatcher; + /** Used for configured projects which may have multiple open roots */ + openRefCount = 0; + + constructor(projectFilename: string, projectService: ProjectService, documentRegistry: ts.DocumentRegistry, projectOptions: ProjectOptions) { + super(projectFilename, projectService, documentRegistry, projectOptions); + } + + addOpenRef() { + this.openRefCount++; + } + + deleteOpenRef() { + this.openRefCount--; + return this.openRefCount; + } + } + + export interface ProjectOpenResult { success?: boolean; errorMsg?: string; @@ -504,9 +511,9 @@ namespace ts.server { // open, non-configured root files openFileRoots: ScriptInfo[] = []; // projects built from openFileRoots - inferredProjects: Project[] = []; + inferredProjects: InferredProject[] = []; // projects specified by a tsconfig.json file - configuredProjects: Project[] = []; + configuredProjects: ConfiguredProject[] = []; // open files referenced by a project openFilesReferenced: ScriptInfo[] = []; // open files that are roots of a configured project @@ -673,7 +680,7 @@ namespace ts.server { } createInferredProject(root: ScriptInfo) { - const project = new Project(/*projectFilename*/ undefined, this, this.documentRegistry); + const project = new InferredProject(this, this.documentRegistry); project.addRoot(root); let currentPath = ts.getDirectoryPath(root.fileName); @@ -733,7 +740,7 @@ namespace ts.server { } updateConfiguredProjectList() { - const configuredProjects: Project[] = []; + const configuredProjects: ConfiguredProject[] = []; for (let i = 0, len = this.configuredProjects.length; i < len; i++) { if (this.configuredProjects[i].openRefCount > 0) { configuredProjects.push(this.configuredProjects[i]); @@ -745,12 +752,12 @@ namespace ts.server { removeProject(project: Project) { this.log("remove project: " + project.getRootFiles().toString()); if (project.isConfiguredProject()) { - project.projectFileWatcher.close(); - project.directoryWatcher.close(); - this.configuredProjects = copyListRemovingItem(project, this.configuredProjects); + (project).projectFileWatcher.close(); + (project).directoryWatcher.close(); + this.configuredProjects = copyListRemovingItem((project), this.configuredProjects); } else { - for (const directory of project.directoriesWatchedForTsconfig) { + for (const directory of (project).directoriesWatchedForTsconfig) { // if the ref count for this directory watcher drops to 0, it's time to close it project.projectService.directoryWatchersRefCount[directory]--; if (!project.projectService.directoryWatchersRefCount[directory]) { @@ -759,7 +766,7 @@ namespace ts.server { delete project.projectService.directoryWatchersForTsconfig[directory]; } } - this.inferredProjects = copyListRemovingItem(project, this.inferredProjects); + this.inferredProjects = copyListRemovingItem((project), this.inferredProjects); } const fileNames = project.getFileNames(); @@ -848,7 +855,7 @@ namespace ts.server { for (let i = 0, len = this.openFileRootsConfigured.length; i < len; i++) { if (info === this.openFileRootsConfigured[i]) { - if (info.defaultProject.deleteOpenRef() === 0) { + if ((info.defaultProject).deleteOpenRef() === 0) { removedProject = info.defaultProject; } } @@ -1246,13 +1253,13 @@ namespace ts.server { } - openConfigFile(configFilename: string, clientFileName?: string): { success: boolean, project?: Project, errors?: Diagnostic[] } { + openConfigFile(configFilename: string, clientFileName?: string): { success: boolean, project?: ConfiguredProject, errors?: Diagnostic[] } { const { succeeded, projectOptions, errors } = this.configFileToProjectOptions(configFilename); if (!succeeded) { return { success: false, errors }; } else { - const project = this.createProject(configFilename, projectOptions); + const project = new ConfiguredProject(configFilename, this, this.documentRegistry, projectOptions); let errors: Diagnostic[]; for (const rootFilename of projectOptions.files) { if (this.host.fileExists(rootFilename)) { @@ -1328,9 +1335,5 @@ namespace ts.server { } } } - - createProject(projectFilename: string, projectOptions?: ProjectOptions) { - return new Project(projectFilename, this, this.documentRegistry, projectOptions); - } } }