Handle the script infos that are opened with non rooted disk path

Fixes #19588
This commit is contained in:
Sheetal Nandi
2017-11-03 15:28:28 -07:00
parent 11d46130ae
commit 2cb14e5e8e
5 changed files with 52 additions and 22 deletions
@@ -2837,8 +2837,9 @@ namespace ts.projectSystem {
// Run the last one = get error request
host.runQueuedTimeoutCallbacks(newTimeoutId);
host.checkTimeoutQueueLength(2);
assert.isFalse(hasError);
host.checkTimeoutQueueLength(2);
checkErrorMessage(host, "syntaxDiag", { file: untitledFile, diagnostics: [] });
host.clearOutput();
+34 -13
View File
@@ -355,6 +355,10 @@ namespace ts.server {
* list of open files
*/
readonly openFiles: ScriptInfo[] = [];
/**
* Map of open files that are opened without complete path but have projectRoot as current directory
*/
private readonly openFilesWithNonRootedDiskPath = createMap<ScriptInfo>();
private compilerOptionsForInferredProjects: CompilerOptions;
private compilerOptionsForInferredProjectsPerProjectRoot = createMap<CompilerOptions>();
@@ -932,12 +936,15 @@ namespace ts.server {
// Closing file should trigger re-reading the file content from disk. This is
// because the user may chose to discard the buffer content before saving
// to the disk, and the server's version of the file can be out of sync.
info.close();
const fileExists = this.host.fileExists(info.fileName);
info.close(fileExists);
this.stopWatchingConfigFilesForClosedScriptInfo(info);
unorderedRemoveItem(this.openFiles, info);
const fileExists = this.host.fileExists(info.fileName);
const canonicalFileName = this.toCanonicalFileName(info.fileName);
if (this.openFilesWithNonRootedDiskPath.get(canonicalFileName) === info) {
this.openFilesWithNonRootedDiskPath.delete(canonicalFileName);
}
// collect all projects that should be removed
let projectsToRemove: Project[];
@@ -1518,7 +1525,7 @@ namespace ts.server {
else {
const scriptKind = propertyReader.getScriptKind(f, this.hostConfiguration.extraFileExtensions);
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
scriptInfo = this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(normalizedPath, scriptKind, hasMixedContent, project.directoryStructureHost);
scriptInfo = this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(normalizedPath, project.currentDirectory, scriptKind, hasMixedContent, project.directoryStructureHost);
path = scriptInfo.path;
// If this script info is not already a root add it
if (!project.isRoot(scriptInfo)) {
@@ -1672,9 +1679,9 @@ namespace ts.server {
}
/*@internal*/
getOrCreateScriptInfoNotOpenedByClient(uncheckedFileName: string, hostToQueryFileExistsOn: DirectoryStructureHost) {
getOrCreateScriptInfoNotOpenedByClient(uncheckedFileName: string, currentDirectory: string, hostToQueryFileExistsOn: DirectoryStructureHost) {
return this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(
toNormalizedPath(uncheckedFileName), /*scriptKind*/ undefined,
toNormalizedPath(uncheckedFileName), currentDirectory, /*scriptKind*/ undefined,
/*hasMixedContent*/ undefined, hostToQueryFileExistsOn
);
}
@@ -1705,20 +1712,26 @@ namespace ts.server {
}
/*@internal*/
getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName: NormalizedPath, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) {
return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName: NormalizedPath, currentDirectory: string, scriptKind: ScriptKind | undefined, hasMixedContent: boolean | undefined, hostToQueryFileExistsOn: DirectoryStructureHost | undefined) {
return this.getOrCreateScriptInfoWorker(fileName, currentDirectory, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
}
/*@internal*/
getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) {
return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, currentDirectory: string, fileContent: string | undefined, scriptKind: ScriptKind | undefined, hasMixedContent: boolean | undefined) {
return this.getOrCreateScriptInfoWorker(fileName, currentDirectory, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
}
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) {
return this.getOrCreateScriptInfoWorker(fileName, this.currentDirectory, openedByClient, fileContent, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
}
private getOrCreateScriptInfoWorker(fileName: NormalizedPath, currentDirectory: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) {
Debug.assert(fileContent === undefined || openedByClient, "ScriptInfo needs to be opened by client to be able to set its user defined content");
const path = normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName);
const path = normalizedPathToPath(fileName, currentDirectory, this.toCanonicalFileName);
let info = this.getScriptInfoForPath(path);
if (!info) {
Debug.assert(isRootedDiskPath(fileName) || openedByClient, "Script info with relative file name can only be open script info");
Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "Open script files with non rooted disk path opened with current directory context cannot have same canonical names");
const isDynamic = isDynamicFileName(fileName);
// If the file is not opened by client and the file doesnot exist on the disk, return
if (!openedByClient && !isDynamic && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) {
@@ -1729,6 +1742,10 @@ namespace ts.server {
if (!openedByClient) {
this.watchClosedScriptInfo(info);
}
else if (!isRootedDiskPath(fileName) && currentDirectory !== this.currentDirectory) {
// File that is opened by user but isn't rooted disk path
this.openFilesWithNonRootedDiskPath.set(this.toCanonicalFileName(fileName), info);
}
}
if (openedByClient && !info.isScriptOpen()) {
// Opening closed script info
@@ -1745,8 +1762,12 @@ namespace ts.server {
return info;
}
/**
* This gets the script info for the normalized path. If the path is not rooted disk path then the open script info with project root context is preferred
*/
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
return this.getScriptInfoForPath(normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName));
return !isRootedDiskPath(fileName) && this.openFilesWithNonRootedDiskPath.get(this.toCanonicalFileName(fileName)) ||
this.getScriptInfoForPath(normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName));
}
getScriptInfoForPath(fileName: Path) {
@@ -1926,7 +1947,7 @@ namespace ts.server {
let sendConfigFileDiagEvent = false;
let configFileErrors: ReadonlyArray<Diagnostic>;
const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, fileContent, scriptKind, hasMixedContent);
const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, projectRootPath ? this.getNormalizedAbsolutePath(projectRootPath) : this.currentDirectory, fileContent, scriptKind, hasMixedContent);
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
if (!project) {
configFileName = this.getConfigFileNameForFile(info, projectRootPath);
+5 -5
View File
@@ -287,7 +287,7 @@ namespace ts.server {
}
private getOrCreateScriptInfoAndAttachToProject(fileName: string) {
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.directoryStructureHost);
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.currentDirectory, this.directoryStructureHost);
if (scriptInfo) {
const existingValue = this.rootFilesMap.get(scriptInfo.path);
if (existingValue !== scriptInfo && existingValue !== undefined) {
@@ -367,7 +367,7 @@ namespace ts.server {
/*@internal*/
toPath(fileName: string) {
return this.projectService.toPath(fileName);
return toPath(fileName, this.currentDirectory, this.projectService.toCanonicalFileName);
}
/*@internal*/
@@ -660,7 +660,7 @@ namespace ts.server {
}
containsFile(filename: NormalizedPath, requireOpen?: boolean) {
const info = this.projectService.getScriptInfoForNormalizedPath(filename);
const info = this.projectService.getScriptInfoForPath(this.toPath(filename));
if (info && (info.isScriptOpen() || !requireOpen)) {
return this.containsScriptInfo(info);
}
@@ -857,7 +857,7 @@ namespace ts.server {
// by the LSHost for files in the program when the program is retrieved above but
// the program doesn't contain external files so this must be done explicitly.
inserted => {
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(inserted, this.directoryStructureHost);
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(inserted, this.currentDirectory, this.directoryStructureHost);
scriptInfo.attachToProject(this);
},
removed => this.detachScriptInfoFromProject(removed)
@@ -903,7 +903,7 @@ namespace ts.server {
}
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(fileName);
const scriptInfo = this.projectService.getScriptInfoForPath(this.toPath(fileName));
if (scriptInfo && !scriptInfo.isAttached(this)) {
return Errors.ThrowProjectDoesNotContainDocument(fileName, this);
}
+2 -2
View File
@@ -248,9 +248,9 @@ namespace ts.server {
}
}
public close() {
public close(fileExists = true) {
this.textStorage.isOpen = false;
if (this.isDynamicOrHasMixedContent()) {
if (this.isDynamicOrHasMixedContent() || !fileExists) {
if (this.textStorage.reload("")) {
this.markContainingProjectsAsDirty();
}
+9 -1
View File
@@ -7031,7 +7031,7 @@ declare namespace ts.server {
constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path);
isScriptOpen(): boolean;
open(newText: string): void;
close(): void;
close(fileExists?: boolean): void;
getSnapshot(): IScriptSnapshot;
getFormatCodeSettings(): FormatCodeSettings;
attachToProject(project: Project): boolean;
@@ -7459,6 +7459,10 @@ declare namespace ts.server {
* list of open files
*/
readonly openFiles: ScriptInfo[];
/**
* Map of open files that are opened without complete path but have projectRoot as current directory
*/
private readonly openFilesWithNonRootedDiskPath;
private compilerOptionsForInferredProjects;
private compilerOptionsForInferredProjectsPerProjectRoot;
/**
@@ -7598,6 +7602,10 @@ declare namespace ts.server {
private watchClosedScriptInfo(info);
private stopWatchingScriptInfo(info);
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost): ScriptInfo;
private getOrCreateScriptInfoWorker(fileName, currentDirectory, openedByClient, fileContent?, scriptKind?, hasMixedContent?, hostToQueryFileExistsOn?);
/**
* This gets the script info for the normalized path. If the path is not rooted disk path then the open script info with project root context is preferred
*/
getScriptInfoForNormalizedPath(fileName: NormalizedPath): ScriptInfo;
getScriptInfoForPath(fileName: Path): ScriptInfo;
setHostConfiguration(args: protocol.ConfigureRequestArguments): void;