From 5665c098dab4429eb66744884315bef80b5e4486 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Mon, 14 Aug 2017 16:55:37 -0700 Subject: [PATCH 1/2] Introduce the concept of a Dynamic File Dynamic files are generally created by the debugger when while debugging it can't find a matching file on disk. Since these files don't exist on disk, we shouldn't check if the file exists on disk, and allow the content to be controlled by the host. --- src/server/builder.ts | 6 +++--- src/server/editorServices.ts | 28 +++++++++++++++++----------- src/server/scriptInfo.ts | 11 ++++++----- src/server/utilities.ts | 8 ++++---- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/server/builder.ts b/src/server/builder.ts index 8a10682b4c..5cf65611fb 100644 --- a/src/server/builder.ts +++ b/src/server/builder.ts @@ -5,7 +5,7 @@ namespace ts.server { export function shouldEmitFile(scriptInfo: ScriptInfo) { - return !scriptInfo.hasMixedContent; + return !scriptInfo.hasMixedContent && !scriptInfo.isDynamic; } /** @@ -188,7 +188,7 @@ namespace ts.server { */ getFilesAffectedBy(scriptInfo: ScriptInfo): string[] { const info = this.getOrCreateFileInfo(scriptInfo.path); - const singleFileResult = scriptInfo.hasMixedContent ? [] : [scriptInfo.fileName]; + const singleFileResult = scriptInfo.hasMixedContent || scriptInfo.isDynamic ? [] : [scriptInfo.fileName]; if (info.updateShapeSignature()) { const options = this.project.getCompilerOptions(); // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, @@ -303,7 +303,7 @@ namespace ts.server { getFilesAffectedBy(scriptInfo: ScriptInfo): string[] { this.ensureProjectDependencyGraphUpToDate(); - const singleFileResult = scriptInfo.hasMixedContent ? [] : [scriptInfo.fileName]; + const singleFileResult = scriptInfo.hasMixedContent || scriptInfo.isDynamic ? [] : [scriptInfo.fileName]; const fileInfo = this.getFileInfo(scriptInfo.path); if (!fileInfo || !fileInfo.updateShapeSignature()) { return singleFileResult; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index fe7946fc0f..6d0f8d41e6 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -234,18 +234,21 @@ namespace ts.server { getFileName(f: T): string; getScriptKind(f: T): ScriptKind; hasMixedContent(f: T, extraFileExtensions: JsFileExtensionInfo[]): boolean; + isDynamicFile(f: T): boolean; } const fileNamePropertyReader: FilePropertyReader = { getFileName: x => x, getScriptKind: _ => undefined, hasMixedContent: (fileName, extraFileExtensions) => some(extraFileExtensions, ext => ext.isMixedContent && fileExtensionIs(fileName, ext.extension)), + isDynamicFile: x => x[0] == '^', }; const externalFilePropertyReader: FilePropertyReader = { getFileName: x => x.fileName, getScriptKind: x => tryConvertScriptKindName(x.scriptKind), - hasMixedContent: x => x.hasMixedContent + hasMixedContent: x => x.hasMixedContent, + isDynamicFile: x => x.fileName[0] == '^', }; function findProjectByName(projectName: string, projects: T[]): T { @@ -1177,15 +1180,16 @@ namespace ts.server { private addFilesToProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: ReadonlyArray): void { let errors: Diagnostic[]; for (const f of files) { - const rootFilename = propertyReader.getFileName(f); + const rootFileName = propertyReader.getFileName(f); const scriptKind = propertyReader.getScriptKind(f); const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions); - if (this.host.fileExists(rootFilename)) { - const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFilename), /*openedByClient*/ clientFileName === rootFilename, /*fileContent*/ undefined, scriptKind, hasMixedContent); + const isDynamicFile = propertyReader.isDynamicFile(f); + if (isDynamicFile || this.host.fileExists(rootFileName)) { + const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFileName), /*openedByClient*/ clientFileName === rootFileName, /*fileContent*/ undefined, scriptKind, hasMixedContent, isDynamicFile); project.addRoot(info); } else { - (errors || (errors = [])).push(createFileNotFoundDiagnostic(rootFilename)); + (errors || (errors = [])).push(createFileNotFoundDiagnostic(rootFileName)); } } project.setProjectErrors(concatenate(configFileErrors, errors)); @@ -1215,7 +1219,8 @@ namespace ts.server { let rootFilesChanged = false; for (const f of newUncheckedFiles) { const newRootFile = propertyReader.getFileName(f); - if (!this.host.fileExists(newRootFile)) { + const isDynamic = propertyReader.isDynamicFile(f); + if (!isDynamic && !this.host.fileExists(newRootFile)) { (projectErrors || (projectErrors = [])).push(createFileNotFoundDiagnostic(newRootFile)); continue; } @@ -1226,7 +1231,7 @@ namespace ts.server { if (!scriptInfo) { const scriptKind = propertyReader.getScriptKind(f); const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions); - scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent); + scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, isDynamic); } } newRootScriptInfos.push(scriptInfo); @@ -1410,17 +1415,17 @@ namespace ts.server { watchClosedScriptInfo(info: ScriptInfo) { // do not watch files with mixed content - server doesn't know how to interpret it - if (!info.hasMixedContent) { + if (!info.hasMixedContent && !info.isDynamic) { const { fileName } = info; info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName))); } } - getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean) { + getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, isDynamic?: boolean) { let info = this.getScriptInfoForNormalizedPath(fileName); if (!info) { - if (openedByClient || this.host.fileExists(fileName)) { - info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent); + if (openedByClient || isDynamic || this.host.fileExists(fileName)) { + info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, isDynamic); this.filenameToScriptInfo.set(info.path, info); @@ -1430,6 +1435,7 @@ namespace ts.server { fileContent = this.host.readFile(fileName) || ""; } } + else { this.watchClosedScriptInfo(info); } diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 4d73fc4740..a8ba9f5d4c 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -156,11 +156,12 @@ namespace ts.server { private readonly host: ServerHost, readonly fileName: NormalizedPath, readonly scriptKind: ScriptKind, - public hasMixedContent = false) { + public hasMixedContent = false, + public isDynamic = false) { this.path = toPath(fileName, host.getCurrentDirectory(), createGetCanonicalFileName(host.useCaseSensitiveFileNames)); this.textStorage = new TextStorage(host, fileName); - if (hasMixedContent) { + if (hasMixedContent || isDynamic) { this.textStorage.reload(""); } this.scriptKind = scriptKind @@ -180,7 +181,7 @@ namespace ts.server { public close() { this.isOpen = false; - this.textStorage.useText(this.hasMixedContent ? "" : undefined); + this.textStorage.useText(this.hasMixedContent || this.isDynamic ? "" : undefined); this.markContainingProjectsAsDirty(); } @@ -307,7 +308,7 @@ namespace ts.server { } reloadFromFile(tempFileName?: NormalizedPath) { - if (this.hasMixedContent) { + if (this.hasMixedContent || this.isDynamic) { this.reload(""); } else { @@ -354,4 +355,4 @@ namespace ts.server { return this.scriptKind === ScriptKind.JS || this.scriptKind === ScriptKind.JSX; } } -} \ No newline at end of file +} diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 0d4bc101ff..7a5dbdf7cc 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -84,7 +84,7 @@ namespace ts.server { }; } - export function mergeMapLikes(target: MapLike, source: MapLike ): void { + export function mergeMapLikes(target: MapLike, source: MapLike): void { for (const key in source) { if (hasProperty(source, key)) { target[key] = source[key]; @@ -115,9 +115,9 @@ namespace ts.server { } export function createNormalizedPathMap(): NormalizedPathMap { -/* tslint:disable:no-null-keyword */ + /* tslint:disable:no-null-keyword */ const map = createMap(); -/* tslint:enable:no-null-keyword */ + /* tslint:enable:no-null-keyword */ return { get(path) { return map.get(path); @@ -290,4 +290,4 @@ namespace ts.server { deleted(oldItems[oldIndex++]); } } -} \ No newline at end of file +} From b07fd3e1809fcde8f8360349b913cedf838181d9 Mon Sep 17 00:00:00 2001 From: Paul van Brenk Date: Tue, 15 Aug 2017 12:27:02 -0700 Subject: [PATCH 2/2] Fix linter issues --- src/server/editorServices.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 6d0f8d41e6..19953c9a79 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -241,14 +241,14 @@ namespace ts.server { getFileName: x => x, getScriptKind: _ => undefined, hasMixedContent: (fileName, extraFileExtensions) => some(extraFileExtensions, ext => ext.isMixedContent && fileExtensionIs(fileName, ext.extension)), - isDynamicFile: x => x[0] == '^', + isDynamicFile: x => x[0] === "^", }; const externalFilePropertyReader: FilePropertyReader = { getFileName: x => x.fileName, getScriptKind: x => tryConvertScriptKindName(x.scriptKind), hasMixedContent: x => x.hasMixedContent, - isDynamicFile: x => x.fileName[0] == '^', + isDynamicFile: x => x.fileName[0] === "^", }; function findProjectByName(projectName: string, projects: T[]): T {