Add test infrastructure

This commit is contained in:
Andrew Branch
2022-03-01 15:45:31 -08:00
parent 81ec4fd493
commit 422d845cc0
7 changed files with 101 additions and 65 deletions
+21 -1
View File
@@ -300,7 +300,7 @@ namespace ts.server {
getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan {
const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position);
const request = this.processRequest<protocol.DefinitionRequest>(CommandNames.DefinitionAndBoundSpan, args);
const request = this.processRequest<protocol.DefinitionAndBoundSpanRequest>(CommandNames.DefinitionAndBoundSpan, args);
const response = this.processResponse<protocol.DefinitionInfoAndBoundSpanResponse>(request);
const body = Debug.checkDefined(response.body); // TODO: GH#18217
@@ -334,6 +334,26 @@ namespace ts.server {
}));
}
getSourceDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan {
const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position);
const request = this.processRequest<protocol.SourceDefinitionAndBoundSpanRequest>(CommandNames.SourceDefinitionAndBoundSpan, args);
const response = this.processResponse<protocol.DefinitionInfoAndBoundSpanResponse>(request);
const body = Debug.checkDefined(response.body); // TODO: GH#18217
return {
definitions: body.definitions.map(entry => ({
containerKind: ScriptElementKind.unknown,
containerName: "",
fileName: entry.file,
textSpan: this.decodeSpan(entry),
kind: ScriptElementKind.unknown,
name: "",
unverified: entry.unverified,
})),
textSpan: this.decodeSpan(body.textSpan, request.arguments.file)
};
}
getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
const args = this.createFileLocationRequestArgs(fileName, position);
+7
View File
@@ -697,6 +697,13 @@ namespace FourSlash {
this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinitionAndBoundSpan());
}
public verifyGoToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, end?: ArrayOrSingle<string | { marker: string, unverified?: boolean }> | { file: string, unverified?: boolean }) {
if (this.testType !== FourSlashTestType.Server) {
this.raiseError("goToSourceDefinition may only be used in fourslash/server tests.");
}
this.verifyGoToX(startMarkerNames, end, () => (this.languageService as ts.server.SessionClient).getSourceDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition)!);
}
private getGoToDefinition(): readonly ts.DefinitionInfo[] {
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
}
+4
View File
@@ -324,6 +324,10 @@ namespace FourSlashInterface {
this.state.verifyGoToType(arg0, endMarkerName);
}
public goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, end: { file: string } | ArrayOrSingle<string>) {
this.state.verifyGoToSourceDefinition(startMarkerNames, end);
}
public goToDefinitionForMarkers(...markerNames: string[]) {
this.state.verifyGoToDefinitionForMarkers(markerNames);
}
+7
View File
@@ -83,6 +83,9 @@ namespace ts.server.protocol {
SignatureHelp = "signatureHelp",
/* @internal */
SignatureHelpFull = "signatureHelp-full",
SourceDefinitionAndBoundSpan = "sourceDefinitionAndBoundSpan",
/* @internal */
SourceDefinitionAndBoundSpanFull = "sourceDefinitionAndBoundSpan-full",
Status = "status",
TypeDefinition = "typeDefinition",
ProjectInfo = "projectInfo",
@@ -904,6 +907,10 @@ namespace ts.server.protocol {
readonly command: CommandTypes.DefinitionAndBoundSpan;
}
export interface SourceDefinitionAndBoundSpanRequest extends FileLocationRequest {
readonly command: CommandTypes.SourceDefinitionAndBoundSpan;
}
export interface DefinitionAndBoundSpanResponse extends Response {
readonly body: DefinitionInfoAndBoundSpan;
}
+47 -64
View File
@@ -1239,36 +1239,6 @@ namespace ts.server {
}
private getDefinitionAndBoundSpan(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.DefinitionInfoAndBoundSpan | DefinitionInfoAndBoundSpan {
// const { file, project } = this.getFileAndProject(args);
// const position = this.getPositionInFile(args, file);
// const scriptInfo = Debug.checkDefined(project.getScriptInfo(file));
// const unmappedDefinitionAndBoundSpan = project.getLanguageService().getDefinitionAndBoundSpan(file, position);
// if (!unmappedDefinitionAndBoundSpan || !unmappedDefinitionAndBoundSpan.definitions) {
// return {
// definitions: emptyArray,
// textSpan: undefined! // TODO: GH#18217
// };
// }
// const definitions = this.mapDefinitionInfoLocations(unmappedDefinitionAndBoundSpan.definitions, project);
// const { textSpan } = unmappedDefinitionAndBoundSpan;
// if (simplifiedResult) {
// return {
// definitions: this.mapDefinitionInfo(definitions, project),
// textSpan: toProtocolTextSpan(textSpan, scriptInfo)
// };
// }
// return {
// definitions: definitions.map(Session.mapToOriginalLocation),
// textSpan,
// };
// }
// private getSourceDefinitionAndBoundSpan(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.DefinitionInfoAndBoundSpan | DefinitionInfoAndBoundSpan {
const { file, project } = this.getFileAndProject(args);
const position = this.getPositionInFile(args, file);
const scriptInfo = Debug.checkDefined(project.getScriptInfo(file));
@@ -1282,17 +1252,7 @@ namespace ts.server {
};
}
const definitions = this.mapDefinitionInfoLocations(unmappedDefinitionAndBoundSpan.definitions, project).slice();
const needsJsResolution = every(definitions.filter(d => d.isAliasTarget), d => !!d.isAmbient);
if (needsJsResolution) {
project.withAuxiliaryProjectForFiles([file], auxiliaryProject => {
const jsDefinitions = auxiliaryProject.getLanguageService().getDefinitionAndBoundSpan(file, position);
for (const jsDefinition of jsDefinitions?.definitions || emptyArray) {
pushIfUnique(definitions, jsDefinition, (a, b) => a.fileName === b.fileName && a.textSpan.start === b.textSpan.start);
}
});
}
const definitions = this.mapDefinitionInfoLocations(unmappedDefinitionAndBoundSpan.definitions, project);
const { textSpan } = unmappedDefinitionAndBoundSpan;
if (simplifiedResult) {
@@ -1306,30 +1266,47 @@ namespace ts.server {
definitions: definitions.map(Session.mapToOriginalLocation),
textSpan,
};
}
// function getLocationsResolvingToDts(symbol: Symbol, checker: TypeChecker) {
// let locations: DocumentPosition[] | undefined;
// while (symbol.flags & SymbolFlags.Alias) {
// const aliasDeclaration = find(symbol.declarations || emptyArray, isAliasSymbolDeclaration);
// if (!aliasDeclaration) break;
private getSourceDefinitionAndBoundSpan(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.DefinitionInfoAndBoundSpan | DefinitionInfoAndBoundSpan {
const { file, project } = this.getFileAndProject(args);
const position = this.getPositionInFile(args, file);
const scriptInfo = Debug.checkDefined(project.getScriptInfo(file));
// const resolvedSymbol = checker.getImmediateAliasedSymbol(symbol);
// if (!resolvedSymbol) break;
const unmappedDefinitionAndBoundSpan = project.getLanguageService().getDefinitionAndBoundSpan(file, position);
// const hasUnmappedDtsDeclarations = some(resolvedSymbol.declarations, d => {
// const sourceFile = getSourceFileOfNode(d);
// if (!isDeclarationFileName(sourceFile.fileName)) return false;
// const mappedFileName = project.getSourceMapper().tryGetSourcePosition(sourceFile)?.fileName;
// if (mappedFileName && project.projectService.fileExists(toNormalizedPath(mappedFileName))) return false;
// });
// if (hasUnmappedDtsDeclarations) {
// const sourceFile = getSourceFileOfNode(aliasDeclaration);
// locations = append(locations, { fileName: sourceFile.fileName, pos: aliasDeclaration.getStart(sourceFile) });
// }
// symbol = resolvedSymbol;
// }
// return locations;
// }
if (!unmappedDefinitionAndBoundSpan || !unmappedDefinitionAndBoundSpan.definitions) {
return {
definitions: emptyArray,
textSpan: undefined! // TODO: GH#18217
};
}
let definitions = this.mapDefinitionInfoLocations(unmappedDefinitionAndBoundSpan.definitions, project).slice();
const needsJsResolution = every(definitions.filter(d => d.isAliasTarget), d => !!d.isAmbient);
if (needsJsResolution) {
project.withAuxiliaryProjectForFiles([file], auxiliaryProject => {
const jsDefinitions = auxiliaryProject.getLanguageService().getDefinitionAndBoundSpan(file, position);
for (const jsDefinition of jsDefinitions?.definitions || emptyArray) {
pushIfUnique(definitions, jsDefinition, (a, b) => a.fileName === b.fileName && a.textSpan.start === b.textSpan.start);
}
});
}
definitions = definitions.filter(d => !d.isAmbient);
const { textSpan } = unmappedDefinitionAndBoundSpan;
if (simplifiedResult) {
return {
definitions: this.mapDefinitionInfo(definitions, project),
textSpan: toProtocolTextSpan(textSpan, scriptInfo)
};
}
return {
definitions: definitions.map(Session.mapToOriginalLocation),
textSpan,
};
}
private getEmitOutput(args: protocol.EmitOutputRequestArgs): EmitOutput | protocol.EmitOutput {
@@ -2750,12 +2727,18 @@ namespace ts.server {
[CommandNames.DefinitionFull]: (request: protocol.DefinitionRequest) => {
return this.requiredResponse(this.getDefinition(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.DefinitionAndBoundSpan]: (request: protocol.DefinitionRequest) => {
[CommandNames.DefinitionAndBoundSpan]: (request: protocol.DefinitionAndBoundSpanRequest) => {
return this.requiredResponse(this.getDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ true));
},
[CommandNames.DefinitionAndBoundSpanFull]: (request: protocol.DefinitionRequest) => {
[CommandNames.DefinitionAndBoundSpanFull]: (request: protocol.DefinitionAndBoundSpanRequest) => {
return this.requiredResponse(this.getDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.SourceDefinitionAndBoundSpan]: (request: protocol.SourceDefinitionAndBoundSpanRequest) => {
return this.requiredResponse(this.getSourceDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ true));
},
[CommandNames.SourceDefinitionAndBoundSpanFull]: (request: protocol.SourceDefinitionAndBoundSpanRequest) => {
return this.requiredResponse(this.getSourceDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.EmitOutput]: (request: protocol.EmitOutputRequest) => {
return this.requiredResponse(this.getEmitOutput(request.arguments));
},
+2
View File
@@ -317,6 +317,8 @@ declare namespace FourSlashInterface {
goToDefinition(startsAndEnds: { [startMarkerName: string]: ArrayOrSingle<string> }): void;
/** Verifies goToDefinition for each `${markerName}Reference` -> `${markerName}Definition` */
goToDefinitionForMarkers(...markerNames: string[]): void;
goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, fileResult: { file: string }): void;
goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string>): void;
goToType(startsAndEnds: { [startMarkerName: string]: ArrayOrSingle<string> }): void;
goToType(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string>): void;
verifyGetEmitOutputForCurrentFile(expected: string): void;
@@ -0,0 +1,13 @@
/// <reference path="../fourslash.ts" />
// @Filename: /a.js
//// export const /*end*/a = "a";
// @Filename: /a.d.ts
//// export declare const a: string;
// @Filename: /index.ts
//// import { a } from "./a";
//// [|a/*start*/|]
verify.goToSourceDefinition("start", "end");