From d52894302abffb97fa5c949e66a5986e14b80ba1 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Thu, 8 Dec 2016 17:56:08 -0800 Subject: [PATCH] Changes due to CR comments --- src/compiler/commandLineParser.ts | 4 ++-- src/compiler/core.ts | 8 +++---- src/compiler/types.ts | 8 +++---- .../unittests/tsserverProjectSystem.ts | 3 ++- src/server/editorServices.ts | 21 +++++++++-------- src/server/project.ts | 23 +++++++++++-------- src/server/protocol.ts | 2 +- src/server/scriptInfo.ts | 8 +++++-- 8 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 4dcee48d692..9c7b6f48b3a 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -841,7 +841,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = [], fileExtensionMap: FileExtensionMap = {}): ParsedCommandLine { + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = [], fileExtensionMap: FileExtensionMapItem[] = []): ParsedCommandLine { const errors: Diagnostic[] = []; const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName); @@ -1185,7 +1185,7 @@ namespace ts { * @param host The host used to resolve files and directories. * @param errors An array for diagnostic reporting. */ - function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], fileExtensionMap: FileExtensionMap): ExpandResult { + function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], fileExtensionMap: FileExtensionMapItem[]): ExpandResult { basePath = normalizePath(basePath); // The exclude spec list is converted into a regular expression, which allows us to quickly diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 16b42d5084e..f1cf4ffcaa9 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1942,12 +1942,12 @@ namespace ts { export const supportedJavascriptExtensions = [".js", ".jsx"]; const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); - export function getSupportedExtensions(options?: CompilerOptions, fileExtensionMap?: FileExtensionMap): string[] { + export function getSupportedExtensions(options?: CompilerOptions, fileExtensionMap?: FileExtensionMapItem[]): string[] { let typeScriptHostExtensions: string[] = []; let allHostExtensions: string[] = []; if (fileExtensionMap) { - allHostExtensions = concatenate(concatenate(fileExtensionMap.javaScript, fileExtensionMap.typeScript), fileExtensionMap.mixedContent); - typeScriptHostExtensions = fileExtensionMap.typeScript; + allHostExtensions = ts.map(fileExtensionMap, item => item.extension); + typeScriptHostExtensions = ts.map(ts.filter(fileExtensionMap, item => item.scriptKind === ScriptKind.TS), item => item.extension); } const allTypeScriptExtensions = concatenate(supportedTypeScriptExtensions, typeScriptHostExtensions); const allExtensions = concatenate(allSupportedExtensions, allHostExtensions); @@ -1962,7 +1962,7 @@ namespace ts { return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension)); } - export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, fileExtensionMap?: FileExtensionMap) { + export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, fileExtensionMap?: FileExtensionMapItem[]) { if (!fileName) { return false; } for (const extension of getSupportedExtensions(compilerOptions, fileExtensionMap)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 97eb145916a..3f1a5465c87 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3086,10 +3086,10 @@ namespace ts { ThisProperty } - export interface FileExtensionMap { - javaScript?: string[]; - typeScript?: string[]; - mixedContent?: string[]; + export interface FileExtensionMapItem { + extension: string; + scriptKind: ScriptKind; + isMixedContent: boolean; } export interface DiagnosticMessage { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 7218b419f7c..c3ced46e453 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1552,7 +1552,8 @@ namespace ts.projectSystem { checkProjectActualFiles(projectService.configuredProjects[0], [file1.path]); // Specify .html extension as mixed content - const configureHostRequest = makeSessionRequest(CommandNames.Configure, { fileExtensionMap: { mixedContent: [".html"] } }); + const fileExtensionMap = [{ extension: ".html", scriptKind: ScriptKind.JS, isMixedContent: true }]; + const configureHostRequest = makeSessionRequest(CommandNames.Configure, { fileExtensionMap }); session.executeCommand(configureHostRequest).response; // HTML file still not included in the project as it is closed diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 44381e9e19f..69d7d607641 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -108,7 +108,7 @@ namespace ts.server { export interface HostConfiguration { formatCodeOptions: FormatCodeSettings; hostInfo: string; - fileExtensionMap?: FileExtensionMap; + fileExtensionMap?: FileExtensionMapItem[]; } interface ConfigFileConversionResult { @@ -133,13 +133,16 @@ namespace ts.server { interface FilePropertyReader { getFileName(f: T): string; getScriptKind(f: T): ScriptKind; - hasMixedContent(f: T, mixedContentExtensions: string[]): boolean; + hasMixedContent(f: T, fileExtensionMap: FileExtensionMapItem[]): boolean; } const fileNamePropertyReader: FilePropertyReader = { getFileName: x => x, getScriptKind: _ => undefined, - hasMixedContent: (fileName, mixedContentExtensions) => forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension)) + hasMixedContent: (fileName, fileExtensionMap) => { + const mixedContentExtensions = ts.map(ts.filter(fileExtensionMap, item => item.isMixedContent), item => item.extension); + return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension)) + } }; const externalFilePropertyReader: FilePropertyReader = { @@ -284,7 +287,7 @@ namespace ts.server { this.hostConfiguration = { formatCodeOptions: getDefaultFormatCodeSettings(this.host), hostInfo: "Unknown host", - fileExtensionMap: {} + fileExtensionMap: [] }; this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames, host.getCurrentDirectory()); @@ -646,7 +649,7 @@ namespace ts.server { for (const p of info.containingProjects) { if (p.projectKind === ProjectKind.Configured) { if (info.hasMixedContent) { - info.hasChanges = true; + info.registerFileUpdate(); } // last open file in configured project - close it if ((p).deleteOpenRef() === 0) { @@ -922,7 +925,7 @@ namespace ts.server { for (const f of files) { const rootFilename = propertyReader.getFileName(f); const scriptKind = propertyReader.getScriptKind(f); - const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.fileExtensionMap.mixedContent); + const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.fileExtensionMap); if (this.host.fileExists(rootFilename)) { const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFilename), /*openedByClient*/ clientFileName == rootFilename, /*fileContent*/ undefined, scriptKind, hasMixedContent); project.addRoot(info); @@ -968,7 +971,7 @@ namespace ts.server { rootFilesChanged = true; if (!scriptInfo) { const scriptKind = propertyReader.getScriptKind(f); - const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.fileExtensionMap.mixedContent); + const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.fileExtensionMap); scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent); } } @@ -1123,7 +1126,7 @@ namespace ts.server { if (openedByClient) { info.isOpen = true; if (hasMixedContent) { - info.hasChanges = true; + info.registerFileUpdate(); } } } @@ -1225,12 +1228,12 @@ namespace ts.server { } openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean): OpenConfiguredProjectResult { - const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent); const { configFileName = undefined, configFileErrors = undefined }: OpenConfiguredProjectResult = this.findContainingExternalProject(fileName) ? {} : this.openOrUpdateConfiguredProjectForFile(fileName); // at this point if file is the part of some configured/external project then this project should be created + const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent); this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true); this.printProjects(); return { configFileName, configFileErrors }; diff --git a/src/server/project.ts b/src/server/project.ts index 9df839aad28..bf1bd6a9fb9 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -187,6 +187,10 @@ namespace ts.server { public languageServiceEnabled = true; builder: Builder; + /** + * Set of files names that were updated since the last call to getChangesSinceVersion. + */ + private updatedFileNames: Map; /** * Set of files that was returned from the last call to getChangesSinceVersion. */ @@ -208,6 +212,7 @@ namespace ts.server { */ private projectStateVersion = 0; + private typingFiles: SortedReadonlyArray; protected projectErrors: Diagnostic[]; @@ -480,6 +485,10 @@ namespace ts.server { this.markAsDirty(); } + registerFileUpdate(fileName: string) { + (this.updatedFileNames || (this.updatedFileNames = createMap()))[fileName] = fileName; + } + markAsDirty() { this.projectStateVersion++; } @@ -562,10 +571,6 @@ namespace ts.server { return !hasChanges; } - private hasChangedFiles() { - return this.rootFiles && forEach(this.rootFiles, info => info.hasChanges); - } - private setTypings(typings: SortedReadonlyArray): boolean { if (arrayIsEqualTo(this.typingFiles, typings)) { return false; @@ -579,7 +584,7 @@ namespace ts.server { const oldProgram = this.program; this.program = this.languageService.getProgram(); - let hasChanges = this.hasChangedFiles(); + let hasChanges = false; // bump up the version if // - oldProgram is not set - this is a first time updateGraph is called // - newProgram is different from the old program and structure of the old program was not reused. @@ -674,7 +679,7 @@ namespace ts.server { // check if requested version is the same that we have reported last time if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) { // if current structure version is the same - return info witout any changes - if (this.projectStructureVersion == this.lastReportedVersion) { + if (this.projectStructureVersion == this.lastReportedVersion && !this.updatedFileNames) { return { info, projectErrors: this.projectErrors }; } // compute and return the difference @@ -683,7 +688,7 @@ namespace ts.server { const added: string[] = []; const removed: string[] = []; - const updated = this.rootFiles.filter(info => info.hasChanges).map(info => info.fileName); + const updated: string[] = getOwnKeys(this.updatedFileNames); for (const id in currentFiles) { if (!hasProperty(lastReportedFileNames, id)) { added.push(id); @@ -694,11 +699,9 @@ namespace ts.server { removed.push(id); } } - for (const root of this.rootFiles) { - root.hasChanges = false; - } this.lastReportedFileNames = currentFiles; this.lastReportedVersion = this.projectStructureVersion; + this.updatedFileNames = undefined; return { info, changes: { added, removed, updated }, projectErrors: this.projectErrors }; } else { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 2e721dc8967..6df390344cc 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -998,7 +998,7 @@ namespace ts.server.protocol { /** * The host's supported file extension mappings */ - fileExtensionMap?: FileExtensionMap; + fileExtensionMap?: FileExtensionMapItem[]; } /** diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index e5e1c3577e0..ee3cfb0e137 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -28,8 +28,6 @@ namespace ts.server { : getScriptKindFromFileName(fileName); } - public hasChanges = false; - getFormatCodeSettings() { return this.formatCodeSettings; } @@ -91,6 +89,12 @@ namespace ts.server { return this.containingProjects[0]; } + registerFileUpdate(): void { + for (const p of this.containingProjects) { + p.registerFileUpdate(this.path); + } + } + setFormatOptions(formatSettings: FormatCodeSettings): void { if (formatSettings) { if (!this.formatCodeSettings) {