Skip to content

Fix the issue with file being included in the referencing project on rename when it wasnt included earlier #28400

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/compiler/sourcemapDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ namespace ts.sourcemaps {
if (!maps[targetIndex] || comparePaths(loc.fileName, maps[targetIndex].sourcePath, sourceRoot, !host.useCaseSensitiveFileNames) !== 0) {
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 {
Expand All @@ -94,13 +94,13 @@ namespace ts.sourcemaps {
// if no exact match, closest is 2's compliment of result
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 {
// Lookup file in program, if provided
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
if (!file || file.resolvedPath !== path) {
// Otherwise check the cache (which may hit disk)
Expand Down
18 changes: 13 additions & 5 deletions src/services/sourcemaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ namespace ts {
let sourcemappedFileCache: SourceFileLikeCache;
return { tryGetOriginalLocation, tryGetGeneratedLocation, toLineColumnOffset, clearCache };

function toPath(fileName: string) {
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
}

function scanForSourcemapURL(fileName: string) {
const mappedFile = sourcemappedFileCache.get(toPath(fileName, currentDirectory, getCanonicalFileName));
const mappedFile = sourcemappedFileCache.get(toPath(fileName));
if (!mappedFile) {
return;
}
Expand Down Expand Up @@ -88,7 +92,7 @@ namespace ts {
}
possibleMapLocations.push(fileName + ".map");
for (const location of possibleMapLocations) {
const mapPath = toPath(location, getDirectoryPath(fileName), getCanonicalFileName);
const mapPath = ts.toPath(location, getDirectoryPath(fileName), getCanonicalFileName);
if (host.fileExists(mapPath)) {
return convertDocumentToSourceMapper(file, host.readFile(mapPath)!, mapPath); // TODO: GH#18217
}
Expand Down Expand Up @@ -120,12 +124,16 @@ namespace ts {
}

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 {
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
const file = getProgram().getSourceFile(path) || sourcemappedFileCache.get(path)!; // TODO: GH#18217
const file = getFile(fileName)!; // TODO: GH#18217
return file.getLineAndCharacterOfPosition(position);
}

Expand Down
193 changes: 126 additions & 67 deletions src/testRunner/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10562,90 +10562,149 @@ declare class TestLib {
});

describe("tsserverProjectSystem with tsbuild projects", () => {
function getProjectFiles(project: string): [File, File] {
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() {
function createHost(files: ReadonlyArray<File>, rootNames: ReadonlyArray<string>) {
const host = createServerHost(files);

// ts build should succeed
const solutionBuilder = tscWatch.createSolutionBuilder(host, [containerConfig.path], {});
const solutionBuilder = tscWatch.createSolutionBuilder(host, rootNames, {});
solutionBuilder.buildAllProjects();
assert.equal(host.getOutput().length, 0);

return host;
}

it("does not error on container only project", () => {
const host = createHost();
describe("with container project", () => {
function getProjectFiles(project: string): [File, File] {
return [
TestFSWithWatch.getTsBuildProjectFile(project, "tsconfig.json"),
TestFSWithWatch.getTsBuildProjectFile(project, "index.ts"),
];
}

// Open external project for the folder
const session = createSession(host);
const service = session.getProjectService();
service.openExternalProjects([{
projectFileName: TestFSWithWatch.getTsBuildProjectFilePath(project, project),
rootFiles: files.map(f => ({ fileName: f.path })),
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
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];

it("does not error on container only project", () => {
const host = createHost(files, [containerConfig.path]);

// Open external project for the folder
const session = createSession(host);
const service = session.getProjectService();
service.openExternalProjects([{
projectFileName: TestFSWithWatch.getTsBuildProjectFilePath(project, project),
rootFiles: files.map(f => ({ fileName: f.path })),
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;
assert.deepEqual(semanticDiagnostics, []);
assert.deepEqual(optionsDiagnostics, []);
});

it("can successfully find references with --out options", () => {
const host = createHost(files, [containerConfig.path]);
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", () => {
const host = createHost();
it("can go to definition correctly", () => {
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);
openFilesForSession([containerCompositeExec[1]], session);
const service = session.getProjectService();
openFilesForSession([mainTs], session);
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 } }] }
]);
checkProjectActualFiles(service.configuredProjects.get(mainConfig.path)!, [mainTs.path, libFile.path, mainConfig.path, `${dependecyLocation}/fns.d.ts`]);
for (let i = 0; i < 5; i++) {
const startSpan = { line: i + 5, offset: 1 };
const response = session.executeCommandSeq<protocol.DefinitionAndBoundSpanRequest>({
command: protocol.CommandTypes.DefinitionAndBoundSpan,
arguments: { file: mainTs.path, ...startSpan }
}).response as protocol.DefinitionInfoAndBoundSpan;
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 } }
});
}
});
});

Expand Down