mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 07:13:45 -05:00
Add test for goto-definition with project references (#24867)
* Add test for goto-definition with project references * Assert that the declaration file is what we emit
This commit is contained in:
@@ -2439,7 +2439,7 @@ namespace ts.server.protocol {
|
||||
/**
|
||||
* An item found in a navto response.
|
||||
*/
|
||||
export interface NavtoItem {
|
||||
export interface NavtoItem extends FileSpan {
|
||||
/**
|
||||
* The symbol's name.
|
||||
*/
|
||||
@@ -2465,21 +2465,6 @@ namespace ts.server.protocol {
|
||||
*/
|
||||
kindModifiers?: string;
|
||||
|
||||
/**
|
||||
* The file in which the symbol is found.
|
||||
*/
|
||||
file: string;
|
||||
|
||||
/**
|
||||
* The location within file at which the symbol is found.
|
||||
*/
|
||||
start: Location;
|
||||
|
||||
/**
|
||||
* One past the last character of the symbol.
|
||||
*/
|
||||
end: Location;
|
||||
|
||||
/**
|
||||
* Name of symbol's container symbol (if any); for example,
|
||||
* the class name if symbol is a class member.
|
||||
|
||||
@@ -415,22 +415,33 @@ namespace ts.projectSystem {
|
||||
checkArray("Open files", arrayFrom(projectService.openFiles.keys(), path => projectService.getScriptInfoForPath(path as Path)!.fileName), expectedFiles.map(file => file.path));
|
||||
}
|
||||
|
||||
function protocolLocationFromSubstring(str: string, substring: string) {
|
||||
const start = str.indexOf(substring);
|
||||
Debug.assert(start !== -1);
|
||||
return protocolToLocation(str)(start);
|
||||
}
|
||||
function protocolToLocation(text: string): (pos: number) => protocol.Location {
|
||||
const lineStarts = computeLineStarts(text);
|
||||
return pos => {
|
||||
const x = computeLineAndCharacterOfPosition(lineStarts, pos);
|
||||
return { line: x.line + 1, offset: x.character + 1 };
|
||||
};
|
||||
}
|
||||
function protocolTextSpanFromSubstring(str: string, substring: string): protocol.TextSpan {
|
||||
const span = textSpanFromSubstring(str, substring);
|
||||
const toLocation = protocolToLocation(str);
|
||||
return { start: toLocation(span.start), end: toLocation(span.start + span.length) };
|
||||
}
|
||||
function textSpanFromSubstring(str: string, substring: string): TextSpan {
|
||||
const start = str.indexOf(substring);
|
||||
Debug.assert(start !== -1);
|
||||
return createTextSpan(start, substring.length);
|
||||
}
|
||||
|
||||
function protocolTextSpanFromSubstring(str: string, substring: string): protocol.TextSpan {
|
||||
const start = str.indexOf(substring);
|
||||
Debug.assert(start !== -1);
|
||||
const lineStarts = computeLineStarts(str);
|
||||
const toLocation = (pos: number) => lineAndCharacterToLocation(computeLineAndCharacterOfPosition(lineStarts, pos));
|
||||
return { start: toLocation(start), end: toLocation(start + substring.length) };
|
||||
function protocolFileLocationFromSubstring(file: File, substring: string): protocol.FileLocationRequestArgs {
|
||||
return { file: file.path, ...protocolLocationFromSubstring(file.content, substring) };
|
||||
}
|
||||
|
||||
function lineAndCharacterToLocation(lc: LineAndCharacter): protocol.Location {
|
||||
return { line: lc.line + 1, offset: lc.character + 1 };
|
||||
function protocolFileSpanFromSubstring(file: File, substring: string): protocol.FileSpan {
|
||||
return { file: file.path, ...protocolTextSpanFromSubstring(file.content, substring) };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,13 +496,19 @@ namespace ts.projectSystem {
|
||||
};
|
||||
}
|
||||
|
||||
export function openFilesForSession(files: ReadonlyArray<File>, session: server.Session) {
|
||||
export function openFilesForSession(files: ReadonlyArray<File>, session: server.Session): void {
|
||||
for (const file of files) {
|
||||
const request = makeSessionRequest<protocol.OpenRequestArgs>(CommandNames.Open, { file: file.path });
|
||||
session.executeCommand(request);
|
||||
}
|
||||
}
|
||||
|
||||
export function closeFilesForSession(files: ReadonlyArray<File>, session: server.Session): void {
|
||||
for (const file of files) {
|
||||
session.executeCommand(makeSessionRequest<protocol.FileRequestArgs>(CommandNames.Close, { file: file.path }));
|
||||
}
|
||||
}
|
||||
|
||||
interface ErrorInformation {
|
||||
diagnosticMessage: DiagnosticMessage;
|
||||
errorTextArguments?: string[];
|
||||
@@ -8830,4 +8847,108 @@ export const x = 10;`
|
||||
assert.equal(moduleInfo.cacheSourceFile.sourceFile.text, updatedModuleContent);
|
||||
});
|
||||
});
|
||||
|
||||
function makeSampleProjects() {
|
||||
const aTs: File = {
|
||||
path: "/a/a.ts",
|
||||
content: "export function fnA() {}",
|
||||
};
|
||||
const aTsconfig: File = {
|
||||
path: "/a/tsconfig.json",
|
||||
content: `{
|
||||
"compilerOptions": {
|
||||
"outDir": "bin",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"composite": true,
|
||||
}
|
||||
}`,
|
||||
};
|
||||
|
||||
const bTs: File = {
|
||||
path: "/b/b.ts",
|
||||
content: 'import { fnA } from "../a/a";\nexport function fnB() { fnA(); }',
|
||||
};
|
||||
const bTsconfig: File = {
|
||||
path: "/b/tsconfig.json",
|
||||
content: `{
|
||||
"compilerOptions": {
|
||||
"outDir": "bin",
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../a" }
|
||||
]
|
||||
}`,
|
||||
};
|
||||
|
||||
const host = createServerHost([aTs, aTsconfig, bTs, bTsconfig]);
|
||||
const session = createSession(host);
|
||||
|
||||
writeDeclarationFiles(aTs, host, session, [
|
||||
{ name: "/a/bin/a.d.ts.map", text: '{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["../a.ts"],"names":[],"mappings":"AAAA,wBAAgB,GAAG,SAAK"}' },
|
||||
// Need to mangle the sourceMappingURL part or it breaks the build
|
||||
{ name: "/a/bin/a.d.ts", text: `export declare function fnA(): void;\n//# source${""}MappingURL=a.d.ts.map` },
|
||||
]);
|
||||
|
||||
return { session, aTs, bTs };
|
||||
}
|
||||
|
||||
describe("tsserverProjectSystem project references", () => {
|
||||
it("goToDefinition", () => {
|
||||
const { session, aTs, bTs } = makeSampleProjects();
|
||||
|
||||
openFilesForSession([bTs], session);
|
||||
|
||||
const definitionRequest = makeSessionRequest<protocol.FileLocationRequestArgs>(CommandNames.Definition, protocolFileLocationFromSubstring(bTs, "fnA()"));
|
||||
const definitionResponse = session.executeCommand(definitionRequest).response as protocol.DefinitionResponse["body"];
|
||||
|
||||
assert.deepEqual(definitionResponse, [protocolFileSpanFromSubstring(aTs, "fnA")]);
|
||||
});
|
||||
|
||||
it("navigateTo", () => {
|
||||
const { session, bTs } = makeSampleProjects();
|
||||
|
||||
openFilesForSession([bTs], session);
|
||||
|
||||
const navtoRequest = makeSessionRequest<protocol.NavtoRequestArgs>(CommandNames.Navto, { file: bTs.path, searchValue: "fn" });
|
||||
const navtoResponse = session.executeCommand(navtoRequest).response as protocol.NavtoResponse["body"];
|
||||
|
||||
assert.deepEqual(navtoResponse, [
|
||||
// TODO: First result should be from a.ts, not a.d.ts
|
||||
{
|
||||
file: "/a/bin/a.d.ts",
|
||||
start: { line: 1, offset: 1 },
|
||||
end: { line: 1, offset: 37 },
|
||||
name: "fnA",
|
||||
matchKind: "prefix",
|
||||
kind: ScriptElementKind.functionElement,
|
||||
kindModifiers: "export,declare",
|
||||
},
|
||||
{
|
||||
...protocolFileSpanFromSubstring(bTs, "export function fnB() { fnA(); }"),
|
||||
name: "fnB",
|
||||
matchKind: "prefix",
|
||||
kind: ScriptElementKind.functionElement,
|
||||
kindModifiers: "export",
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
function writeDeclarationFiles(file: File, host: TestServerHost, session: TestSession, expectedFiles: ReadonlyArray<{ readonly name: string, readonly text: string }>): void {
|
||||
openFilesForSession([file], session);
|
||||
const project = Debug.assertDefined(session.getProjectService().getDefaultProjectForFile(file.path as server.NormalizedPath, /*ensureProject*/ false));
|
||||
const program = project.getCurrentProgram();
|
||||
const output = getFileEmitOutput(program, Debug.assertDefined(program.getSourceFile(file.path)), /*emitOnlyDtsFiles*/ true);
|
||||
closeFilesForSession([file], session);
|
||||
|
||||
Debug.assert(!output.emitSkipped);
|
||||
assert.deepEqual(output.outputFiles, expectedFiles.map(e => ({ ...e, writeByteOrderMark: false })));
|
||||
|
||||
for (const { name, text } of output.outputFiles) {
|
||||
const directory: Folder = { path: getDirectoryPath(name) };
|
||||
host.ensureFileOrFolder(directory);
|
||||
host.writeFile(name, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user