Make goto-definition go to a signature declaration if possible

This commit is contained in:
Andy Hanson 2016-08-29 10:14:59 -07:00
parent b0f7f2f99c
commit 4514f8fde5
6 changed files with 98 additions and 29 deletions

View File

@ -1546,7 +1546,7 @@ namespace FourSlash {
public goToDefinition(definitionIndex: number) {
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
if (!definitions || !definitions.length) {
this.raiseError("goToDefinition failed - expected to at least one definition location but got 0");
this.raiseError("goToDefinition failed - expected to find at least one definition location but got 0");
}
if (definitionIndex >= definitions.length) {
@ -1561,7 +1561,7 @@ namespace FourSlash {
public goToTypeDefinition(definitionIndex: number) {
const definitions = this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
if (!definitions || !definitions.length) {
this.raiseError("goToTypeDefinition failed - expected to at least one definition location but got 0");
this.raiseError("goToTypeDefinition failed - expected to find at least one definition location but got 0");
}
if (definitionIndex >= definitions.length) {
@ -1582,7 +1582,7 @@ namespace FourSlash {
this.raiseError(`goToDefinition - expected to 0 definition locations but got ${definitions.length}`);
}
else if (!foundDefinitions && !negative) {
this.raiseError("goToDefinition - expected to at least one definition location but got 0");
this.raiseError("goToDefinition - expected to find at least one definition location but got 0");
}
}

View File

@ -2788,18 +2788,37 @@ namespace ts {
return node && node.parent && node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node;
}
function climbPastPropertyAccess(node: Node) {
return isRightSideOfPropertyAccess(node) ? node.parent : node;
}
function isCallExpressionTarget(node: Node): boolean {
if (isRightSideOfPropertyAccess(node)) {
node = node.parent;
}
return node && node.parent && node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
return !!getCallOrNewExpressionWorker(node, SyntaxKind.CallExpression);
}
function isNewExpressionTarget(node: Node): boolean {
if (isRightSideOfPropertyAccess(node)) {
node = node.parent;
return !!getCallOrNewExpressionWorker(node, SyntaxKind.NewExpression);
}
function getCallOrNewExpressionTargetingNode(node: Node): CallExpression | NewExpression | undefined {
return <CallExpression>getCallOrNewExpressionWorker(node, SyntaxKind.CallExpression) || <NewExpression>getCallOrNewExpressionWorker(node, SyntaxKind.NewExpression);
}
function tryGetCalledDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined {
const callOrNewExpression = getCallOrNewExpressionTargetingNode(node);
if (callOrNewExpression) {
const signature = typeChecker.getResolvedSignature(callOrNewExpression);
return signature.declaration;
}
return node && node.parent && node.parent.kind === SyntaxKind.NewExpression && (<CallExpression>node.parent).expression === node;
}
function getCallOrNewExpressionWorker(node: Node, kind: SyntaxKind): Node | undefined {
const target = climbPastPropertyAccess(node);
return target &&
target.parent &&
target.parent.kind === kind &&
(<CallExpression>target.parent).expression === target &&
target.parent;
}
function isNameOfModuleDeclaration(node: Node) {
@ -5068,14 +5087,25 @@ namespace ts {
};
}
function getSymbolInfo(typeChecker: TypeChecker, symbol: Symbol, node: Node) {
return {
symbolName: typeChecker.symbolToString(symbol), // Do not get scoped name, just the name of the symbol
symbolKind: getSymbolKind(symbol, node),
containerName: symbol.parent ? typeChecker.symbolToString(symbol.parent, node) : ""
};
}
function getDefinitionFromSignatureDeclaration(decl: SignatureDeclaration): DefinitionInfo {
const typeChecker = program.getTypeChecker();
const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, decl.symbol, decl);
return createDefinitionInfo(decl, symbolKind, symbolName, containerName);
}
function getDefinitionFromSymbol(symbol: Symbol, node: Node): DefinitionInfo[] {
const typeChecker = program.getTypeChecker();
const result: DefinitionInfo[] = [];
const declarations = symbol.getDeclarations();
const symbolName = typeChecker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol
const symbolKind = getSymbolKind(symbol, node);
const containerSymbol = symbol.parent;
const containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : "";
const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, symbol, node);
if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) &&
!tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) {
@ -5201,6 +5231,12 @@ namespace ts {
}
const typeChecker = program.getTypeChecker();
const calledDeclaration = tryGetCalledDeclaration(typeChecker, node);
if (calledDeclaration) {
return [getDefinitionFromSignatureDeclaration(calledDeclaration)];
}
let symbol = typeChecker.getSymbolAtLocation(node);
// Could not find a symbol e.g. node is string or number keyword,

View File

@ -11,11 +11,11 @@
goTo.marker('constructorOverloadReference1');
goTo.definition();
verify.caretAtMarker('constructorDefinition');
verify.caretAtMarker('constructorOverload1');
goTo.marker('constructorOverloadReference2');
goTo.definition();
verify.caretAtMarker('constructorDefinition');
verify.caretAtMarker('constructorOverload2');
goTo.marker('constructorOverload1');
goTo.definition();

View File

@ -9,11 +9,11 @@
goTo.marker('functionOverloadReference1');
goTo.definition();
verify.caretAtMarker('functionOverloadDefinition');
verify.caretAtMarker('functionOverload1');
goTo.marker('functionOverloadReference2');
goTo.definition();
verify.caretAtMarker('functionOverloadDefinition');
verify.caretAtMarker('functionOverload2');
goTo.marker('functionOverload');
goTo.definition();

View File

@ -1,11 +1,11 @@
/// <reference path='fourslash.ts' />
////class MethodOverload {
//// static me/*staticMethodOverload1*/thod();
//// static me/*staticMethodOverload2*/thod(foo: string);
/////*staticMethodDefinition*/static method(foo?: any) { }
//// public met/*instanceMethodOverload1*/hod(): any;
//// public met/*instanceMethodOverload2*/hod(foo: string);
//// /*staticMethodOverload1*/static /*staticMethodOverload1Name*/method();
//// /*staticMethodOverload2*/static method(foo: string);
//// /*staticMethodDefinition*/static method(foo?: any) { }
//// /*instanceMethodOverload1*/public /*instanceMethodOverload1Name*/method(): any;
//// /*instanceMethodOverload2*/public method(foo: string);
/////*instanceMethodDefinition*/public method(foo?: any) { return "foo" }
////}
@ -20,25 +20,25 @@
goTo.marker('staticMethodReference1');
goTo.definition();
verify.caretAtMarker('staticMethodDefinition');
verify.caretAtMarker('staticMethodOverload1');
goTo.marker('staticMethodReference2');
goTo.definition();
verify.caretAtMarker('staticMethodDefinition');
verify.caretAtMarker('staticMethodOverload2');
goTo.marker('instanceMethodReference1');
goTo.definition();
verify.caretAtMarker('instanceMethodDefinition');
verify.caretAtMarker('instanceMethodOverload1');
goTo.marker('instanceMethodReference2');
goTo.definition();
verify.caretAtMarker('instanceMethodDefinition');
verify.caretAtMarker('instanceMethodOverload2');
goTo.marker('staticMethodOverload1');
goTo.marker('staticMethodOverload1Name');
goTo.definition();
verify.caretAtMarker('staticMethodDefinition');
goTo.marker('instanceMethodOverload1');
goTo.marker('instanceMethodOverload1Name');
goTo.definition();
verify.caretAtMarker('instanceMethodDefinition');

View File

@ -0,0 +1,33 @@
///<reference path="fourslash.ts"/>
////class A {
//// /*ctr*/constructor() {}
//// x() {}
////}
/////*B*/class B extends A {}
////class C extends B {
//// constructor() {
//// /*super*/super();
//// }
//// method() {
//// /*superExpression*/super.x();
//// }
////}
////class D {
//// constructor() {
//// /*superBroken*/super();
//// }
////}
// Super in call position goes to constructor.
goTo.marker("super");
goTo.definition();
verify.caretAtMarker("ctr");
// Super in any other position goes to the superclass.
goTo.marker("superExpression");
goTo.definition();
verify.caretAtMarker("B");
goTo.marker("superBroken");
verify.definitionCountIs(0);