mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-12 20:25:48 -06:00
Go to Implementation
This commit is contained in:
parent
a0137597f9
commit
111e50921c
@ -103,7 +103,8 @@ namespace ts {
|
||||
|
||||
getJsxElementAttributesType,
|
||||
getJsxIntrinsicTagNames,
|
||||
isOptionalParameter
|
||||
isOptionalParameter,
|
||||
isTypeAssignableTo
|
||||
};
|
||||
|
||||
const tupleTypes = createMap<TupleType>();
|
||||
|
||||
@ -1889,6 +1889,7 @@ namespace ts {
|
||||
getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
|
||||
getJsxIntrinsicTagNames(): Symbol[];
|
||||
isOptionalParameter(node: ParameterDeclaration): boolean;
|
||||
isTypeAssignableTo(source: Type, target: Type): boolean;
|
||||
|
||||
// Should not be called directly. Should only be accessed through the Program instance.
|
||||
/* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
|
||||
|
||||
@ -1669,6 +1669,11 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isFunctionDeclarationIdentifierName(node: Identifier): boolean {
|
||||
return node.parent.kind === SyntaxKind.FunctionDeclaration &&
|
||||
(<FunctionDeclaration>node.parent).name === node;
|
||||
}
|
||||
|
||||
// An alias symbol is created by one of the following declarations:
|
||||
// import <symbol> = ...
|
||||
// import <symbol> from ...
|
||||
|
||||
@ -1604,6 +1604,15 @@ namespace FourSlash {
|
||||
assertFn(actualCount, expectedCount, this.messageAtLastKnownMarker("Type definitions Count"));
|
||||
}
|
||||
|
||||
public verifyImplementationsCount(negative: boolean, expectedCount: number) {
|
||||
const assertFn = negative ? assert.notEqual : assert.equal;
|
||||
|
||||
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
const actualCount = implementations && implementations.length || 0;
|
||||
|
||||
assertFn(actualCount, expectedCount, this.messageAtLastKnownMarker("Implementations Count"));
|
||||
}
|
||||
|
||||
public verifyDefinitionsName(negative: boolean, expectedName: string, expectedContainerName: string) {
|
||||
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
const actualDefinitionName = definitions && definitions.length ? definitions[0].name : "";
|
||||
@ -1618,6 +1627,47 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public goToImplementation(implIndex: number) {
|
||||
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
if (!implementations || !implementations.length) {
|
||||
this.raiseError("goToImplementation failed - expected to at least one implementation location but got 0");
|
||||
}
|
||||
|
||||
if (implIndex >= implementations.length) {
|
||||
this.raiseError(`goToImplementation failed - implIndex value (${implIndex}) exceeds implementation list size (${implementations.length})`);
|
||||
}
|
||||
|
||||
const implementation = implementations[implIndex];
|
||||
this.openFile(implementation.fileName);
|
||||
this.currentCaretPosition = implementation.textSpan.start;
|
||||
}
|
||||
|
||||
public verifyRangesInImplementationList() {
|
||||
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
if (!implementations || !implementations.length) {
|
||||
this.raiseError("verifyRangesInImplementationList failed - expected to at least one implementation location but got 0");
|
||||
}
|
||||
|
||||
const ranges = this.getRanges();
|
||||
|
||||
if (!ranges || !ranges.length) {
|
||||
this.raiseError("verifyRangesInImplementationList failed - expected to at least one range in test source");
|
||||
}
|
||||
|
||||
for (const range of ranges) {
|
||||
let rangeIsPresent = false;
|
||||
const length = range.end - range.start;
|
||||
for (const impl of implementations) {
|
||||
if (range.fileName === impl.fileName && range.start === impl.textSpan.start && length === impl.textSpan.length) {
|
||||
rangeIsPresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert.isTrue(rangeIsPresent, `No implementation found for range ${range.start}, ${range.end} in ${range.fileName}: ${this.rangeText(range)}`);
|
||||
}
|
||||
assert.equal(implementations.length, ranges.length, `Different number of implementations (${implementations.length}) and ranges (${ranges.length})`);
|
||||
}
|
||||
|
||||
public getMarkers(): Marker[] {
|
||||
// Return a copy of the list
|
||||
return this.testData.markers.slice(0);
|
||||
@ -2768,6 +2818,10 @@ namespace FourSlashInterface {
|
||||
this.state.goToTypeDefinition(definitionIndex);
|
||||
}
|
||||
|
||||
public implementation(implementationIndex = 0) {
|
||||
this.state.goToImplementation(implementationIndex);
|
||||
}
|
||||
|
||||
public position(position: number, fileIndex?: number): void;
|
||||
public position(position: number, fileName?: string): void;
|
||||
public position(position: number, fileNameOrIndex?: any): void {
|
||||
@ -2876,6 +2930,10 @@ namespace FourSlashInterface {
|
||||
this.state.verifyTypeDefinitionsCount(this.negative, expectedCount);
|
||||
}
|
||||
|
||||
public implementationCountIs(expectedCount: number) {
|
||||
this.state.verifyImplementationsCount(this.negative, expectedCount);
|
||||
}
|
||||
|
||||
public definitionLocationExists() {
|
||||
this.state.verifyDefinitionLocationExists(this.negative);
|
||||
}
|
||||
@ -3113,6 +3171,10 @@ namespace FourSlashInterface {
|
||||
public ProjectInfo(expected: string[]) {
|
||||
this.state.verifyProjectInfo(expected);
|
||||
}
|
||||
|
||||
public allRangesAppearInImplementationList() {
|
||||
this.state.verifyRangesInImplementationList();
|
||||
}
|
||||
}
|
||||
|
||||
export class Edit {
|
||||
|
||||
@ -408,6 +408,9 @@ namespace Harness.LanguageService {
|
||||
getTypeDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
|
||||
return unwrapJSONCallResult(this.shim.getTypeDefinitionAtPosition(fileName, position));
|
||||
}
|
||||
getImplementationAtPosition(fileName: string, position: number): ts.ImplementationLocation[] {
|
||||
return unwrapJSONCallResult(this.shim.getImplementationAtPosition(fileName, position));
|
||||
}
|
||||
getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] {
|
||||
return unwrapJSONCallResult(this.shim.getReferencesAtPosition(fileName, position));
|
||||
}
|
||||
|
||||
@ -353,6 +353,28 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
|
||||
const request = this.processRequest<protocol.ImplementationRequest>(CommandNames.Implementation, args);
|
||||
const response = this.processResponse<protocol.ImplementationResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
findReferences(fileName: string, position: number): ReferencedSymbol[] {
|
||||
// Not yet implemented.
|
||||
return [];
|
||||
|
||||
15
src/server/protocol.d.ts
vendored
15
src/server/protocol.d.ts
vendored
@ -193,6 +193,14 @@ declare namespace ts.server.protocol {
|
||||
export interface TypeDefinitionRequest extends FileLocationRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to implementation request; value of command field is
|
||||
* "implementation". Return response giving the file locations that
|
||||
* implement the symbol found in file at location line, col.
|
||||
*/
|
||||
export interface ImplementationRequest extends FileLocationRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Location in source code expressed as (one-based) line and character offset.
|
||||
*/
|
||||
@ -240,6 +248,13 @@ declare namespace ts.server.protocol {
|
||||
body?: FileSpan[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation response message. Gives text range for implementations.
|
||||
*/
|
||||
export interface ImplementationResponse extends Response {
|
||||
body?: FileSpan[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get occurrences request; value of command field is
|
||||
* "occurrences". Return response giving spans that are relevant
|
||||
|
||||
@ -111,6 +111,7 @@ namespace ts.server {
|
||||
export const Formatonkey = "formatonkey";
|
||||
export const Geterr = "geterr";
|
||||
export const GeterrForProject = "geterrForProject";
|
||||
export const Implementation = "implementation";
|
||||
export const SemanticDiagnosticsSync = "semanticDiagnosticsSync";
|
||||
export const SyntacticDiagnosticsSync = "syntacticDiagnosticsSync";
|
||||
export const NavBar = "navbar";
|
||||
@ -357,6 +358,28 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
private getImplementation(line: number, offset: number, fileName: string): protocol.FileSpan[] {
|
||||
const file = ts.normalizePath(fileName);
|
||||
const project = this.projectService.getProjectForFile(file);
|
||||
if (!project || project.languageServiceDiabled) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
const compilerService = project.compilerService;
|
||||
const position = compilerService.host.lineOffsetToPosition(file, line, offset);
|
||||
|
||||
const implementations = compilerService.languageService.getImplementationAtPosition(file, position);
|
||||
if (!implementations) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return implementations.map(impl => ({
|
||||
file: impl.fileName,
|
||||
start: compilerService.host.positionToLineOffset(impl.fileName, impl.textSpan.start),
|
||||
end: compilerService.host.positionToLineOffset(impl.fileName, ts.textSpanEnd(impl.textSpan))
|
||||
}));
|
||||
}
|
||||
|
||||
private getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[] {
|
||||
fileName = ts.normalizePath(fileName);
|
||||
const project = this.projectService.getProjectForFile(fileName);
|
||||
@ -1074,6 +1097,10 @@ namespace ts.server {
|
||||
const defArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
return { response: this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file), responseRequired: true };
|
||||
},
|
||||
[CommandNames.Implementation]: (request: protocol.Request) => {
|
||||
const implArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
return { response: this.getImplementation(implArgs.line, implArgs.offset, implArgs.file), responseRequired: true };
|
||||
},
|
||||
[CommandNames.References]: (request: protocol.Request) => {
|
||||
const defArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
return { response: this.getReferences(defArgs.line, defArgs.offset, defArgs.file), responseRequired: true };
|
||||
|
||||
@ -1213,6 +1213,7 @@ namespace ts {
|
||||
|
||||
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
|
||||
getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
|
||||
getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[];
|
||||
|
||||
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
|
||||
findReferences(fileName: string, position: number): ReferencedSymbol[];
|
||||
@ -1301,6 +1302,11 @@ namespace ts {
|
||||
isDefinition: boolean;
|
||||
}
|
||||
|
||||
export interface ImplementationLocation {
|
||||
textSpan: TextSpan;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export interface DocumentHighlights {
|
||||
fileName: string;
|
||||
highlightSpans: HighlightSpan[];
|
||||
@ -5288,6 +5294,149 @@ namespace ts {
|
||||
return getDefinitionFromSymbol(type.symbol, node);
|
||||
}
|
||||
|
||||
/// Goto implementation
|
||||
function getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
|
||||
synchronizeHostData();
|
||||
|
||||
const entries: ImplementationLocation[] = [];
|
||||
const node = getTouchingPropertyName(getValidSourceFile(fileName), position);
|
||||
const typeChecker = program.getTypeChecker();
|
||||
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const entry = getReferenceEntryForShorthandPropertyAssignment(node, typeChecker);
|
||||
entries.push({
|
||||
textSpan: entry.textSpan,
|
||||
fileName: entry.fileName
|
||||
});
|
||||
}
|
||||
else if (definitionIsImplementation(node, typeChecker)) {
|
||||
const definitions = getDefinitionAtPosition(fileName, position);
|
||||
forEach(definitions, (definition: DefinitionInfo) => {
|
||||
entries.push({
|
||||
textSpan: definition.textSpan,
|
||||
fileName: definition.fileName
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Do a search for all references and filter them down to implementations only
|
||||
const result = getReferencedSymbolsForNode(node, program.getSourceFiles(), /*findInStrings*/false, /*findInComments*/false, /*implementations*/true);
|
||||
|
||||
forEach(result, referencedSymbol => {
|
||||
if (referencedSymbol.references) {
|
||||
forEach(referencedSymbol.references, entry => {
|
||||
entries.push({
|
||||
textSpan: entry.textSpan,
|
||||
fileName: entry.fileName
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the implementation for this node is the same as its definition
|
||||
*/
|
||||
function definitionIsImplementation(node: Node, typeChecker: TypeChecker) {
|
||||
if (node.kind === SyntaxKind.SuperKeyword || node.kind === SyntaxKind.ThisKeyword) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node.parent.kind === SyntaxKind.PropertyAccessExpression || node.parent.kind === SyntaxKind.ElementAccessExpression) {
|
||||
const expression = (<ElementAccessExpression | PropertyAccessExpression>node.parent).expression;
|
||||
|
||||
// Members of "this" and "super" only have one possible implementation, so no need to find
|
||||
// all references. Similarly, for the left hand side of the expression it only really
|
||||
// makes sense to return the definition
|
||||
if (node === expression || expression.kind === SyntaxKind.SuperKeyword || expression.kind === SyntaxKind.ThisKeyword) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check to see if this is a property that can have multiple implementations by determining
|
||||
// if the parent is an interface (or class, or union/intersection)
|
||||
const expressionType = typeChecker.getTypeAtLocation(expression);
|
||||
if (expressionType && expressionType.getFlags() & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.UnionOrIntersection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Also check the right hand side to see if this is a type being accessed on a namespace/module
|
||||
const rightHandType = typeChecker.getTypeAtLocation(node);
|
||||
return rightHandType && !(rightHandType.getFlags() & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.UnionOrIntersection));
|
||||
}
|
||||
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
return symbol && !isClassOrInterfaceReference(symbol) && !(symbol.parent && isClassOrInterfaceReference(symbol.parent));
|
||||
}
|
||||
|
||||
function getReferenceEntryForShorthandPropertyAssignment(node: Node, typeChecker: TypeChecker) {
|
||||
const refSymbol = typeChecker.getSymbolAtLocation(node);
|
||||
const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(refSymbol.valueDeclaration);
|
||||
|
||||
if (shorthandSymbol) {
|
||||
const shorthandDeclarations = shorthandSymbol.getDeclarations();
|
||||
if (shorthandDeclarations.length === 1) {
|
||||
return getReferenceEntryFromNode(shorthandDeclarations[0]);
|
||||
}
|
||||
else if (shorthandDeclarations.length > 1) {
|
||||
// This can happen when the property being assigned is a constructor for a
|
||||
// class that also has interface declarations with the same name. We just want
|
||||
// the class itself
|
||||
|
||||
return forEach(shorthandDeclarations, declaration => {
|
||||
if (declaration.kind === SyntaxKind.ClassDeclaration) {
|
||||
return getReferenceEntryFromNode(declaration);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isClassOrInterfaceReference(toCheck: Symbol) {
|
||||
return toCheck.getFlags() & (SymbolFlags.Class | SymbolFlags.Interface);
|
||||
}
|
||||
|
||||
function isIdentifierOfImplementation(node: Identifier): boolean {
|
||||
const parent = node.parent;
|
||||
|
||||
if (isIdentifierName(node) || isFunctionDeclarationIdentifierName(node)) {
|
||||
// PropertyAccessExpression?
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return !!(<FunctionLikeDeclaration>parent).body;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
return !!(<PropertyDeclaration>parent).initializer;
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (isVariableLike(parent) && parent.name === node) {
|
||||
return !!parent.initializer;
|
||||
}
|
||||
|
||||
return isIdentifierOfClass(node) || isIdentifierOfEnumDeclaration(node);
|
||||
}
|
||||
|
||||
function isIdentifierOfClass(node: Identifier) {
|
||||
return (node.parent.kind === SyntaxKind.ClassDeclaration || node.parent.kind === SyntaxKind.ClassExpression) &&
|
||||
(<ClassLikeDeclaration>node.parent).name === node;
|
||||
}
|
||||
|
||||
function isIdentifierOfEnumDeclaration(node: Identifier) {
|
||||
return node.parent.kind === SyntaxKind.EnumDeclaration && (<EnumDeclaration>node.parent).name === node;
|
||||
}
|
||||
|
||||
function isTypeAssertionExpression(node: Node): node is TypeAssertion {
|
||||
return node.kind === SyntaxKind.TypeAssertionExpression;
|
||||
}
|
||||
|
||||
function getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
|
||||
let results = getOccurrencesAtPositionCore(fileName, position);
|
||||
|
||||
@ -5334,7 +5483,7 @@ namespace ts {
|
||||
node.kind === SyntaxKind.StringLiteral ||
|
||||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
|
||||
|
||||
const referencedSymbols = getReferencedSymbolsForNode(node, sourceFilesToSearch, /*findInStrings*/ false, /*findInComments*/ false);
|
||||
const referencedSymbols = getReferencedSymbolsForNode(node, sourceFilesToSearch, /*findInStrings*/ false, /*findInComments*/ false, /*implementations*/false);
|
||||
return convertReferencedSymbols(referencedSymbols);
|
||||
}
|
||||
|
||||
@ -6006,7 +6155,7 @@ namespace ts {
|
||||
case SyntaxKind.ThisKeyword:
|
||||
// case SyntaxKind.SuperKeyword: TODO:GH#9268
|
||||
case SyntaxKind.StringLiteral:
|
||||
return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments);
|
||||
return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments, /*implementations*/false);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@ -6024,37 +6173,40 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencedSymbolsForNode(node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] {
|
||||
function getReferencedSymbolsForNode(node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean, implementations: boolean): ReferencedSymbol[] {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
|
||||
// Labels
|
||||
if (isLabelName(node)) {
|
||||
if (isJumpStatementTarget(node)) {
|
||||
const labelDefinition = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
|
||||
// if we have a label definition, look within its statement for references, if not, then
|
||||
// the label is undefined and we have no results..
|
||||
return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : undefined;
|
||||
}
|
||||
else {
|
||||
// it is a label definition and not a target, search within the parent labeledStatement
|
||||
return getLabelReferencesInNode(node.parent, <Identifier>node);
|
||||
}
|
||||
}
|
||||
|
||||
if (isThis(node)) {
|
||||
return getReferencesForThisKeyword(node, sourceFiles);
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.SuperKeyword) {
|
||||
return getReferencesForSuperKeyword(node);
|
||||
}
|
||||
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
|
||||
if (!symbol && node.kind === SyntaxKind.StringLiteral) {
|
||||
return getReferencesForStringLiteral(<StringLiteral>node, sourceFiles);
|
||||
if (!implementations) {
|
||||
// Labels
|
||||
if (isLabelName(node)) {
|
||||
if (isJumpStatementTarget(node)) {
|
||||
const labelDefinition = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
|
||||
// if we have a label definition, look within its statement for references, if not, then
|
||||
// the label is undefined and we have no results..
|
||||
return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : undefined;
|
||||
}
|
||||
else {
|
||||
// it is a label definition and not a target, search within the parent labeledStatement
|
||||
return getLabelReferencesInNode(node.parent, <Identifier>node);
|
||||
}
|
||||
}
|
||||
|
||||
if (isThis(node)) {
|
||||
return getReferencesForThisKeyword(node, sourceFiles);
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.SuperKeyword) {
|
||||
return getReferencesForSuperKeyword(node);
|
||||
}
|
||||
|
||||
|
||||
if (!symbol && node.kind === SyntaxKind.StringLiteral) {
|
||||
return getReferencesForStringLiteral(<StringLiteral>node, sourceFiles);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Could not find a symbol e.g. unknown identifier
|
||||
if (!symbol) {
|
||||
// Can't have references to something that we have no symbol for.
|
||||
@ -6084,9 +6236,11 @@ namespace ts {
|
||||
// Maps from a symbol ID to the ReferencedSymbol entry in 'result'.
|
||||
const symbolToIndex: number[] = [];
|
||||
|
||||
const indexToSymbol: {[index: number]: Symbol} = {};
|
||||
|
||||
if (scope) {
|
||||
result = [];
|
||||
getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex);
|
||||
getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex, indexToSymbol);
|
||||
}
|
||||
else {
|
||||
const internedName = getInternedName(symbol, node, declarations);
|
||||
@ -6097,11 +6251,15 @@ namespace ts {
|
||||
|
||||
if (nameTable[internedName] !== undefined) {
|
||||
result = result || [];
|
||||
getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex);
|
||||
getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex, indexToSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (implementations) {
|
||||
return filterToImplementations(node, symbol, result, indexToSymbol);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
function getDefinition(symbol: Symbol): DefinitionInfo {
|
||||
@ -6363,7 +6521,8 @@ namespace ts {
|
||||
findInStrings: boolean,
|
||||
findInComments: boolean,
|
||||
result: ReferencedSymbol[],
|
||||
symbolToIndex: number[]): void {
|
||||
symbolToIndex: number[],
|
||||
indexToSymbol: {[index: number]: Symbol}): void {
|
||||
|
||||
const sourceFile = container.getSourceFile();
|
||||
const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
|
||||
@ -6430,7 +6589,6 @@ namespace ts {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
function getReferencedSymbol(symbol: Symbol): ReferencedSymbol {
|
||||
@ -6439,6 +6597,7 @@ namespace ts {
|
||||
if (index === undefined) {
|
||||
index = result.length;
|
||||
symbolToIndex[symbolId] = index;
|
||||
indexToSymbol[index] = symbol;
|
||||
|
||||
result.push({
|
||||
definition: getDefinition(symbol),
|
||||
@ -6459,6 +6618,268 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function filterToImplementations(node: Node, searchSymbol: Symbol, refs: ReferencedSymbol[], indexToSymbol: {[index: number]: Symbol}): ReferencedSymbol[] {
|
||||
if (isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
||||
const type = typeChecker.getTypeAtLocation(node.parent.expression);
|
||||
|
||||
if (type.getFlags() & TypeFlags.Class) {
|
||||
// The search results in refs will contain all implementations of the property. This includes
|
||||
// all implementations in classes that parentSymbol extends from and sibling implementations
|
||||
// (i.e. implementations in classes with common ancestors that declare the property). We need to
|
||||
// filter the results to only the implementation used by parentSymbol's class and any implementations
|
||||
// in any sub-classes
|
||||
return filterToClassMemberImplementations(node.parent, searchSymbol, refs, indexToSymbol);
|
||||
}
|
||||
else if (type.getFlags() & (TypeFlags.UnionOrIntersection | TypeFlags.Interface)) {
|
||||
// If parentSymbol did not declare the property being accessed, then the search results
|
||||
// in refs will also contain references to the interfaces that parentSymbol inherits from.
|
||||
// We need to filter out any implementations of those parent interfaces in addition to filtering out the
|
||||
// non-implementation references
|
||||
return filterReferenceEntries(refs, (entry) => {
|
||||
const impl = getImplementationFromEntry(entry);
|
||||
if (impl) {
|
||||
const entryNode = getTokenAtPosition(getValidSourceFile(entry.fileName), entry.textSpan.start);
|
||||
const element = getContainingObjectLiteralElement(entryNode);
|
||||
if (element && element.parent && element.parent.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
const objType = getDeclaredTypeOfObjectLiteralExpression(element.parent);
|
||||
|
||||
if (objType && typeChecker.isTypeAssignableTo(objType, type)) {
|
||||
return impl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const defClass = getContainingClass(entryNode);
|
||||
if (defClass) {
|
||||
const classType = typeChecker.getTypeAtLocation(defClass);
|
||||
if (typeChecker.isTypeAssignableTo(classType, type)) {
|
||||
return impl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return filterReferenceEntries(refs, getImplementationFromEntry);
|
||||
}
|
||||
|
||||
function getDeclaredTypeOfObjectLiteralExpression(node: Node): Type {
|
||||
if (node && node.parent) {
|
||||
if (node.parent.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
return getDeclaredTypeOfObjectLiteralExpression(node.parent);
|
||||
}
|
||||
|
||||
if (isVariableLike(node.parent) && node.parent.type) {
|
||||
return typeChecker.getTypeAtLocation(node.parent.type);
|
||||
}
|
||||
else if (node.parent.kind === SyntaxKind.ReturnStatement) {
|
||||
const containerSig = typeChecker.getSignatureFromDeclaration(getContainingFunction(node));
|
||||
return typeChecker.getReturnTypeOfSignature(containerSig);
|
||||
}
|
||||
else if (node.parent.kind === SyntaxKind.TypeAssertionExpression) {
|
||||
return typeChecker.getTypeAtLocation((<TypeAssertion>node.parent).type);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function filterReferenceEntries(refs: ReferencedSymbol[], filterer: (x: ReferenceEntry) => ReferenceEntry): ReferencedSymbol[] {
|
||||
const result: ReferencedSymbol[] = [];
|
||||
forEach(refs, (ref) => {
|
||||
const filtered: ReferenceEntry[] = [];
|
||||
forEach(ref.references, (entry) => {
|
||||
const filteredEntry = filterer(entry);
|
||||
if (filteredEntry) {
|
||||
filtered.push(filteredEntry);
|
||||
}
|
||||
});
|
||||
if (filtered.length) {
|
||||
result.push({ definition: ref.definition, references: filtered });
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function filterToClassMemberImplementations(parent: PropertyAccessExpression, searchSymbol: Symbol, refs: ReferencedSymbol[], indexToSymbol: {[index: number]: Symbol}): ReferencedSymbol[] {
|
||||
// Need to find out what class this member is being accessed on
|
||||
const type = typeChecker.getTypeAtLocation(parent.expression);
|
||||
const classHierarchy: Symbol[] = getClassHierarchy(searchSymbol.parent);
|
||||
|
||||
let lowest: ReferencedSymbol[];
|
||||
let lowestRank: number;
|
||||
|
||||
// Filter down the references to those that refer to implementations of the symbol. That is, implementations
|
||||
// from subclasses of the parent class and the lowest implementation in the parent class' class hierarchy
|
||||
forEach(refs, (refSymbol, index) => {
|
||||
const actual = indexToSymbol[index];
|
||||
|
||||
// We only care about implementations in classes in the hierarchy
|
||||
if (actual.parent.getFlags() & SymbolFlags.Interface) {
|
||||
return;
|
||||
}
|
||||
const rank = classHierarchy.indexOf(actual.parent);
|
||||
|
||||
// No need to check anything past the lowest we have found so far
|
||||
if (lowest && rank > lowestRank) {
|
||||
return;
|
||||
}
|
||||
|
||||
const implementations: ReferenceEntry[] = [];
|
||||
forEach(refSymbol.references, (entry) => {
|
||||
const impl = getImplementationFromEntry(entry, type, classHierarchy);
|
||||
if (impl) {
|
||||
implementations.push(impl);
|
||||
}
|
||||
});
|
||||
if (implementations.length) {
|
||||
if (!lowest || rank < lowestRank) {
|
||||
lowest = [{ definition: refSymbol.definition, references: implementations }];
|
||||
lowestRank = rank;
|
||||
}
|
||||
else if (rank === lowestRank) {
|
||||
lowest.push({ definition: refSymbol.definition, references: implementations });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return lowest;
|
||||
}
|
||||
|
||||
function getImplementationFromEntry(entry: ReferenceEntry, base?: Type, hierarchy?: Symbol[]): ReferenceEntry {
|
||||
const sourceFile = getValidSourceFile(entry.fileName);
|
||||
const refNode = getTouchingPropertyName(sourceFile, entry.textSpan.start);
|
||||
|
||||
// Check to make sure this reference is either part of a sub class or a class that we explicitly
|
||||
// inherit from in the class hierarchy
|
||||
if (isMemberOfSubOrParentClass(refNode, base, hierarchy) && refNode.kind === SyntaxKind.Identifier) {
|
||||
// Check if we found a function/propertyAssignment/method with an implementation or initializer
|
||||
if (isIdentifierOfImplementation(<Identifier>refNode)) {
|
||||
return getReferenceEntryFromNode(refNode.parent);
|
||||
}
|
||||
else if (refNode.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
// Go ahead and dereference the shorthand assignment by going to its definition
|
||||
return getReferenceEntryForShorthandPropertyAssignment(refNode, typeChecker);
|
||||
}
|
||||
|
||||
// Check if the node is within an extends or implements clause
|
||||
const containingHeritageClause = getContainingClassHeritageClause(refNode);
|
||||
if (containingHeritageClause) {
|
||||
return getReferenceEntryFromNode(containingHeritageClause.parent);
|
||||
}
|
||||
|
||||
// If we got a type reference, try and see if the reference applies to any expressions that can implement an interface
|
||||
const containingTypeReference = getContainingTypeReference(refNode);
|
||||
if (containingTypeReference) {
|
||||
const parent = containingTypeReference.parent;
|
||||
if (isVariableLike(parent) && parent.type === containingTypeReference && parent.initializer && isImplementationExpression(parent.initializer)) {
|
||||
return getReferenceEntryFromNode(parent.initializer);
|
||||
}
|
||||
else if (isFunctionLike(parent) && parent.type === containingTypeReference && parent.body && parent.body.kind === SyntaxKind.Block) {
|
||||
return forEachReturnStatement(<Block>parent.body, (returnStatement) => {
|
||||
if (returnStatement.expression && isImplementationExpression(returnStatement.expression)) {
|
||||
return getReferenceEntryFromNode(returnStatement.expression);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (isTypeAssertionExpression(parent) && isImplementationExpression(parent.expression)) {
|
||||
return getReferenceEntryFromNode(parent.expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isMemberOfSubOrParentClass(reference: Node, base: Type, hierarchy: Symbol[]) {
|
||||
if (!base || !hierarchy) {
|
||||
return true;
|
||||
}
|
||||
const referenceSymbol = typeChecker.getSymbolAtLocation(reference);
|
||||
if (referenceSymbol && referenceSymbol.parent) {
|
||||
if (hierarchy.indexOf(referenceSymbol.parent) !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const referenceParentDeclarations = referenceSymbol.parent.getDeclarations();
|
||||
if (referenceParentDeclarations.length) {
|
||||
const referenceParentType = typeChecker.getTypeAtLocation(referenceParentDeclarations[0]);
|
||||
return typeChecker.isTypeAssignableTo(referenceParentType, base);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getContainingTypeReference(node: Node): Node {
|
||||
if (node) {
|
||||
if (node.kind === SyntaxKind.TypeReference) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) {
|
||||
return getContainingTypeReference(node.parent);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getContainingClassHeritageClause(node: Node): HeritageClause {
|
||||
if (node) {
|
||||
if (node.kind === SyntaxKind.ExpressionWithTypeArguments
|
||||
&& node.parent.kind === SyntaxKind.HeritageClause
|
||||
&& isClassLike(node.parent.parent)) {
|
||||
return <HeritageClause>node.parent;
|
||||
}
|
||||
|
||||
else if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
return getContainingClassHeritageClause(node.parent);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is an expression that could be used to implement an interface.
|
||||
*/
|
||||
function isImplementationExpression(node: Expression): boolean {
|
||||
// Unwrap parentheses
|
||||
if (node.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
return isImplementationExpression((<ParenthesizedExpression>node).expression);
|
||||
}
|
||||
|
||||
return node.kind === SyntaxKind.ArrowFunction ||
|
||||
node.kind === SyntaxKind.FunctionExpression ||
|
||||
node.kind === SyntaxKind.ObjectLiteralExpression ||
|
||||
node.kind === SyntaxKind.ClassExpression;
|
||||
}
|
||||
|
||||
function getClassHierarchy(symbol: Symbol) {
|
||||
const classes: Symbol[] = [];
|
||||
getClassHierarchyRecursive(symbol, classes, createMap<Symbol>());
|
||||
return classes;
|
||||
|
||||
function getClassHierarchyRecursive(symbol: Symbol, result: Symbol[], previousIterationSymbolsCache: SymbolTable) {
|
||||
if (hasProperty(previousIterationSymbolsCache, symbol.name)) {
|
||||
return;
|
||||
}
|
||||
previousIterationSymbolsCache[symbol.name] = symbol;
|
||||
|
||||
if (result.indexOf(symbol) !== -1) {
|
||||
return;
|
||||
}
|
||||
result.push(symbol);
|
||||
forEach(symbol.getDeclarations(), (decl) => {
|
||||
if (isClassLike(decl)) {
|
||||
const heritage = getClassExtendsHeritageClauseElement(decl);
|
||||
if (heritage) {
|
||||
const type = typeChecker.getTypeAtLocation(heritage);
|
||||
if (type && type.symbol) {
|
||||
getClassHierarchyRecursive(type.symbol, result, previousIterationSymbolsCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesForSuperKeyword(superKeyword: Node): ReferencedSymbol[] {
|
||||
let searchSpaceNode = getSuperContainer(superKeyword, /*stopOnFunctions*/ false);
|
||||
if (!searchSpaceNode) {
|
||||
@ -8310,6 +8731,7 @@ namespace ts {
|
||||
getQuickInfoAtPosition,
|
||||
getDefinitionAtPosition,
|
||||
getTypeDefinitionAtPosition,
|
||||
getImplementationAtPosition,
|
||||
getReferencesAtPosition,
|
||||
findReferences,
|
||||
getOccurrencesAtPosition,
|
||||
|
||||
@ -173,6 +173,12 @@ namespace ts {
|
||||
*/
|
||||
getTypeDefinitionAtPosition(fileName: string, position: number): string;
|
||||
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type:
|
||||
* { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean, isDefinition?: boolean }[]
|
||||
*/
|
||||
getImplementationAtPosition(fileName: string, position: number): string;
|
||||
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type:
|
||||
* { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean, isDefinition?: boolean }[]
|
||||
@ -772,6 +778,19 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
/// GOTO Implementation
|
||||
|
||||
/**
|
||||
* Computes the implementation location of the symbol
|
||||
* at the requested position.
|
||||
*/
|
||||
public getImplementationAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
`getImplementationAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getImplementationAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
public getRenameInfo(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
`getRenameInfo('${fileName}', ${position})`,
|
||||
|
||||
@ -110,6 +110,7 @@ declare namespace FourSlashInterface {
|
||||
eof(): void;
|
||||
definition(definitionIndex?: number): void;
|
||||
type(definitionIndex?: number): void;
|
||||
implementation(implementationIndex?: number): void;
|
||||
position(position: number, fileIndex?: number): any;
|
||||
position(position: number, fileName?: string): any;
|
||||
file(index: number, content?: string, scriptKindName?: string): any;
|
||||
@ -134,6 +135,7 @@ declare namespace FourSlashInterface {
|
||||
quickInfoExists(): void;
|
||||
definitionCountIs(expectedCount: number): void;
|
||||
typeDefinitionCountIs(expectedCount: number): void;
|
||||
implementationCountIs(expectedCount: number): void;
|
||||
definitionLocationExists(): void;
|
||||
verifyDefinitionsName(name: string, containerName: string): void;
|
||||
isValidBraceCompletionAtPosition(openingBrace?: string): void;
|
||||
@ -226,6 +228,7 @@ declare namespace FourSlashInterface {
|
||||
getSyntacticDiagnostics(expected: string): void;
|
||||
getSemanticDiagnostics(expected: string): void;
|
||||
ProjectInfo(expected: string[]): void;
|
||||
allRangesAppearInImplementationList(): void;
|
||||
}
|
||||
class edit {
|
||||
backspace(count?: number): void;
|
||||
|
||||
12
tests/cases/fourslash/goToImplementationClassMethod_00.ts
Normal file
12
tests/cases/fourslash/goToImplementationClassMethod_00.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls made on members declared in a class
|
||||
|
||||
//// class Bar {
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// new Bar().hel/*reference*/lo;
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
21
tests/cases/fourslash/goToImplementationClassMethod_01.ts
Normal file
21
tests/cases/fourslash/goToImplementationClassMethod_01.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls made on member declared in an abstract class
|
||||
|
||||
//// abstract class AbstractBar {
|
||||
//// abstract he/*declaration*/llo(): void;
|
||||
//// }
|
||||
////
|
||||
//// class Bar extends AbstractBar{
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// function whatever(x: AbstractBar) {
|
||||
//// x.he/*reference*/llo();
|
||||
//// }
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
|
||||
goTo.marker("declaration");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
13
tests/cases/fourslash/goToImplementationEnum_00.ts
Normal file
13
tests/cases/fourslash/goToImplementationEnum_00.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls made on members of an enum
|
||||
|
||||
//// enum Foo {
|
||||
//// [|Foo1 = function initializer() { return 5 } ()|],
|
||||
//// Foo2 = 6
|
||||
//// }
|
||||
////
|
||||
//// Foo.Fo/*reference*/o1;
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
13
tests/cases/fourslash/goToImplementationEnum_01.ts
Normal file
13
tests/cases/fourslash/goToImplementationEnum_01.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls made on enum name
|
||||
|
||||
//// [|enum Foo {
|
||||
//// Foo1 = function initializer() { return 5 } (),
|
||||
//// Foo2 = 6
|
||||
//// }|]
|
||||
////
|
||||
//// Fo/*reference*/o;
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,27 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should return method implementations in object literals within variable-like declarations
|
||||
|
||||
//// interface Foo {
|
||||
//// he/*declaration*/llo: () => void
|
||||
//// }
|
||||
////
|
||||
//// var bar: Foo = { [|hello: helloImpl|] };
|
||||
////
|
||||
//// function helloImpl () {}
|
||||
////
|
||||
//// function whatever(x: Foo = { [|hello() {/**1*/}|] }) {
|
||||
//// x.he/*function_call*/llo()
|
||||
//// }
|
||||
////
|
||||
//// class Bar {
|
||||
//// x: Foo = { [|hello() {/*2*/}|] }
|
||||
////
|
||||
//// constructor(public f: Foo = { [|hello() {/**3*/}|] } ) {}
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
|
||||
goTo.marker("declaration");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,25 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should return implementations in a simple class
|
||||
|
||||
//// interface Foo {
|
||||
//// hel/*declaration*/lo(): void;
|
||||
//// okay?: number;
|
||||
//// }
|
||||
////
|
||||
//// class Bar implements Foo {
|
||||
//// [|hello() {}|]
|
||||
//// public sure() {}
|
||||
//// }
|
||||
////
|
||||
//// function whatever(a: Foo) {
|
||||
//// a.he/*function_call*/llo();
|
||||
//// }
|
||||
////
|
||||
//// whatever(new Bar());
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
|
||||
goTo.marker("declaration");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,25 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should return implementations when left hand side of function call is an abstract class
|
||||
|
||||
//// interface Foo {
|
||||
//// he/*declaration*/llo(): void
|
||||
//// }
|
||||
////
|
||||
//// abstract class AbstractBar implements Foo {
|
||||
//// abstract hello(): void;
|
||||
//// }
|
||||
////
|
||||
//// class Bar extends AbstractBar {
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// function whatever(a: AbstractBar) {
|
||||
//// a.he/*function_call*/llo();
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
|
||||
goTo.marker("declaration");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,25 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should not return super implementations when method is implemented in class
|
||||
|
||||
//// interface Foo {
|
||||
//// hello (): void;
|
||||
//// }
|
||||
////
|
||||
//// class Bar extends SuperBar {
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// class SuperBar implements Foo {
|
||||
//// hello() {} // should not show up
|
||||
//// }
|
||||
////
|
||||
//// class OtherBar implements Foo {
|
||||
//// hello() {} // should not show up
|
||||
//// }
|
||||
////
|
||||
//// new Bar().hel/*function_call*/lo();
|
||||
//// new Bar()["hello"]();
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,26 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should return implementation in class and all sub-classes of target
|
||||
|
||||
//// interface Foo {
|
||||
//// hello (): void;
|
||||
//// }
|
||||
////
|
||||
//// class Bar extends SuperBar {
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// class SuperBar implements Foo {
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// class OtherBar implements Foo {
|
||||
//// hello() {} // should not show up
|
||||
//// }
|
||||
////
|
||||
//// function (x: SuperBar) {
|
||||
//// x.he/*function_call*/llo()
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,38 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should not return implementations in classes with a shared parent that implements the interface
|
||||
|
||||
//// interface Foo {
|
||||
//// hello (): void;
|
||||
//// }
|
||||
////
|
||||
//// class SuperBar implements Foo {
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// class Bar extends SuperBar {
|
||||
//// hello2() {}
|
||||
//// }
|
||||
////
|
||||
//// class OtherBar extends SuperBar {
|
||||
//// [|hello() {}|] // This could be considered a false positive because it does not extend Bar. Returned because it shares a common ancestor and is structurally equivalent
|
||||
//// hello2() {}
|
||||
//// hello3() {}
|
||||
//// }
|
||||
////
|
||||
//// class NotRelatedToBar {
|
||||
//// hello() {} // Equivalent to last case, but shares no common ancestors with Bar and so is not returned
|
||||
//// hello2() {}
|
||||
//// hello3() {}
|
||||
//// }
|
||||
////
|
||||
//// class NotBar extends SuperBar {
|
||||
//// hello() {} // Should not be returned because it is not structurally equivalent to Bar
|
||||
//// }
|
||||
////
|
||||
//// function whatever(x: Bar) {
|
||||
//// x.he/*function_call*/llo()
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,49 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should not return references to parent interfaces even if the method is declared there
|
||||
|
||||
//// interface SuperFoo {
|
||||
//// hello (): void;
|
||||
//// }
|
||||
////
|
||||
//// interface Foo extends SuperFoo {
|
||||
//// someOtherFunction(): void;
|
||||
//// }
|
||||
////
|
||||
//// class Bar implements Foo {
|
||||
//// [|hello() {}|]
|
||||
//// someOtherFunction() {}
|
||||
//// }
|
||||
////
|
||||
//// function createFoo(): Foo {
|
||||
//// return {
|
||||
//// [|hello() {}|],
|
||||
//// someOtherFunction() {}
|
||||
//// };
|
||||
//// }
|
||||
////
|
||||
//// var y: Foo = {
|
||||
//// [|hello() {}|],
|
||||
//// someOtherFunction() {}
|
||||
//// };
|
||||
////
|
||||
//// class FooLike implements SuperFoo {
|
||||
//// [|hello() {}|] // This case could be considered a false positive. It does not explicitly implement Foo but does implement it structurally and it shares a common ancestor
|
||||
//// someOtherFunction() {}
|
||||
//// }
|
||||
////
|
||||
//// class NotRelatedToFoo {
|
||||
//// hello() {} // This case is equivalent to the last case, but is not returned because it does not share a common ancestor with Foo
|
||||
//// someOtherFunction() {}
|
||||
//// }
|
||||
////
|
||||
//// class NotFoo implements SuperFoo {
|
||||
//// hello() {} // We only want implementations of Foo, even though the function is declared in SuperFoo
|
||||
//// }
|
||||
////
|
||||
//// function (x: Foo) {
|
||||
//// x.he/*function_call*/llo()
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,23 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls made on this
|
||||
|
||||
//// interface Foo {
|
||||
//// hello (): void;
|
||||
//// }
|
||||
////
|
||||
//// class SuperBar implements Foo {
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// class Bar extends SuperBar {
|
||||
//// whatever() { this.he/*function_call*/llo(); }
|
||||
//// }
|
||||
////
|
||||
//// class SubBar extends Bar {
|
||||
//// hello() {}
|
||||
//// }
|
||||
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,30 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls made on super
|
||||
|
||||
//// interface Foo {
|
||||
//// hello (): void;
|
||||
//// }
|
||||
////
|
||||
//// class SubBar extends Bar {
|
||||
//// hello() {}
|
||||
//// }
|
||||
////
|
||||
//// class Bar extends SuperBar {
|
||||
//// hello() {}
|
||||
////
|
||||
//// whatever() {
|
||||
//// super.he/*function_call*/llo();
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// class SuperBar extends MegaBar {
|
||||
//// [|hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// class MegaBar implements Foo {
|
||||
//// hello() {}
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,36 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle intersection types
|
||||
|
||||
//// interface Foo {
|
||||
//// hello(): void;
|
||||
//// aloha(): void;
|
||||
//// }
|
||||
////
|
||||
//// interface Bar {
|
||||
//// hello(): void;
|
||||
//// goodbye(): void;
|
||||
//// }
|
||||
////
|
||||
//// class FooImpl implements Foo {
|
||||
//// hello() {/**FooImpl*/}
|
||||
//// aloha() {}
|
||||
//// }
|
||||
////
|
||||
//// class BarImpl implements Bar {
|
||||
//// hello() {/**BarImpl*/}
|
||||
//// goodbye() {}
|
||||
//// }
|
||||
////
|
||||
//// class FooAndBarImpl implements Foo, Bar {
|
||||
//// [|hello() {/**FooAndBarImpl*/}|]
|
||||
//// aloha() {}
|
||||
//// goodbye() {}
|
||||
//// }
|
||||
////
|
||||
//// function someFunction(x: Foo & Bar) {
|
||||
//// x.he/*function_call*/llo();
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,36 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle union types
|
||||
|
||||
//// interface Foo {
|
||||
//// hello(): void;
|
||||
//// aloha(): void;
|
||||
//// }
|
||||
////
|
||||
//// interface Bar {
|
||||
//// hello(): void;
|
||||
//// goodbye(): void;
|
||||
//// }
|
||||
////
|
||||
//// class FooImpl implements Foo {
|
||||
//// [|hello() {/**FooImpl*/}|]
|
||||
//// aloha() {}
|
||||
//// }
|
||||
////
|
||||
//// class BarImpl implements Bar {
|
||||
//// [|hello() {/**BarImpl*/}|]
|
||||
//// goodbye() {}
|
||||
//// }
|
||||
////
|
||||
//// class FooAndBarImpl implements Foo, Bar {
|
||||
//// [|hello() {/**FooAndBarImpl*/}|]
|
||||
//// aloha() {}
|
||||
//// goodbye() {}
|
||||
//// }
|
||||
////
|
||||
//// function someFunction(x: Foo | Bar) {
|
||||
//// x.he/*function_call*/llo();
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle members of object literals in type assertion expressions
|
||||
|
||||
//// interface Foo {
|
||||
//// hel/*reference*/lo(): void;
|
||||
//// }
|
||||
////
|
||||
//// var x = <Foo> { [|hello: () => {}|] };
|
||||
//// var y = <Foo> (((({ [|hello: () => {}|] }))));
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,23 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle property assignments in object literals within variable like declarations
|
||||
|
||||
//// interface Foo {
|
||||
//// hello: number
|
||||
//// }
|
||||
////
|
||||
//// var bar: Foo = { [|hello: 5|] };
|
||||
////
|
||||
////
|
||||
//// function whatever(x: Foo = { [|hello: 5 * 9|] }) {
|
||||
//// x.he/*reference*/llo
|
||||
//// }
|
||||
////
|
||||
//// class Bar {
|
||||
//// x: Foo = { [|hello: 6|] }
|
||||
////
|
||||
//// constructor(public f: Foo = { [|hello: 7|] } ) {}
|
||||
//// }
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,16 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle property assignments within class declarations
|
||||
|
||||
//// interface Foo { hello: number }
|
||||
////
|
||||
//// class Bar implements Foo {
|
||||
//// [|hello = 5 * 9;|]
|
||||
//// }
|
||||
////
|
||||
//// function whatever(foo: Foo) {
|
||||
//// foo.he/*reference*/llo;
|
||||
//// }
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
26
tests/cases/fourslash/goToImplementationInterface_00.ts
Normal file
26
tests/cases/fourslash/goToImplementationInterface_00.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to definitions in object literals in variable like declarations when invoked on interface
|
||||
|
||||
//// interface Fo/*interface_definition*/o {
|
||||
//// hello: () => void
|
||||
//// }
|
||||
////
|
||||
//// interface Baz extends Foo {}
|
||||
////
|
||||
//// var bar: Foo = [|{ hello: helloImpl /**0*/ }|];
|
||||
////
|
||||
//// function helloImpl () {}
|
||||
////
|
||||
//// function whatever(x: Foo = [|{ hello() {/**1*/} }|] ) {
|
||||
//// }
|
||||
////
|
||||
//// class Bar {
|
||||
//// x: Foo = [|{ hello() {/*2*/} }|]
|
||||
////
|
||||
//// constructor(public f: Foo = [|{ hello() {/**3*/} }|] ) {}
|
||||
//// }
|
||||
|
||||
|
||||
goTo.marker("interface_definition");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
25
tests/cases/fourslash/goToImplementationInterface_01.ts
Normal file
25
tests/cases/fourslash/goToImplementationInterface_01.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
//// interface Fo/*interface_definition*/o { hello(): void }
|
||||
////
|
||||
//// [|class SuperBar implements Foo {
|
||||
//// hello () {}
|
||||
//// }|]
|
||||
////
|
||||
//// [|abstract class AbstractBar implements Foo {
|
||||
//// abstract hello (): void;
|
||||
//// }|]
|
||||
////
|
||||
//// class Bar extends SuperBar {
|
||||
//// }
|
||||
////
|
||||
//// class NotAbstractBar extends AbstractBar {
|
||||
//// hello () {}
|
||||
//// }
|
||||
////
|
||||
//// var x = new SuperBar();
|
||||
//// var y: SuperBar = new SuperBar();
|
||||
//// var z: AbstractBar = new NotAbstractBar();
|
||||
|
||||
goTo.marker("interface_definition");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
20
tests/cases/fourslash/goToImplementationInterface_02.ts
Normal file
20
tests/cases/fourslash/goToImplementationInterface_02.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to definitions in object literals in return statements of functions with the type of the interface
|
||||
|
||||
//// interface Fo/*interface_definition*/o { hello: () => void }
|
||||
////
|
||||
//// function createFoo(): Foo {
|
||||
//// return [|{
|
||||
//// hello() {}
|
||||
//// }|];
|
||||
//// }
|
||||
////
|
||||
//// function createFooLike() {
|
||||
//// return {
|
||||
//// hello() {}
|
||||
//// };
|
||||
//// }
|
||||
|
||||
goTo.marker("interface_definition");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
10
tests/cases/fourslash/goToImplementationInterface_03.ts
Normal file
10
tests/cases/fourslash/goToImplementationInterface_03.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to object literals within cast expressions when invoked on interface
|
||||
|
||||
//// interface Fo/*interface_definition*/o { hello: () => void }
|
||||
////
|
||||
//// var x = <Foo> [|{ hello: () => {} }|];
|
||||
|
||||
goTo.marker("interface_definition");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
22
tests/cases/fourslash/goToImplementationInterface_04.ts
Normal file
22
tests/cases/fourslash/goToImplementationInterface_04.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to function literals that implement the interface within variable like declarations when invoked on an interface
|
||||
|
||||
//// interface Fo/*interface_definition*/o {
|
||||
//// (a: number): void
|
||||
//// }
|
||||
////
|
||||
//// var bar: Foo = [|(a) => {/**0*/}|];
|
||||
////
|
||||
//// function whatever(x: Foo = [|(a) => {/**1*/}|] ) {
|
||||
//// }
|
||||
////
|
||||
//// class Bar {
|
||||
//// x: Foo = [|(a) => {/**2*/}|]
|
||||
////
|
||||
//// constructor(public f: Foo = [|function(a) {}|] ) {}
|
||||
//// }
|
||||
|
||||
|
||||
goTo.marker("interface_definition");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
14
tests/cases/fourslash/goToImplementationInterface_05.ts
Normal file
14
tests/cases/fourslash/goToImplementationInterface_05.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to function literals that implement the interface within type assertions when invoked on an interface
|
||||
|
||||
//// interface Fo/*interface_definition*/o {
|
||||
//// (a: number): void
|
||||
//// }
|
||||
////
|
||||
//// let bar2 = <Foo> [|function(a) {}|];
|
||||
////
|
||||
|
||||
|
||||
goTo.marker("interface_definition");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
16
tests/cases/fourslash/goToImplementationInterface_06.ts
Normal file
16
tests/cases/fourslash/goToImplementationInterface_06.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to class expressions that implement a constructor type
|
||||
|
||||
//// interface Fo/*interface_definition*/o {
|
||||
//// new (a: number): SomeOtherType;
|
||||
//// }
|
||||
////
|
||||
//// interface SomeOtherType {}
|
||||
////
|
||||
//// let x: Foo = [|class { constructor (a: number) {} }|];
|
||||
//// let y = <Foo> [|class { constructor (a: number) {} }|];
|
||||
|
||||
|
||||
goTo.marker("interface_definition");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
12
tests/cases/fourslash/goToImplementationInvalid.ts
Normal file
12
tests/cases/fourslash/goToImplementationInvalid.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should not crash when invoked on an invalid location
|
||||
|
||||
//// var x1 = 50/*0*/0;
|
||||
//// var x2 = "hel/*1*/lo";
|
||||
//// /*2*/
|
||||
|
||||
for(var i = 0; i < 3; i++) {
|
||||
goTo.marker("" + i);
|
||||
verify.implementationCountIs(0);
|
||||
}
|
||||
9
tests/cases/fourslash/goToImplementationLocal_00.ts
Normal file
9
tests/cases/fourslash/goToImplementationLocal_00.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should return definition of locally declared functions
|
||||
|
||||
//// he/*function_call*/llo();
|
||||
//// [|function hello() {}|]
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
9
tests/cases/fourslash/goToImplementationLocal_01.ts
Normal file
9
tests/cases/fourslash/goToImplementationLocal_01.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should return the defintion of locally defined variables
|
||||
|
||||
//// const [|hello = function() {}|];
|
||||
//// he/*function_call*/llo();
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
8
tests/cases/fourslash/goToImplementationLocal_02.ts
Normal file
8
tests/cases/fourslash/goToImplementationLocal_02.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
//// const x = { [|hello: () => {}|] };
|
||||
////
|
||||
//// x.he/*function_call*/llo();
|
||||
////
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
12
tests/cases/fourslash/goToImplementationLocal_03.ts
Normal file
12
tests/cases/fourslash/goToImplementationLocal_03.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should return the definition when invoked on variable assignment
|
||||
|
||||
//// let [|he/*local_var*/llo = {}|];
|
||||
////
|
||||
//// x.hello();
|
||||
////
|
||||
//// hello = {};
|
||||
////
|
||||
goTo.marker("local_var");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
10
tests/cases/fourslash/goToImplementationLocal_04.ts
Normal file
10
tests/cases/fourslash/goToImplementationLocal_04.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should return definition of function when invoked on the declaration
|
||||
|
||||
//// [|function he/*local_var*/llo() {}|]
|
||||
////
|
||||
//// hello();
|
||||
////
|
||||
goTo.marker("local_var");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
12
tests/cases/fourslash/goToImplementationLocal_05.ts
Normal file
12
tests/cases/fourslash/goToImplementationLocal_05.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls made the left hand side of a property access expression
|
||||
|
||||
//// class Bar {
|
||||
//// public hello() {}
|
||||
//// }
|
||||
////
|
||||
//// var [|someVar = new Bar()|];
|
||||
//// someVa/*reference*/r.hello();
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
12
tests/cases/fourslash/goToImplementationNamespace_00.ts
Normal file
12
tests/cases/fourslash/goToImplementationNamespace_00.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls on namespaces
|
||||
|
||||
//// [|namespace Foo {
|
||||
//// export function hello() {}
|
||||
//// }|]
|
||||
////
|
||||
//// let x = Fo/*reference*/o;
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
12
tests/cases/fourslash/goToImplementationNamespace_01.ts
Normal file
12
tests/cases/fourslash/goToImplementationNamespace_01.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle calls on modules
|
||||
|
||||
//// [|module Foo {
|
||||
//// export function hello() {}
|
||||
//// }|]
|
||||
////
|
||||
//// let x = Fo/*reference*/o;
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
12
tests/cases/fourslash/goToImplementationNamespace_02.ts
Normal file
12
tests/cases/fourslash/goToImplementationNamespace_02.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle property access expressions on namespaces
|
||||
|
||||
//// namespace Foo {
|
||||
//// [|export function hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// Foo.hell/*reference*/o();
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
12
tests/cases/fourslash/goToImplementationNamespace_03.ts
Normal file
12
tests/cases/fourslash/goToImplementationNamespace_03.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle property access expressions on namespaces
|
||||
|
||||
//// module Foo {
|
||||
//// [|export function hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// Foo.hell/*reference*/o();
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
26
tests/cases/fourslash/goToImplementationNamespace_04.ts
Normal file
26
tests/cases/fourslash/goToImplementationNamespace_04.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle sub-namespaces
|
||||
|
||||
//// /*parentNamespace*/namespace Foo {
|
||||
//// export function hello() {}
|
||||
//// }
|
||||
////
|
||||
//// /*parentNamespace2*/namespace Foo./*childNamespace*/Bar {
|
||||
//// export function okay() {}
|
||||
//// }
|
||||
////
|
||||
//// Fo/*parentReference*/o.hello();
|
||||
//// Foo.Ba/*childReference*/r.okay();
|
||||
|
||||
goTo.marker("parentReference");
|
||||
goTo.implementation(0);
|
||||
verify.caretAtMarker("parentNamespace");
|
||||
|
||||
goTo.marker("parentReference");
|
||||
goTo.implementation(1);
|
||||
verify.caretAtMarker("parentNamespace2");
|
||||
|
||||
goTo.marker("childReference");
|
||||
goTo.implementation();
|
||||
verify.caretAtMarker("childNamespace")
|
||||
26
tests/cases/fourslash/goToImplementationNamespace_05.ts
Normal file
26
tests/cases/fourslash/goToImplementationNamespace_05.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle sub-modules
|
||||
|
||||
//// /*parentModule*/module Foo {
|
||||
//// export function hello() {}
|
||||
//// }
|
||||
////
|
||||
//// /*parentModule2*/module Foo./*childModule*/Bar {
|
||||
//// export function okay() {}
|
||||
//// }
|
||||
////
|
||||
//// Fo/*parentReference*/o.hello();
|
||||
//// Foo.Ba/*childReference*/r.okay();
|
||||
|
||||
goTo.marker("parentReference");
|
||||
goTo.implementation(0);
|
||||
verify.caretAtMarker("parentModule");
|
||||
|
||||
goTo.marker("parentReference");
|
||||
goTo.implementation(1);
|
||||
verify.caretAtMarker("parentModule2");
|
||||
|
||||
goTo.marker("childReference");
|
||||
goTo.implementation();
|
||||
verify.caretAtMarker("childModule")
|
||||
28
tests/cases/fourslash/goToImplementationNamespace_06.ts
Normal file
28
tests/cases/fourslash/goToImplementationNamespace_06.ts
Normal file
@ -0,0 +1,28 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle types that are members of a namespace in type references and heritage clauses
|
||||
|
||||
//// namespace Foo {
|
||||
//// export interface Bar {
|
||||
//// hello(): void;
|
||||
//// }
|
||||
////
|
||||
//// [|class BarImpl implements Bar {
|
||||
//// hello() {}
|
||||
//// }|]
|
||||
//// }
|
||||
////
|
||||
//// [|class Baz implements Foo.Bar {
|
||||
//// hello() {}
|
||||
//// }|]
|
||||
////
|
||||
//// var someVar1 : Foo.Bar = [|{ hello: () => {/**1*/} }|];
|
||||
////
|
||||
//// var someVar2 = <Foo.Bar> [|{ hello: () => {/**2*/} }|];
|
||||
////
|
||||
//// function whatever(x: Foo.Ba/*reference*/r) {
|
||||
////
|
||||
//// }
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
28
tests/cases/fourslash/goToImplementationNamespace_07.ts
Normal file
28
tests/cases/fourslash/goToImplementationNamespace_07.ts
Normal file
@ -0,0 +1,28 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle types that are members of a module in type references and heritage clauses
|
||||
|
||||
//// module Foo {
|
||||
//// export interface Bar {
|
||||
//// hello(): void;
|
||||
//// }
|
||||
////
|
||||
//// [|class BarImpl implements Bar {
|
||||
//// hello() {}
|
||||
//// }|]
|
||||
//// }
|
||||
////
|
||||
//// [|class Baz implements Foo.Bar {
|
||||
//// hello() {}
|
||||
//// }|]
|
||||
////
|
||||
//// var someVar1 : Foo.Bar = [|{ hello: () => {/**1*/} }|];
|
||||
////
|
||||
//// var someVar2 = <Foo.Bar> [|{ hello: () => {/**2*/} }|];
|
||||
////
|
||||
//// function whatever(x: Foo.Ba/*reference*/r) {
|
||||
////
|
||||
//// }
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
@ -0,0 +1,43 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle shorthand property assignments of class constructors
|
||||
|
||||
//// interface Foo {
|
||||
//// someFunction(): void;
|
||||
//// }
|
||||
////
|
||||
//// interface FooConstructor {
|
||||
//// new (): Foo
|
||||
//// }
|
||||
////
|
||||
//// interface Bar {
|
||||
//// Foo: FooConstructor;
|
||||
//// }
|
||||
////
|
||||
//// var x = /*classExpression*/class Foo {
|
||||
//// createBarInClassExpression(): Bar {
|
||||
//// return {
|
||||
//// Fo/*classExpressionRef*/o
|
||||
//// };
|
||||
//// }
|
||||
////
|
||||
//// someFunction() {}
|
||||
//// }
|
||||
////
|
||||
//// /*declaredClass*/class Foo {
|
||||
////
|
||||
//// }
|
||||
////
|
||||
//// function createBarUsingClassDeclaration(): Bar {
|
||||
//// return {
|
||||
//// Fo/*declaredClassRef*/o
|
||||
//// };
|
||||
//// }
|
||||
|
||||
goTo.marker("classExpressionRef");
|
||||
goTo.implementation();
|
||||
verify.caretAtMarker("classExpression");
|
||||
|
||||
goTo.marker("declaredClassRef");
|
||||
goTo.implementation();
|
||||
verify.caretAtMarker("declaredClass");
|
||||
@ -0,0 +1,48 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should handle shorthand property assignments of class constructors when invoked on member of interface
|
||||
|
||||
//// interface Foo {
|
||||
//// someFunction(): void;
|
||||
//// }
|
||||
////
|
||||
//// interface FooConstructor {
|
||||
//// new (): Foo
|
||||
//// }
|
||||
////
|
||||
//// interface Bar {
|
||||
//// Foo: FooConstructor;
|
||||
//// }
|
||||
////
|
||||
//// // Class expression that gets used in a bar implementation
|
||||
//// var x = [|class Foo {
|
||||
//// createBarInClassExpression(): Bar {
|
||||
//// return {
|
||||
//// Foo
|
||||
//// };
|
||||
//// }
|
||||
////
|
||||
//// someFunction() {}
|
||||
//// }|];
|
||||
////
|
||||
//// // Class declaration that gets used in a bar implementation. This class has multiple definitions
|
||||
//// // (the class declaration and the interface above), but we only want the class returned
|
||||
//// [|class Foo {
|
||||
////
|
||||
//// }|]
|
||||
////
|
||||
//// function createBarUsingClassDeclaration(): Bar {
|
||||
//// return {
|
||||
//// Foo
|
||||
//// };
|
||||
//// }
|
||||
////
|
||||
//// // Class expression that does not get used in a bar implementation
|
||||
//// var y = class Foo {
|
||||
//// someFunction() {}
|
||||
//// };
|
||||
////
|
||||
//// createBarUsingClassDeclaration().Fo/*reference*/o;
|
||||
|
||||
goTo.marker("reference");
|
||||
verify.allRangesAppearInImplementationList();;
|
||||
@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to implementation of properties that are assigned to implementations of an interface using shorthand notation
|
||||
|
||||
//// interface Foo {
|
||||
//// hello(): void;
|
||||
//// }
|
||||
////
|
||||
//// function createFoo(): Foo {
|
||||
//// return {
|
||||
//// hello
|
||||
//// };
|
||||
////
|
||||
//// [|function hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// function whatever(x: Foo) {
|
||||
//// x.h/*function_call*/ello();
|
||||
//// }
|
||||
|
||||
goTo.marker("function_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
16
tests/cases/fourslash/goToImplementationSuper_00.ts
Normal file
16
tests/cases/fourslash/goToImplementationSuper_00.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to super class declaration when invoked on a super call expression
|
||||
|
||||
//// [|class Foo {
|
||||
//// constructor() {}
|
||||
//// }|]
|
||||
////
|
||||
//// class Bar extends Foo {
|
||||
//// constructor() {
|
||||
//// su/*super_call*/per();
|
||||
//// }
|
||||
//// }
|
||||
|
||||
goTo.marker("super_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
16
tests/cases/fourslash/goToImplementationSuper_01.ts
Normal file
16
tests/cases/fourslash/goToImplementationSuper_01.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to the super class declaration when invoked on the super keyword in a property access expression
|
||||
|
||||
//// [|class Foo {
|
||||
//// hello() {}
|
||||
//// }|]
|
||||
////
|
||||
//// class Bar extends Foo {
|
||||
//// hello() {
|
||||
//// sup/*super_call*/er.hello();
|
||||
//// }
|
||||
//// }
|
||||
|
||||
goTo.marker("super_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
14
tests/cases/fourslash/goToImplementationThis_00.ts
Normal file
14
tests/cases/fourslash/goToImplementationThis_00.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to class declaration when invoked on this keyword in property access expression
|
||||
|
||||
//// [|class Bar extends Foo {
|
||||
//// hello() {
|
||||
//// thi/*this_call*/s.whatever();
|
||||
//// }
|
||||
////
|
||||
//// whatever() {}
|
||||
//// }|]
|
||||
|
||||
goTo.marker("this_call");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
12
tests/cases/fourslash/goToImplementationThis_01.ts
Normal file
12
tests/cases/fourslash/goToImplementationThis_01.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Should go to class declaration when invoked on a this type reference
|
||||
|
||||
//// [|class Bar extends Foo {
|
||||
//// hello(): th/*this_type*/is {
|
||||
//// return this;
|
||||
//// }
|
||||
//// }|]
|
||||
|
||||
goTo.marker("this_type");
|
||||
verify.allRangesAppearInImplementationList();
|
||||
9
tests/cases/fourslash/server/implementation01.ts
Normal file
9
tests/cases/fourslash/server/implementation01.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/// <reference path="../fourslash.ts"/>
|
||||
|
||||
// @Filename: a.ts
|
||||
//// interface Fo/*1*/o {}
|
||||
//// /*2*/class Bar implements Foo {}
|
||||
|
||||
goTo.marker('1');
|
||||
goTo.implementation();
|
||||
verify.caretAtMarker('2');
|
||||
@ -0,0 +1,35 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: goToImplementationDifferentFile_Implementation.ts
|
||||
//// /*fooClassImplementation*/class FooImpl implements Foo {}
|
||||
////
|
||||
//// /*barClassImplementation*/class Bar {
|
||||
//// /*barHelloFunctionImplementation*/hello() {}
|
||||
//// }
|
||||
////
|
||||
|
||||
// @Filename: goToImplementationDifferentFile_Consumption.ts
|
||||
//// interface Fo/*fooClassReference*/o {}
|
||||
////
|
||||
//// var x = new B/*barClassReference*/ar();
|
||||
////
|
||||
//// x.hel/*barHelloFunctionReference*/lo();
|
||||
////
|
||||
//// /*thisImplementation*/class SomeClass {
|
||||
//// someMethod() {
|
||||
//// thi/*thisReference*/s.someMethod();
|
||||
//// }
|
||||
//// }
|
||||
|
||||
var markerList = [
|
||||
"fooClass",
|
||||
"barClass",
|
||||
"barHelloFunction",
|
||||
"this"
|
||||
];
|
||||
|
||||
markerList.forEach((marker) => {
|
||||
goTo.marker(marker + 'Reference');
|
||||
goTo.implementation();
|
||||
verify.caretAtMarker(marker + 'Implementation');
|
||||
});
|
||||
35
tests/cases/fourslash/shims/getImplementationAtPosition.ts
Normal file
35
tests/cases/fourslash/shims/getImplementationAtPosition.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: goToImplementationDifferentFile_Implementation.ts
|
||||
//// /*fooClassImplementation*/class FooImpl implements Foo {}
|
||||
////
|
||||
//// /*barClassImplementation*/class Bar {
|
||||
//// /*barHelloFunctionImplementation*/hello() {}
|
||||
//// }
|
||||
////
|
||||
|
||||
// @Filename: goToImplementationDifferentFile_Consumption.ts
|
||||
//// interface Fo/*fooClassReference*/o {}
|
||||
////
|
||||
//// var x = new B/*barClassReference*/ar();
|
||||
////
|
||||
//// x.hel/*barHelloFunctionReference*/lo();
|
||||
////
|
||||
//// /*thisImplementation*/class SomeClass {
|
||||
//// someMethod() {
|
||||
//// thi/*thisReference*/s.someMethod();
|
||||
//// }
|
||||
//// }
|
||||
|
||||
var markerList = [
|
||||
"fooClass",
|
||||
"barClass",
|
||||
"barHelloFunction",
|
||||
"this"
|
||||
];
|
||||
|
||||
markerList.forEach((marker) => {
|
||||
goTo.marker(marker + 'Reference');
|
||||
goTo.implementation();
|
||||
verify.caretAtMarker(marker + 'Implementation');
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user