Skip to content

LSP: Jump to the function name #792

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -2650,3 +2650,8 @@ func getPragmaArgument(pragma *Pragma, name string) string {
}
return ""
}

func IsRightSideOfPropertyAccess(node *Node) bool {
parent := node.Parent
return IsPropertyAccessExpression(parent) && parent.AsPropertyAccessExpression().Name() == node
}
16 changes: 8 additions & 8 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -5584,7 +5584,7 @@ func (c *Checker) checkDecorators(node *ast.Node) {

func (c *Checker) checkDecorator(node *ast.Node) {
c.checkGrammarDecorator(node.AsDecorator())
signature := c.getResolvedSignature(node, nil, CheckModeNormal)
signature := c.GetResolvedSignature(node, nil, CheckModeNormal)
c.checkDeprecatedSignature(signature, node)
returnType := c.getReturnTypeOfSignature(signature)
if returnType.flags&TypeFlagsAny != 0 {
Expand Down Expand Up @@ -7820,7 +7820,7 @@ func (c *Checker) checkImportCallExpression(node *ast.Node) *Type {
*/
func (c *Checker) checkCallExpression(node *ast.Node, checkMode CheckMode) *Type {
c.checkGrammarTypeArguments(node, node.TypeArgumentList())
signature := c.getResolvedSignature(node, nil /*candidatesOutArray*/, checkMode)
signature := c.GetResolvedSignature(node, nil /*candidatesOutArray*/, checkMode)
if signature == c.resolvingSignature {
// CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that
// returns a function type. We defer checking and return silentNeverType.
Expand Down Expand Up @@ -7864,7 +7864,7 @@ func (c *Checker) checkDeprecatedSignature(sig *Signature, node *ast.Node) {
}
if sig.declaration != nil && sig.declaration.Flags&ast.NodeFlagsDeprecated != 0 {
suggestionNode := c.getDeprecatedSuggestionNode(node)
name := tryGetPropertyAccessOrIdentifierToString(getInvokedExpression(node))
name := tryGetPropertyAccessOrIdentifierToString(GetInvokedExpression(node))
c.addDeprecatedSuggestionWithSignature(suggestionNode, sig.declaration, name, c.signatureToString(sig))
}
}
Expand Down Expand Up @@ -7901,7 +7901,7 @@ func (c *Checker) isSymbolOrSymbolForCall(node *ast.Node) bool {
* the function will fill it up with appropriate candidate signatures
* @return a signature of the call-like expression or undefined if one can't be found
*/
func (c *Checker) getResolvedSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
func (c *Checker) GetResolvedSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
links := c.signatureLinks.Get(node)
// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
// However, it is possible that either candidatesOutArray was not passed in the first time,
Expand Down Expand Up @@ -9521,7 +9521,7 @@ func (c *Checker) checkTaggedTemplateExpression(node *ast.Node) *Type {
if !c.checkGrammarTaggedTemplateChain(node.AsTaggedTemplateExpression()) {
c.checkGrammarTypeArguments(node, node.TypeArgumentList())
}
signature := c.getResolvedSignature(node, nil, CheckModeNormal)
signature := c.GetResolvedSignature(node, nil, CheckModeNormal)
c.checkDeprecatedSignature(signature, node)
return c.getReturnTypeOfSignature(signature)
}
Expand Down Expand Up @@ -10961,7 +10961,7 @@ func (c *Checker) isUncalledFunctionReference(node *ast.Node, symbol *ast.Symbol
if parent == nil {
parent = node.Parent
}
if isCallLikeExpression(parent) {
if IsCallLikeExpression(parent) {
return isCallOrNewExpression(parent) && ast.IsIdentifier(node) && c.hasMatchingArgument(parent, node)
}
return core.Every(symbol.Declarations, func(d *ast.Node) bool {
Expand Down Expand Up @@ -12261,7 +12261,7 @@ func (c *Checker) checkInstanceOfExpression(left *ast.Expression, right *ast.Exp
if !IsTypeAny(leftType) && c.allTypesAssignableToKind(leftType, TypeFlagsPrimitive) {
c.error(left, diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter)
}
signature := c.getResolvedSignature(left.Parent, nil /*candidatesOutArray*/, checkMode)
signature := c.GetResolvedSignature(left.Parent, nil /*candidatesOutArray*/, checkMode)
if signature == c.resolvingSignature {
// CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that
// returns a function type. We defer checking and return silentNeverType.
Expand Down Expand Up @@ -27529,7 +27529,7 @@ func (c *Checker) getContextualTypeForArgumentAtIndex(callTarget *ast.Node, argI
if c.signatureLinks.Get(callTarget).resolvedSignature == c.resolvingSignature {
signature = c.resolvingSignature
} else {
signature = c.getResolvedSignature(callTarget, nil, CheckModeNormal)
signature = c.GetResolvedSignature(callTarget, nil, CheckModeNormal)
}
if isJsxOpeningLikeElement(callTarget) && argIndex == 0 {
return c.getEffectiveFirstArgumentForJsxSignature(signature, callTarget)
Expand Down
2 changes: 1 addition & 1 deletion internal/checker/flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -2040,7 +2040,7 @@ func (c *Checker) getEffectsSignature(node *ast.Node) *Signature {
case len(signatures) == 1 && signatures[0].typeParameters == nil:
signature = signatures[0]
case core.Some(signatures, c.hasTypePredicateOrNeverReturnType):
signature = c.getResolvedSignature(node, nil, CheckModeNormal)
signature = c.GetResolvedSignature(node, nil, CheckModeNormal)
}
if !(signature != nil && c.hasTypePredicateOrNeverReturnType(signature)) {
signature = c.unknownSignature
Expand Down
2 changes: 1 addition & 1 deletion internal/checker/jsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (c *Checker) checkJsxOpeningLikeElementOrOpeningFragment(node *ast.Node) {
c.checkJsxPreconditions(node)
c.markJsxAliasReferenced(node)
if isNodeOpeningLikeElement {
sig := c.getResolvedSignature(node, nil, CheckModeNormal)
sig := c.GetResolvedSignature(node, nil, CheckModeNormal)
c.checkDeprecatedSignature(sig, node)
elementTypeConstraint := c.getJsxElementTypeTypeAt(node)
if elementTypeConstraint != nil {
Expand Down
8 changes: 6 additions & 2 deletions internal/checker/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,7 @@ func isThisTypeParameter(t *Type) bool {
return t.flags&TypeFlagsTypeParameter != 0 && t.AsTypeParameter().isThisType
}

func isCallLikeExpression(node *ast.Node) bool {
func IsCallLikeExpression(node *ast.Node) bool {
switch node.Kind {
case ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement, ast.KindCallExpression, ast.KindNewExpression,
ast.KindTaggedTemplateExpression, ast.KindDecorator:
Expand Down Expand Up @@ -1829,6 +1829,10 @@ func getNonRestParameterCount(sig *Signature) int {
return len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0)
}

func GetDeclaration(sig *Signature) *ast.Node {
return sig.declaration
}

func minAndMax[T any](slice []T, getValue func(value T) int) (int, int) {
var minValue, maxValue int
for i, element := range slice {
Expand Down Expand Up @@ -2113,7 +2117,7 @@ func tryGetPropertyAccessOrIdentifierToString(expr *ast.Node) string {
return ""
}

func getInvokedExpression(node *ast.Node) *ast.Node {
func GetInvokedExpression(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindTaggedTemplateExpression:
return node.AsTaggedTemplateExpression().Tag
Expand Down
66 changes: 56 additions & 10 deletions internal/ls/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ls
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/astnav"
"github.com/microsoft/typescript-go/internal/checker"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/scanner"
)
Expand All @@ -15,25 +16,70 @@ func (l *LanguageService) ProvideDefinitions(fileName string, position int) []Lo
}

checker := program.GetTypeChecker()
calledDeclaration := tryGetSignatureDeclaration(checker, node)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to port the logic from src/services/goToDefinition.ts in the TypeScript repository.

if calledDeclaration != nil {
name := ast.GetNameOfDeclaration(calledDeclaration)
if name != nil {
return createLocationsFromDeclarations([]*ast.Node{name})
}
}

if symbol := checker.GetSymbolAtLocation(node); symbol != nil {
if symbol.Flags&ast.SymbolFlagsAlias != 0 {
if resolved, ok := checker.ResolveAlias(symbol); ok {
symbol = resolved
}
}

locations := make([]Location, 0, len(symbol.Declarations))
for _, decl := range symbol.Declarations {
file := ast.GetSourceFileOfNode(decl)
loc := decl.Loc
pos := scanner.GetTokenPosOfNode(decl, file, false /*includeJSDoc*/)
return createLocationsFromDeclarations(symbol.Declarations)
}
return nil
}

func createLocationsFromDeclarations(declarations []*ast.Node) []Location {
locations := make([]Location, 0, len(declarations))
for _, decl := range declarations {
file := ast.GetSourceFileOfNode(decl)
loc := decl.Loc
pos := scanner.GetTokenPosOfNode(decl, file, false /*includeJSDoc*/)

locations = append(locations, Location{
FileName: file.FileName(),
Range: core.NewTextRange(pos, loc.End()),
})
}
return locations
}

locations = append(locations, Location{
FileName: file.FileName(),
Range: core.NewTextRange(pos, loc.End()),
})
/** Returns a CallLikeExpression where `node` is the target being invoked. */
func getAncestorCallLikeExpression(node *ast.Node) *ast.Node {
target := ast.FindAncestor(node, func(n *ast.Node) bool {
return !ast.IsRightSideOfPropertyAccess(n)
})

callLike := target.Parent
if callLike != nil && checker.IsCallLikeExpression(callLike) && checker.GetInvokedExpression(callLike) == target {
return callLike
}

return nil
}

func tryGetSignatureDeclaration(typeChecker *checker.Checker, node *ast.Node) *ast.Node {
var signature *checker.Signature
callLike := getAncestorCallLikeExpression(node)
if callLike != nil {
signature = typeChecker.GetResolvedSignature(callLike, nil, checker.CheckModeNormal)
}

// Don't go to a function type, go to the value having that type.
var declaration *ast.Node
if signature != nil && checker.GetDeclaration(signature) != nil {
declaration = checker.GetDeclaration(signature)
if ast.IsFunctionLike(declaration) && !ast.IsFunctionTypeNode(declaration) {
return declaration
}
return locations
}

return nil
}