Allow creation of relative path file infos only for open script infos
Fixes #20476
This commit is contained in:
parent
41b19309d4
commit
55fb052d7a
2 changed files with 49 additions and 11 deletions
|
@ -2109,7 +2109,20 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName: NormalizedPath, currentDirectory: string, scriptKind: ScriptKind | undefined, hasMixedContent: boolean | undefined, hostToQueryFileExistsOn: DirectoryStructureHost | undefined) {
|
private 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);
|
if (isRootedDiskPath(fileName) || isDynamicFileName(fileName)) {
|
||||||
|
return this.getOrCreateScriptInfoWorker(fileName, currentDirectory, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is non rooted path with different current directory than project service current directory
|
||||||
|
// Only paths recognized are open relative file paths
|
||||||
|
const info = this.openFilesWithNonRootedDiskPath.get(this.toCanonicalFileName(fileName));
|
||||||
|
if (info) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This means triple slash references wont be resolved in dynamic and unsaved files
|
||||||
|
// which is intentional since we dont know what it means to be relative to non disk files
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, currentDirectory: string, fileContent: string | undefined, scriptKind: ScriptKind | undefined, hasMixedContent: boolean | undefined) {
|
private getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, currentDirectory: string, fileContent: string | undefined, scriptKind: ScriptKind | undefined, hasMixedContent: boolean | undefined) {
|
||||||
|
@ -2126,7 +2139,7 @@ namespace ts.server {
|
||||||
let info = this.getScriptInfoForPath(path);
|
let info = this.getScriptInfoForPath(path);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
const isDynamic = isDynamicFileName(fileName);
|
const isDynamic = isDynamicFileName(fileName);
|
||||||
Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nScript info with non-dynamic relative file name can only be open script info`);
|
Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nScript info with non-dynamic relative file name can only be open script info or in context of host currentDirectory`);
|
||||||
Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nOpen script files with non rooted disk path opened with current directory context cannot have same canonical names`);
|
Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nOpen script files with non rooted disk path opened with current directory context cannot have same canonical names`);
|
||||||
Debug.assert(!isDynamic || this.currentDirectory === currentDirectory, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nDynamic files must always have current directory context since containing external project name will always match the script info name.`);
|
Debug.assert(!isDynamic || this.currentDirectory === currentDirectory, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nDynamic files must always have current directory context since containing external project name will always match the script info name.`);
|
||||||
// If the file is not opened by client and the file doesnot exist on the disk, return
|
// If the file is not opened by client and the file doesnot exist on the disk, return
|
||||||
|
@ -2139,7 +2152,7 @@ namespace ts.server {
|
||||||
if (!openedByClient) {
|
if (!openedByClient) {
|
||||||
this.watchClosedScriptInfo(info);
|
this.watchClosedScriptInfo(info);
|
||||||
}
|
}
|
||||||
else if (!isRootedDiskPath(fileName) && currentDirectory !== this.currentDirectory) {
|
else if (!isRootedDiskPath(fileName) && !isDynamic) {
|
||||||
// File that is opened by user but isn't rooted disk path
|
// File that is opened by user but isn't rooted disk path
|
||||||
this.openFilesWithNonRootedDiskPath.set(this.toCanonicalFileName(fileName), info);
|
this.openFilesWithNonRootedDiskPath.set(this.toCanonicalFileName(fileName), info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3301,7 +3301,7 @@ namespace ts.projectSystem {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("dynamic file with reference paths external project", () => {
|
it("dynamic file with reference paths without external project", () => {
|
||||||
const file: File = {
|
const file: File = {
|
||||||
path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js",
|
path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js",
|
||||||
content: `/// <reference path="../../../../../../typings/@epic/Core.d.ts" />
|
content: `/// <reference path="../../../../../../typings/@epic/Core.d.ts" />
|
||||||
|
@ -3899,18 +3899,30 @@ var x = 10;`
|
||||||
|
|
||||||
describe("when opening new file that doesnt exist on disk yet", () => {
|
describe("when opening new file that doesnt exist on disk yet", () => {
|
||||||
function verifyNonExistentFile(useProjectRoot: boolean) {
|
function verifyNonExistentFile(useProjectRoot: boolean) {
|
||||||
const host = createServerHost([libFile]);
|
const folderPath = "/user/someuser/projects/someFolder";
|
||||||
|
const fileInRoot: File = {
|
||||||
|
path: `/src/somefile.d.ts`,
|
||||||
|
content: "class c { }"
|
||||||
|
};
|
||||||
|
const fileInProjectRoot: File = {
|
||||||
|
path: `${folderPath}/src/somefile.d.ts`,
|
||||||
|
content: "class c { }"
|
||||||
|
};
|
||||||
|
const host = createServerHost([libFile, fileInRoot, fileInProjectRoot]);
|
||||||
const { hasError, errorLogger } = createErrorLogger();
|
const { hasError, errorLogger } = createErrorLogger();
|
||||||
const session = createSession(host, { canUseEvents: true, logger: errorLogger, useInferredProjectPerProjectRoot: true });
|
const session = createSession(host, { canUseEvents: true, logger: errorLogger, useInferredProjectPerProjectRoot: true });
|
||||||
|
|
||||||
const folderPath = "/user/someuser/projects/someFolder";
|
|
||||||
const projectService = session.getProjectService();
|
const projectService = session.getProjectService();
|
||||||
const untitledFile = "untitled:Untitled-1";
|
const untitledFile = "untitled:Untitled-1";
|
||||||
|
const refPathNotFound1 = "../../../../../../typings/@epic/Core.d.ts";
|
||||||
|
const refPathNotFound2 = "./src/somefile.d.ts";
|
||||||
|
const fileContent = `/// <reference path="${refPathNotFound1}" />
|
||||||
|
/// <reference path="${refPathNotFound2}" />`;
|
||||||
session.executeCommandSeq<protocol.OpenRequest>({
|
session.executeCommandSeq<protocol.OpenRequest>({
|
||||||
command: server.CommandNames.Open,
|
command: server.CommandNames.Open,
|
||||||
arguments: {
|
arguments: {
|
||||||
file: untitledFile,
|
file: untitledFile,
|
||||||
fileContent: `/// <reference path="../../../../../../typings/@epic/Core.d.ts" />`,
|
fileContent,
|
||||||
scriptKindName: "TS",
|
scriptKindName: "TS",
|
||||||
projectRootPath: useProjectRoot ? folderPath : undefined
|
projectRootPath: useProjectRoot ? folderPath : undefined
|
||||||
}
|
}
|
||||||
|
@ -3918,6 +3930,8 @@ var x = 10;`
|
||||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||||
const infoForUntitledAtProjectRoot = projectService.getScriptInfoForPath(`${folderPath.toLowerCase()}/${untitledFile.toLowerCase()}` as Path);
|
const infoForUntitledAtProjectRoot = projectService.getScriptInfoForPath(`${folderPath.toLowerCase()}/${untitledFile.toLowerCase()}` as Path);
|
||||||
const infoForUnitiledAtRoot = projectService.getScriptInfoForPath(`/${untitledFile.toLowerCase()}` as Path);
|
const infoForUnitiledAtRoot = projectService.getScriptInfoForPath(`/${untitledFile.toLowerCase()}` as Path);
|
||||||
|
const infoForSomefileAtProjectRoot = projectService.getScriptInfoForPath(`/${folderPath.toLowerCase()}/src/somefile.d.ts` as Path);
|
||||||
|
const infoForSomefileAtRoot = projectService.getScriptInfoForPath(`${fileInRoot.path.toLowerCase()}` as Path);
|
||||||
if (useProjectRoot) {
|
if (useProjectRoot) {
|
||||||
assert.isDefined(infoForUntitledAtProjectRoot);
|
assert.isDefined(infoForUntitledAtProjectRoot);
|
||||||
assert.isUndefined(infoForUnitiledAtRoot);
|
assert.isUndefined(infoForUnitiledAtRoot);
|
||||||
|
@ -3926,7 +3940,11 @@ var x = 10;`
|
||||||
assert.isDefined(infoForUnitiledAtRoot);
|
assert.isDefined(infoForUnitiledAtRoot);
|
||||||
assert.isUndefined(infoForUntitledAtProjectRoot);
|
assert.isUndefined(infoForUntitledAtProjectRoot);
|
||||||
}
|
}
|
||||||
host.checkTimeoutQueueLength(2);
|
assert.isUndefined(infoForSomefileAtRoot);
|
||||||
|
assert.isUndefined(infoForSomefileAtProjectRoot);
|
||||||
|
|
||||||
|
// Since this is not js project so no typings are queued
|
||||||
|
host.checkTimeoutQueueLength(0);
|
||||||
|
|
||||||
const newTimeoutId = host.getNextTimeoutId();
|
const newTimeoutId = host.getNextTimeoutId();
|
||||||
const expectedSequenceId = session.getNextSeq();
|
const expectedSequenceId = session.getNextSeq();
|
||||||
|
@ -3937,19 +3955,26 @@ var x = 10;`
|
||||||
files: [untitledFile]
|
files: [untitledFile]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
host.checkTimeoutQueueLength(3);
|
host.checkTimeoutQueueLength(1);
|
||||||
|
|
||||||
// Run the last one = get error request
|
// Run the last one = get error request
|
||||||
host.runQueuedTimeoutCallbacks(newTimeoutId);
|
host.runQueuedTimeoutCallbacks(newTimeoutId);
|
||||||
|
|
||||||
assert.isFalse(hasError());
|
assert.isFalse(hasError());
|
||||||
host.checkTimeoutQueueLength(2);
|
host.checkTimeoutQueueLength(0);
|
||||||
checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] });
|
checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] });
|
||||||
session.clearMessages();
|
session.clearMessages();
|
||||||
|
|
||||||
host.runQueuedImmediateCallbacks();
|
host.runQueuedImmediateCallbacks();
|
||||||
assert.isFalse(hasError());
|
assert.isFalse(hasError());
|
||||||
checkErrorMessage(session, "semanticDiag", { file: untitledFile, diagnostics: [] });
|
const errorOffset = fileContent.indexOf(refPathNotFound1) + 1;
|
||||||
|
checkErrorMessage(session, "semanticDiag", {
|
||||||
|
file: untitledFile,
|
||||||
|
diagnostics: [
|
||||||
|
createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"),
|
||||||
|
createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error")
|
||||||
|
]
|
||||||
|
});
|
||||||
session.clearMessages();
|
session.clearMessages();
|
||||||
|
|
||||||
host.runQueuedImmediateCallbacks(1);
|
host.runQueuedImmediateCallbacks(1);
|
||||||
|
|
Loading…
Reference in a new issue