namespace ts . GoToDefinition { export function getDefinitionAtPosition ( program , sourceFile , position ) { const resolvedRef = getReferenceAtPosition ( sourceFile , position , program ) ; const fileReferenceDefinition = resolvedRef && [ getDefinitionInfoForFileReference ( resolvedRef . reference . fileName , resolvedRef . fileName , resolvedRef . unverified ) ] || emptyArray ; if ( resolvedRef ?. file ) { return fileReferenceDefinition ; } const node = getTouchingPropertyName ( sourceFile , position ) ; if ( node === sourceFile ) { return undefined ; } const { parent } = node ; const typeChecker = program . getTypeChecker ( ) ; if ( node . kind === SyntaxKind . OverrideKeyword || ( isJSDocOverrideTag ( node ) && rangeContainsPosition ( node . tagName , position ) ) ) { return getDefinitionFromOverriddenMember ( typeChecker , node ) || emptyArray ; } if ( isJumpStatementTarget ( node ) ) { const label = getTargetLabel ( node . parent , node . text ) ; return label ? [ createDefinitionInfoFromName ( typeChecker , label , ScriptElementKind . label , node . text , undefined ! ) ] : undefined ; } if ( isStaticModifier ( node ) && isClassStaticBlockDeclaration ( node . parent ) ) { const classDecl = node . parent . parent ; const symbol = getSymbol ( classDecl , typeChecker ) ; const staticBlocks = filter ( classDecl . members , isClassStaticBlockDeclaration ) ; const containerName = symbol ? typeChecker . symbolToString ( symbol , classDecl ) : "" ; const sourceFile = node . getSourceFile ( ) ; return map ( staticBlocks , staticBlock => { let { pos } = moveRangePastModifiers ( staticBlock ) ; pos = skipTrivia ( sourceFile . text , pos ) ; return createDefinitionInfoFromName ( typeChecker , staticBlock , ScriptElementKind . constructorImplementationElement , "static {}" , containerName , { start : pos , length : "static" . length } ) ; } ) ; } const symbol = getSymbol ( node , typeChecker ) ; if ( ! symbol ) { return concatenate ( fileReferenceDefinition , getDefinitionInfoForIndexSignatures ( node , typeChecker ) ) ; } const calledDeclaration = tryGetSignatureDeclaration ( typeChecker , node ) ; if ( calledDeclaration && ! ( isJsxOpeningLikeElement ( node . parent ) && isConstructorLike ( calledDeclaration ) ) ) { const sigInfo = createDefinitionFromSignatureDeclaration ( typeChecker , calledDeclaration ) ; if ( typeChecker . getRootSymbols ( symbol ) . some ( s => symbolMatchesSignature ( s , calledDeclaration ) ) ) { return [ sigInfo ] ; } else { const defs = getDefinitionFromSymbol ( typeChecker , symbol , node , calledDeclaration ) || emptyArray ; return node . kind === SyntaxKind . SuperKeyword ? [ sigInfo , ... defs ] : [ ... defs , sigInfo ] ; } } if ( node . parent . kind === SyntaxKind . ShorthandPropertyAssignment ) { const shorthandSymbol = typeChecker . getShorthandAssignmentValueSymbol ( symbol . valueDeclaration ) ; const definitions = shorthandSymbol ?. declarations ? shorthandSymbol . declarations . map ( decl => createDefinitionInfo ( decl , typeChecker , shorthandSymbol , node ) ) : emptyArray ; return concatenate ( definitions , getDefinitionFromObjectLiteralElement ( typeChecker , node ) || emptyArray ) ; } if ( isPropertyName ( node ) && isBindingElement ( parent ) && isObjectBindingPattern ( parent . parent ) && ( node === ( parent . propertyName || parent . name ) ) ) { const name = getNameFromPropertyName ( node ) ; const type = typeChecker . getTypeAtLocation ( parent . parent ) ; return name === undefined ? emptyArray : flatMap ( type . isUnion ( ) ? type . types : [ type ] , t => { const prop = t . getProperty ( name ) ; return prop && getDefinitionFromSymbol ( typeChecker , prop , node ) ; } ) ; } return concatenate ( fileReferenceDefinition , getDefinitionFromObjectLiteralElement ( typeChecker , node ) || getDefinitionFromSymbol ( typeChecker , symbol , node ) ) ; } /**
     * True if we should not add definitions for both the signature symbol and the definition symbol.
     * True for `const |f = |() => 0`, false for `function |f() {} const |g = f;`.
     * Also true for any assignment RHS.
     */ function symbolMatchesSignature ( s , calledDeclaration ) { return s === calledDeclaration . symbol || s === calledDeclaration . symbol . parent || isAssignmentExpression ( calledDeclaration . parent ) || ( ! isCallLikeExpression ( calledDeclaration . parent ) && s === calledDeclaration . parent . symbol ) ; } function getDefinitionFromObjectLiteralElement ( typeChecker , node ) { const element = getContainingObjectLiteralElement ( node ) ; if ( element ) { const contextualType = element && typeChecker . getContextualType ( element . parent ) ; if ( contextualType ) { return flatMap ( getPropertySymbolsFromContextualType ( element , typeChecker , contextualType , false ) , propertySymbol => getDefinitionFromSymbol ( typeChecker , propertySymbol , node ) ) ; } } } function getDefinitionFromOverriddenMember ( typeChecker , node ) { const classElement = findAncestor ( node , isClassElement ) ; if ( ! ( classElement && classElement . name ) ) return ; const baseDeclaration = findAncestor ( classElement , isClassLike ) ; if ( ! baseDeclaration ) return ; const baseTypeNode = getEffectiveBaseTypeNode ( baseDeclaration ) ; const baseType = baseTypeNode ? typeChecker . getTypeAtLocation ( baseTypeNode ) : undefined ; if ( ! baseType ) return ; const name = unescapeLeadingUnderscores ( getTextOfPropertyName ( classElement . name ) ) ; const symbol = hasStaticModifier ( classElement ) ? typeChecker . getPropertyOfType ( typeChecker . getTypeOfSymbolAtLocation ( baseType . symbol , baseDeclaration ) , name ) : typeChecker . getPropertyOfType ( baseType , name ) ; if ( ! symbol ) return ; return getDefinitionFromSymbol ( typeChecker , symbol , node ) ; } export function getReferenceAtPosition ( sourceFile , position , program ) { const referencePath = findReferenceInPosition ( sourceFile . referencedFiles , position ) ; if ( referencePath ) { const file = program . getSourceFileFromReference ( sourceFile , referencePath ) ; return file && { reference : referencePath , fileName : file . fileName , file , unverified : false } ; } const typeReferenceDirective = findReferenceInPosition ( sourceFile . typeReferenceDirectives , position ) ; if ( typeReferenceDirective ) { const reference = program . getResolvedTypeReferenceDirectives ( ) . get ( typeReferenceDirective . fileName ) ; const file = reference && program . getSourceFile ( reference . resolvedFileName ! ) ; return file && { reference : typeReferenceDirective , fileName : file . fileName , file , unverified : false } ; } const libReferenceDirective = findReferenceInPosition ( sourceFile . libReferenceDirectives , position ) ; if ( libReferenceDirective ) { const file = program . getLibFileFromReference ( libReferenceDirective ) ; return file && { reference : libReferenceDirective , fileName : file . fileName , file , unverified : false } ; } if ( sourceFile . resolvedModules ?. size ( ) ) { const node = getTokenAtPosition ( sourceFile , position ) ; if ( isModuleSpecifierLike ( node ) && isExternalModuleNameRelative ( node . text ) && sourceFile . resolvedModules . has ( node . text , getModeForUsageLocation ( sourceFile , node ) ) ) { const verifiedFileName = sourceFile . resolvedModules . get ( node . text , getModeForUsageLocation ( sourceFile , node ) ) ?. resolvedFileName ; const fileName = verifiedFileName || resolvePath ( getDirectoryPath ( sourceFile . fileName ) , node . text ) ; return { file : program . getSourceFile ( fileName ) , fileName , reference : { pos : node . getStart ( ) , end : node . getEnd ( ) , fileName : node . text } , unverified : ! verifiedFileName , } ; } } return undefined ; } export function getTypeDefinitionAtPosition ( typeChecker , sourceFile , position ) { const node = getTouchingPropertyName ( sourceFile , position ) ; if ( node === sourceFile ) { return undefined ; } const symbol = getSymbol ( node , typeChecker ) ; if ( ! symbol ) return undefined ; const typeAtLocation = typeChecker . getTypeOfSymbolAtLocation ( symbol , node ) ; const returnType = tryGetReturnTypeOfFunction ( symbol , typeAtLocation , typeChecker ) ; const fromReturnType = returnType && definitionFromType ( returnType , typeChecker , node ) ; const typeDefinitions = fromReturnType && fromReturnType . length !== 0 ? fromReturnType : definitionFromType ( typeAtLocation , typeChecker , node ) ; return typeDefinitions . length ? typeDefinitions : ! ( symbol . flags & SymbolFlags . Value ) && symbol . flags & SymbolFlags . Type ? getDefinitionFromSymbol ( typeChecker , skipAlias ( symbol , typeChecker ) , node ) : undefined ; } function definitionFromType ( type , checker , node ) { return flatMap ( type . isUnion ( ) && ! ( type . flags & TypeFlags . Enum ) ? type . types : [ type ] , t => t . symbol && getDefinitionFromSymbol ( checker , t . symbol , node ) ) ; } function tryGetReturnTypeOfFunction ( symbol , type , checker ) { if ( type . symbol === symbol || symbol . valueDeclaration && type . symbol && isVariableDeclaration ( symbol . valueDeclaration ) && symbol . valueDeclaration . initializer === type . symbol . valueDeclaration as Node ) { const sigs = type . getCallSignatures ( ) ; if ( sigs . length === 1 ) return checker . getReturnTypeOfSignature ( first ( sigs ) ) ; } return undefined ; } export function getDefinitionAndBoundSpan ( program , sourceFile , position ) { const definitions = getDefinitionAtPosition ( program , sourceFile , position ) ; if ( ! definitions || definitions . length === 0 ) { return undefined ; } const comment = findReferenceInPosition ( sourceFile . referencedFiles , position ) || findReferenceInPosition ( sourceFile . typeReferenceDirectives , position ) || findReferenceInPosition ( sourceFile . libReferenceDirectives , position ) ; if ( comment ) { return { definitions , textSpan : createTextSpanFromRange ( comment ) } ; } const node = getTouchingPropertyName ( sourceFile , position ) ; const textSpan = createTextSpan ( node . getStart ( ) , node . getWidth ( ) ) ; return { definitions , textSpan } ; } function getDefinitionInfoForIndexSignatures ( node , checker ) { return mapDefined ( checker . getIndexInfosAtLocation ( node ) , info => info . declaration && createDefinitionFromSignatureDeclaration ( checker , info . declaration ) ) ; } function getSymbol ( node , checker ) { const symbol = checker . getSymbolAtLocation ( node ) ; if ( symbol ?. declarations && symbol . flags & SymbolFlags . Alias && shouldSkipAlias ( node , symbol . declarations [ 0 ] ) ) { const aliased = checker . getAliasedSymbol ( symbol ) ; if ( aliased . declarations ) { return aliased ; } } return symbol ; } function shouldSkipAlias ( node , declaration ) { if ( node . kind !== SyntaxKind . Identifier ) { return false ; } if ( node . parent === declaration ) { return true ; } switch ( declaration . kind ) { case SyntaxKind . ImportClause : case SyntaxKind . ImportEqualsDeclaration : return true ; case SyntaxKind . ImportSpecifier : return declaration . parent . kind === SyntaxKind . NamedImports ; case SyntaxKind . BindingElement : case SyntaxKind . VariableDeclaration : return isInJSFile ( declaration ) && isRequireVariableDeclaration ( declaration ) ; default : return false ; } } function getDefinitionFromSymbol ( typeChecker , symbol , node , declarationNode ? ) { const filteredDeclarations = filter ( symbol . declarations , d => d !== declarationNode && ( ! isAssignmentDeclaration ( d ) || d === symbol . valueDeclaration ) ) || undefined ; return getConstructSignatureDefinition ( ) || getCallSignatureDefinition ( ) || map ( filteredDeclarations , declaration => createDefinitionInfo ( declaration , typeChecker , symbol , node ) ) ; function getConstructSignatureDefinition ( ) { if ( symbol . flags & SymbolFlags . Class && ! ( symbol . flags & ( SymbolFlags . Function | SymbolFlags . Variable ) ) && ( isNewExpressionTarget ( node ) || node . kind === SyntaxKind . ConstructorKeyword ) ) { const cls = find ( filteredDeclarations ! , isClassLike ) || Debug . fail ( "Expected declaration to have at least one class-like declaration" ) ; return getSignatureDefinition ( cls . members , true ) ; } } function getCallSignatureDefinition ( ) { return isCallOrNewExpressionTarget ( node ) || isNameOfFunctionDeclaration ( node ) ? getSignatureDefinition ( filteredDeclarations , false ) : undefined ; } function getSignatureDefinition ( signatureDeclarations , selectConstructors ) { if ( ! signatureDeclarations ) { return undefined ; } const declarations = signatureDeclarations . filter ( selectConstructors ? isConstructorDeclaration : isFunctionLike ) ; const declarationsWithBody = declarations . filter ( d => ! ! ( d as FunctionLikeDeclaration ) . body ) ; return declarations . length ? declarationsWithBody . length !== 0 ? declarationsWithBody . map ( x => createDefinitionInfo ( x , typeChecker , symbol , node ) ) : [ createDefinitionInfo ( last ( declarations ) , typeChecker , symbol , node ) ] : undefined ; } } /** Creates a DefinitionInfo from a Declaration, using the declaration's name if possible. */ function createDefinitionInfo ( declaration , checker , symbol , node ) { const symbolName = checker . symbolToString ( symbol ) ; const symbolKind = SymbolDisplay . getSymbolKind ( checker , symbol , node ) ; const containerName = symbol . parent ? checker . symbolToString ( symbol . parent , node ) : "" ; return createDefinitionInfoFromName ( checker , declaration , symbolKind , symbolName , containerName ) ; } /** Creates a DefinitionInfo directly from the name of a declaration. */ function createDefinitionInfoFromName ( checker , declaration , symbolKind , symbolName , containerName , textSpan ? ) { const sourceFile = declaration . getSourceFile ( ) ; if ( ! textSpan ) { const name = getNameOfDeclaration ( declaration ) || declaration ; textSpan = createTextSpanFromNode ( name , sourceFile ) ; } return { fileName : sourceFile . fileName , textSpan , kind : symbolKind , name : symbolName , containerKind : undefined ! , containerName , ... FindAllReferences . toContextSpan ( textSpan , sourceFile , FindAllReferences . getContextNode ( declaration ) ) , isLocal : ! isDefinitionVisible ( checker , declaration ) } ; } function isDefinitionVisible ( checker , declaration ) { if ( checker . isDeclarationVisible ( declaration ) ) return true ; if ( ! declaration . parent ) return false ; if ( hasInitializer ( declaration . parent ) && declaration . parent . initializer === declaration ) return isDefinitionVisible ( checker , declaration . parent as Declaration ) ; switch ( declaration . kind ) { case SyntaxKind . PropertyDeclaration : case SyntaxKind . GetAccessor : case SyntaxKind . SetAccessor : case SyntaxKind . MethodDeclaration : if ( hasEffectiveModifier ( declaration , ModifierFlags . Private ) ) return false ; case SyntaxKind . Constructor : case SyntaxKind . PropertyAssignment : case SyntaxKind . ShorthandPropertyAssignment : case SyntaxKind . ObjectLiteralExpression : case SyntaxKind . ClassExpression : case SyntaxKind . ArrowFunction : case SyntaxKind . FunctionExpression : return isDefinitionVisible ( checker , declaration . parent as Declaration ) ; default : return false ; } } function createDefinitionFromSignatureDeclaration ( typeChecker , decl ) { return createDefinitionInfo ( decl , typeChecker , decl . symbol , decl ) ; } export function findReferenceInPosition ( refs , pos ) { return find ( refs , ref => textRangeContainsPositionInclusive ( ref , pos ) ) ; } function getDefinitionInfoForFileReference ( name , targetFileName , unverified ) { return { fileName : targetFileName , textSpan : createTextSpanFromBounds ( 0 , 0 ) , kind : ScriptElementKind . scriptElement , name , containerName : undefined ! , containerKind : undefined ! , unverified , } ; } /** Returns a CallLikeExpression where `node` is the target being invoked. */ function getAncestorCallLikeExpression ( node ) { const target = findAncestor ( node , n => ! isRightSideOfPropertyAccess ( n ) ) ; const callLike = target ?. parent ; return callLike && isCallLikeExpression ( callLike ) && getInvokedExpression ( callLike ) === target ? callLike : undefined ; } function tryGetSignatureDeclaration ( typeChecker , node ) { const callLike = getAncestorCallLikeExpression ( node ) ; const signature = callLike && typeChecker . getResolvedSignature ( callLike ) ; return tryCast ( signature && signature . declaration , ( d ) : d is SignatureDeclaration => isFunctionLike ( d ) && ! isFunctionTypeNode ( d ) ) ; } function isConstructorLike ( node ) { switch ( node . kind ) { case SyntaxKind . Constructor : case SyntaxKind . ConstructorType : case SyntaxKind . ConstructSignature : return true ; default : return false ; } } }