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:
Sheetal Nandi 2018-11-07 14:17:17 -08:00 committed by GitHub
commit 15af7e17c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 142 additions and 75 deletions

View file

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

View file

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

View file

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