diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 35dea56cf0b..870df4a54af 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1669,11 +1669,6 @@ namespace ts { return false; } - export function isFunctionDeclarationIdentifierName(node: Identifier): boolean { - return node.parent.kind === SyntaxKind.FunctionDeclaration && - (node.parent).name === node; - } - // An alias symbol is created by one of the following declarations: // import = ... // import from ... diff --git a/src/server/session.ts b/src/server/session.ts index 783451e02c6..2eb994519b1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -366,9 +366,9 @@ namespace ts.server { } const compilerService = project.compilerService; - const position = compilerService.host.lineOffsetToPosition(file, line, offset); + const implementations = compilerService.languageService.getImplementationAtPosition(file, + compilerService.host.lineOffsetToPosition(file, line, offset)); - const implementations = compilerService.languageService.getImplementationAtPosition(file, position); if (!implementations) { return undefined; } diff --git a/src/services/services.ts b/src/services/services.ts index bb63f30c603..6f682822bba 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -5302,6 +5302,8 @@ namespace ts { const node = getTouchingPropertyName(getValidSourceFile(fileName), position); const typeChecker = program.getTypeChecker(); + // If invoked directly on a shorthand property assignment, then return + // the declaration of the symbol being assigned (not the symbol being assigned to). if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { const entry = getReferenceEntryForShorthandPropertyAssignment(node, typeChecker); entries.push({ @@ -5309,6 +5311,10 @@ namespace ts { fileName: entry.fileName }); } + + // For most symbols, the definition is the same as the implementation so we can just + // call "Go to Definition". This case should handle anything that is not a type + // reference to or member of an interface, class, or union/intersection type. else if (definitionIsImplementation(node, typeChecker)) { const definitions = getDefinitionAtPosition(fileName, position); forEach(definitions, (definition: DefinitionInfo) => { @@ -5318,8 +5324,12 @@ namespace ts { }); }); } + + // Interfaces, classes, and unions/intersection types separate the implementation and + // definition so "Go to Definition" is not sufficient. This case handles invocations + // on type references and members of those types. else { - // Do a search for all references and filter them down to implementations only + // Perform "Find all References" and filter them down to implementations only const result = getReferencedSymbolsForNode(node, program.getSourceFiles(), /*findInStrings*/false, /*findInComments*/false, /*implementations*/true); forEach(result, referencedSymbol => { @@ -5362,7 +5372,8 @@ namespace ts { return false; } - // Also check the right hand side to see if this is a type being accessed on a namespace/module + // Also check the right hand side to see if this is a type being accessed on a namespace/module. + // For example, SomeModule.SomeType const rightHandType = typeChecker.getTypeAtLocation(node); return rightHandType && !(rightHandType.getFlags() & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.UnionOrIntersection)); } @@ -5424,6 +5435,11 @@ namespace ts { return isIdentifierOfClass(node) || isIdentifierOfEnumDeclaration(node); } + function isFunctionDeclarationIdentifierName(node: Identifier): boolean { + return node.parent.kind === SyntaxKind.FunctionDeclaration && + (node.parent).name === node; + } + function isIdentifierOfClass(node: Identifier) { return (node.parent.kind === SyntaxKind.ClassDeclaration || node.parent.kind === SyntaxKind.ClassExpression) && (node.parent).name === node;