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.
This commit is contained in:
Paul van Brenk
2017-08-14 16:55:37 -07:00
parent 80a7ed9a42
commit 5665c098da
4 changed files with 30 additions and 23 deletions

View File

@@ -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;

View File

@@ -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<string> = {
getFileName: x => x,
getScriptKind: _ => undefined,
hasMixedContent: (fileName, extraFileExtensions) => some(extraFileExtensions, ext => ext.isMixedContent && fileExtensionIs(fileName, ext.extension)),
isDynamicFile: x => x[0] == '^',
};
const externalFilePropertyReader: FilePropertyReader<protocol.ExternalFile> = {
getFileName: x => x.fileName,
getScriptKind: x => tryConvertScriptKindName(x.scriptKind),
hasMixedContent: x => x.hasMixedContent
hasMixedContent: x => x.hasMixedContent,
isDynamicFile: x => x.fileName[0] == '^',
};
function findProjectByName<T extends Project>(projectName: string, projects: T[]): T {
@@ -1177,15 +1180,16 @@ namespace ts.server {
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: ReadonlyArray<Diagnostic>): 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);
}

View File

@@ -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;
}
}
}
}

View File

@@ -84,7 +84,7 @@ namespace ts.server {
};
}
export function mergeMapLikes(target: MapLike<any>, source: MapLike <any>): void {
export function mergeMapLikes(target: MapLike<any>, source: MapLike<any>): void {
for (const key in source) {
if (hasProperty(source, key)) {
target[key] = source[key];
@@ -115,9 +115,9 @@ namespace ts.server {
}
export function createNormalizedPathMap<T>(): NormalizedPathMap<T> {
/* tslint:disable:no-null-keyword */
/* tslint:disable:no-null-keyword */
const map = createMap<T>();
/* 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++]);
}
}
}
}