Merge pull request #28400 from Microsoft/gotoDefinition
Fix the issue with file being included in the referencing project on rename when it wasnt included earlier
This commit is contained in:
commit
15af7e17c6
3 changed files with 142 additions and 75 deletions
|
@ -83,7 +83,7 @@ namespace ts.sourcemaps {
|
||||||
if (!maps[targetIndex] || comparePaths(loc.fileName, maps[targetIndex].sourcePath, sourceRoot, !host.useCaseSensitiveFileNames) !== 0) {
|
if (!maps[targetIndex] || comparePaths(loc.fileName, maps[targetIndex].sourcePath, sourceRoot, !host.useCaseSensitiveFileNames) !== 0) {
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
return { fileName: toPath(map.file!, sourceRoot, host.getCanonicalFileName), position: maps[targetIndex].emittedPosition }; // Closest pos
|
return { fileName: getNormalizedAbsolutePath(map.file!, sourceRoot), position: maps[targetIndex].emittedPosition }; // Closest pos
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOriginalPosition(loc: SourceMappableLocation): SourceMappableLocation {
|
function getOriginalPosition(loc: SourceMappableLocation): SourceMappableLocation {
|
||||||
|
@ -94,13 +94,13 @@ namespace ts.sourcemaps {
|
||||||
// if no exact match, closest is 2's compliment of result
|
// if no exact match, closest is 2's compliment of result
|
||||||
targetIndex = ~targetIndex;
|
targetIndex = ~targetIndex;
|
||||||
}
|
}
|
||||||
return { fileName: toPath(maps[targetIndex].sourcePath, sourceRoot, host.getCanonicalFileName), position: maps[targetIndex].sourcePosition }; // Closest pos
|
return { fileName: getNormalizedAbsolutePath(maps[targetIndex].sourcePath, sourceRoot), position: maps[targetIndex].sourcePosition }; // Closest pos
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceFileLike(fileName: string, location: string): SourceFileLike | undefined {
|
function getSourceFileLike(fileName: string, location: string): SourceFileLike | undefined {
|
||||||
// Lookup file in program, if provided
|
// Lookup file in program, if provided
|
||||||
const path = toPath(fileName, location, host.getCanonicalFileName);
|
const path = toPath(fileName, location, host.getCanonicalFileName);
|
||||||
const file = program && program.getSourceFile(path);
|
const file = program && program.getSourceFileByPath(path);
|
||||||
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
|
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
|
||||||
if (!file || file.resolvedPath !== path) {
|
if (!file || file.resolvedPath !== path) {
|
||||||
// Otherwise check the cache (which may hit disk)
|
// Otherwise check the cache (which may hit disk)
|
||||||
|
|
|
@ -23,8 +23,12 @@ namespace ts {
|
||||||
let sourcemappedFileCache: SourceFileLikeCache;
|
let sourcemappedFileCache: SourceFileLikeCache;
|
||||||
return { tryGetOriginalLocation, tryGetGeneratedLocation, toLineColumnOffset, clearCache };
|
return { tryGetOriginalLocation, tryGetGeneratedLocation, toLineColumnOffset, clearCache };
|
||||||
|
|
||||||
|
function toPath(fileName: string) {
|
||||||
|
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||||
|
}
|
||||||
|
|
||||||
function scanForSourcemapURL(fileName: string) {
|
function scanForSourcemapURL(fileName: string) {
|
||||||
const mappedFile = sourcemappedFileCache.get(toPath(fileName, currentDirectory, getCanonicalFileName));
|
const mappedFile = sourcemappedFileCache.get(toPath(fileName));
|
||||||
if (!mappedFile) {
|
if (!mappedFile) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +92,7 @@ namespace ts {
|
||||||
}
|
}
|
||||||
possibleMapLocations.push(fileName + ".map");
|
possibleMapLocations.push(fileName + ".map");
|
||||||
for (const location of possibleMapLocations) {
|
for (const location of possibleMapLocations) {
|
||||||
const mapPath = toPath(location, getDirectoryPath(fileName), getCanonicalFileName);
|
const mapPath = ts.toPath(location, getDirectoryPath(fileName), getCanonicalFileName);
|
||||||
if (host.fileExists(mapPath)) {
|
if (host.fileExists(mapPath)) {
|
||||||
return convertDocumentToSourceMapper(file, host.readFile(mapPath)!, mapPath); // TODO: GH#18217
|
return convertDocumentToSourceMapper(file, host.readFile(mapPath)!, mapPath); // TODO: GH#18217
|
||||||
}
|
}
|
||||||
|
@ -120,12 +124,16 @@ namespace ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFile(fileName: string): SourceFileLike | undefined {
|
function getFile(fileName: string): SourceFileLike | undefined {
|
||||||
return getProgram().getSourceFile(fileName) || sourcemappedFileCache.get(toPath(fileName, currentDirectory, getCanonicalFileName));
|
const path = toPath(fileName);
|
||||||
|
const file = getProgram().getSourceFileByPath(path);
|
||||||
|
if (file && file.resolvedPath === path) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
return sourcemappedFileCache.get(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toLineColumnOffset(fileName: string, position: number): LineAndCharacter {
|
function toLineColumnOffset(fileName: string, position: number): LineAndCharacter {
|
||||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
const file = getFile(fileName)!; // TODO: GH#18217
|
||||||
const file = getProgram().getSourceFile(path) || sourcemappedFileCache.get(path)!; // TODO: GH#18217
|
|
||||||
return file.getLineAndCharacterOfPosition(position);
|
return file.getLineAndCharacterOfPosition(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10562,90 +10562,149 @@ declare class TestLib {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("tsserverProjectSystem with tsbuild projects", () => {
|
describe("tsserverProjectSystem with tsbuild projects", () => {
|
||||||
function getProjectFiles(project: string): [File, File] {
|
function createHost(files: ReadonlyArray<File>, rootNames: ReadonlyArray<string>) {
|
||||||
return [
|
|
||||||
TestFSWithWatch.getTsBuildProjectFile(project, "tsconfig.json"),
|
|
||||||
TestFSWithWatch.getTsBuildProjectFile(project, "index.ts"),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const project = "container";
|
|
||||||
const containerLib = getProjectFiles("container/lib");
|
|
||||||
const containerExec = getProjectFiles("container/exec");
|
|
||||||
const containerCompositeExec = getProjectFiles("container/compositeExec");
|
|
||||||
const containerConfig = TestFSWithWatch.getTsBuildProjectFile(project, "tsconfig.json");
|
|
||||||
const files = [libFile, ...containerLib, ...containerExec, ...containerCompositeExec, containerConfig];
|
|
||||||
|
|
||||||
function createHost() {
|
|
||||||
const host = createServerHost(files);
|
const host = createServerHost(files);
|
||||||
|
|
||||||
// ts build should succeed
|
// ts build should succeed
|
||||||
const solutionBuilder = tscWatch.createSolutionBuilder(host, [containerConfig.path], {});
|
const solutionBuilder = tscWatch.createSolutionBuilder(host, rootNames, {});
|
||||||
solutionBuilder.buildAllProjects();
|
solutionBuilder.buildAllProjects();
|
||||||
assert.equal(host.getOutput().length, 0);
|
assert.equal(host.getOutput().length, 0);
|
||||||
|
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
it("does not error on container only project", () => {
|
describe("with container project", () => {
|
||||||
const host = createHost();
|
function getProjectFiles(project: string): [File, File] {
|
||||||
|
return [
|
||||||
|
TestFSWithWatch.getTsBuildProjectFile(project, "tsconfig.json"),
|
||||||
|
TestFSWithWatch.getTsBuildProjectFile(project, "index.ts"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
// Open external project for the folder
|
const project = "container";
|
||||||
const session = createSession(host);
|
const containerLib = getProjectFiles("container/lib");
|
||||||
const service = session.getProjectService();
|
const containerExec = getProjectFiles("container/exec");
|
||||||
service.openExternalProjects([{
|
const containerCompositeExec = getProjectFiles("container/compositeExec");
|
||||||
projectFileName: TestFSWithWatch.getTsBuildProjectFilePath(project, project),
|
const containerConfig = TestFSWithWatch.getTsBuildProjectFile(project, "tsconfig.json");
|
||||||
rootFiles: files.map(f => ({ fileName: f.path })),
|
const files = [libFile, ...containerLib, ...containerExec, ...containerCompositeExec, containerConfig];
|
||||||
options: {}
|
|
||||||
}]);
|
it("does not error on container only project", () => {
|
||||||
checkNumberOfProjects(service, { configuredProjects: 4 });
|
const host = createHost(files, [containerConfig.path]);
|
||||||
files.forEach(f => {
|
|
||||||
const args: protocol.FileRequestArgs = {
|
// Open external project for the folder
|
||||||
file: f.path,
|
const session = createSession(host);
|
||||||
projectFileName: endsWith(f.path, "tsconfig.json") ? f.path : undefined
|
const service = session.getProjectService();
|
||||||
};
|
service.openExternalProjects([{
|
||||||
const syntaxDiagnostics = session.executeCommandSeq<protocol.SyntacticDiagnosticsSyncRequest>({
|
projectFileName: TestFSWithWatch.getTsBuildProjectFilePath(project, project),
|
||||||
command: protocol.CommandTypes.SyntacticDiagnosticsSync,
|
rootFiles: files.map(f => ({ fileName: f.path })),
|
||||||
arguments: args
|
options: {}
|
||||||
|
}]);
|
||||||
|
checkNumberOfProjects(service, { configuredProjects: 4 });
|
||||||
|
files.forEach(f => {
|
||||||
|
const args: protocol.FileRequestArgs = {
|
||||||
|
file: f.path,
|
||||||
|
projectFileName: endsWith(f.path, "tsconfig.json") ? f.path : undefined
|
||||||
|
};
|
||||||
|
const syntaxDiagnostics = session.executeCommandSeq<protocol.SyntacticDiagnosticsSyncRequest>({
|
||||||
|
command: protocol.CommandTypes.SyntacticDiagnosticsSync,
|
||||||
|
arguments: args
|
||||||
|
}).response;
|
||||||
|
assert.deepEqual(syntaxDiagnostics, []);
|
||||||
|
const semanticDiagnostics = session.executeCommandSeq<protocol.SemanticDiagnosticsSyncRequest>({
|
||||||
|
command: protocol.CommandTypes.SemanticDiagnosticsSync,
|
||||||
|
arguments: args
|
||||||
|
}).response;
|
||||||
|
assert.deepEqual(semanticDiagnostics, []);
|
||||||
|
});
|
||||||
|
const containerProject = service.configuredProjects.get(containerConfig.path)!;
|
||||||
|
checkProjectActualFiles(containerProject, [containerConfig.path]);
|
||||||
|
const optionsDiagnostics = session.executeCommandSeq<protocol.CompilerOptionsDiagnosticsRequest>({
|
||||||
|
command: protocol.CommandTypes.CompilerOptionsDiagnosticsFull,
|
||||||
|
arguments: { projectFileName: containerProject.projectName }
|
||||||
}).response;
|
}).response;
|
||||||
assert.deepEqual(syntaxDiagnostics, []);
|
assert.deepEqual(optionsDiagnostics, []);
|
||||||
const semanticDiagnostics = session.executeCommandSeq<protocol.SemanticDiagnosticsSyncRequest>({
|
});
|
||||||
command: protocol.CommandTypes.SemanticDiagnosticsSync,
|
|
||||||
arguments: args
|
it("can successfully find references with --out options", () => {
|
||||||
}).response;
|
const host = createHost(files, [containerConfig.path]);
|
||||||
assert.deepEqual(semanticDiagnostics, []);
|
const session = createSession(host);
|
||||||
|
openFilesForSession([containerCompositeExec[1]], session);
|
||||||
|
const service = session.getProjectService();
|
||||||
|
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||||
|
const locationOfMyConst = protocolLocationFromSubstring(containerCompositeExec[1].content, "myConst");
|
||||||
|
const response = session.executeCommandSeq<protocol.RenameRequest>({
|
||||||
|
command: protocol.CommandTypes.Rename,
|
||||||
|
arguments: {
|
||||||
|
file: containerCompositeExec[1].path,
|
||||||
|
...locationOfMyConst
|
||||||
|
}
|
||||||
|
}).response as protocol.RenameResponseBody;
|
||||||
|
|
||||||
|
|
||||||
|
const myConstLen = "myConst".length;
|
||||||
|
const locationOfMyConstInLib = protocolLocationFromSubstring(containerLib[1].content, "myConst");
|
||||||
|
assert.deepEqual(response.locs, [
|
||||||
|
{ file: containerCompositeExec[1].path, locs: [{ start: locationOfMyConst, end: { line: locationOfMyConst.line, offset: locationOfMyConst.offset + myConstLen } }] },
|
||||||
|
{ file: containerLib[1].path, locs: [{ start: locationOfMyConstInLib, end: { line: locationOfMyConstInLib.line, offset: locationOfMyConstInLib.offset + myConstLen } }] }
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
const containerProject = service.configuredProjects.get(containerConfig.path)!;
|
|
||||||
checkProjectActualFiles(containerProject, [containerConfig.path]);
|
|
||||||
const optionsDiagnostics = session.executeCommandSeq<protocol.CompilerOptionsDiagnosticsRequest>({
|
|
||||||
command: protocol.CommandTypes.CompilerOptionsDiagnosticsFull,
|
|
||||||
arguments: { projectFileName: containerProject.projectName }
|
|
||||||
}).response;
|
|
||||||
assert.deepEqual(optionsDiagnostics, []);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can successfully find references with --out options", () => {
|
it("can go to definition correctly", () => {
|
||||||
const host = createHost();
|
const projectLocation = "/user/username/projects/myproject";
|
||||||
|
const dependecyLocation = `${projectLocation}/dependency`;
|
||||||
|
const mainLocation = `${projectLocation}/main`;
|
||||||
|
const dependencyTs: File = {
|
||||||
|
path: `${dependecyLocation}/FnS.ts`,
|
||||||
|
content: `export function fn1() { }
|
||||||
|
export function fn2() { }
|
||||||
|
export function fn3() { }
|
||||||
|
export function fn4() { }
|
||||||
|
export function fn5() { }`
|
||||||
|
};
|
||||||
|
const dependencyConfig: File = {
|
||||||
|
path: `${dependecyLocation}/tsconfig.json`,
|
||||||
|
content: JSON.stringify({ compilerOptions: { composite: true, declarationMap: true } })
|
||||||
|
};
|
||||||
|
|
||||||
|
const mainTs: File = {
|
||||||
|
path: `${mainLocation}/main.ts`,
|
||||||
|
content: `import {
|
||||||
|
fn1, fn2, fn3, fn4, fn5
|
||||||
|
} from '../dependency/fns'
|
||||||
|
|
||||||
|
fn1();
|
||||||
|
fn2();
|
||||||
|
fn3();
|
||||||
|
fn4();
|
||||||
|
fn5();`
|
||||||
|
};
|
||||||
|
const mainConfig: File = {
|
||||||
|
path: `${mainLocation}/tsconfig.json`,
|
||||||
|
content: JSON.stringify({
|
||||||
|
compilerOptions: { composite: true, declarationMap: true },
|
||||||
|
references: [{ path: "../dependency" }]
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const files = [dependencyTs, dependencyConfig, mainTs, mainConfig, libFile];
|
||||||
|
const host = createHost(files, [mainConfig.path]);
|
||||||
const session = createSession(host);
|
const session = createSession(host);
|
||||||
openFilesForSession([containerCompositeExec[1]], session);
|
|
||||||
const service = session.getProjectService();
|
const service = session.getProjectService();
|
||||||
|
openFilesForSession([mainTs], session);
|
||||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||||
const locationOfMyConst = protocolLocationFromSubstring(containerCompositeExec[1].content, "myConst");
|
checkProjectActualFiles(service.configuredProjects.get(mainConfig.path)!, [mainTs.path, libFile.path, mainConfig.path, `${dependecyLocation}/fns.d.ts`]);
|
||||||
const response = session.executeCommandSeq<protocol.RenameRequest>({
|
for (let i = 0; i < 5; i++) {
|
||||||
command: protocol.CommandTypes.Rename,
|
const startSpan = { line: i + 5, offset: 1 };
|
||||||
arguments: {
|
const response = session.executeCommandSeq<protocol.DefinitionAndBoundSpanRequest>({
|
||||||
file: containerCompositeExec[1].path,
|
command: protocol.CommandTypes.DefinitionAndBoundSpan,
|
||||||
...locationOfMyConst
|
arguments: { file: mainTs.path, ...startSpan }
|
||||||
}
|
}).response as protocol.DefinitionInfoAndBoundSpan;
|
||||||
}).response as protocol.RenameResponseBody;
|
assert.deepEqual(response, {
|
||||||
|
definitions: [{ file: dependencyTs.path, start: { line: i + 1, offset: 17 }, end: { line: i + 1, offset: 20 } }],
|
||||||
|
textSpan: { start: startSpan, end: { line: startSpan.line, offset: startSpan.offset + 3 } }
|
||||||
const myConstLen = "myConst".length;
|
});
|
||||||
const locationOfMyConstInLib = protocolLocationFromSubstring(containerLib[1].content, "myConst");
|
}
|
||||||
assert.deepEqual(response.locs, [
|
|
||||||
{ file: containerCompositeExec[1].path, locs: [{ start: locationOfMyConst, end: { line: locationOfMyConst.line, offset: locationOfMyConst.offset + myConstLen } }] },
|
|
||||||
{ file: containerLib[1].path, locs: [{ start: locationOfMyConstInLib, end: { line: locationOfMyConstInLib.line, offset: locationOfMyConstInLib.offset + myConstLen } }] }
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue