namespace ts . SignatureHelp { const enum InvocationKind { Call , TypeArgs , Contextual } interface CallInvocation { readonly kind : InvocationKind . Call ; readonly node : CallLikeExpression ; } interface TypeArgsInvocation { readonly kind : InvocationKind . TypeArgs ; readonly called : Identifier ; } interface ContextualInvocation { readonly kind : InvocationKind . Contextual ; readonly signature : Signature ; readonly node : Node ; readonly symbol : Symbol ; } type Invocation = CallInvocation | TypeArgsInvocation | ContextualInvocation ; interface ArgumentListInfo { readonly isTypeParameterList : boolean ; readonly invocation : Invocation ; readonly argumentsSpan : TextSpan ; readonly argumentIndex : number ; /** argumentCount is the *apparent* number of arguments. */ readonly argumentCount : number ; } export function getSignatureHelpItems ( program , sourceFile , position , triggerReason , cancellationToken ) { const typeChecker = program . getTypeChecker ( ) ; const startingToken = findTokenOnLeftOfPosition ( sourceFile , position ) ; if ( ! startingToken ) { return undefined ; } const onlyUseSyntacticOwners = ! ! triggerReason && triggerReason . kind === "characterTyped" ; if ( onlyUseSyntacticOwners && ( isInString ( sourceFile , position , startingToken ) || isInComment ( sourceFile , position ) ) ) { return undefined ; } const isManuallyInvoked = ! ! triggerReason && triggerReason . kind === "invoked" ; const argumentInfo = getContainingArgumentInfo ( startingToken , position , sourceFile , typeChecker , isManuallyInvoked ) ; if ( ! argumentInfo ) return undefined ; cancellationToken . throwIfCancellationRequested ( ) ; const candidateInfo = getCandidateOrTypeInfo ( argumentInfo , typeChecker , sourceFile , startingToken , onlyUseSyntacticOwners ) ; cancellationToken . throwIfCancellationRequested ( ) ; if ( ! candidateInfo ) { return isSourceFileJS ( sourceFile ) ? createJSSignatureHelpItems ( argumentInfo , program , cancellationToken ) : undefined ; } return typeChecker . runWithCancellationToken ( cancellationToken , typeChecker => candidateInfo . kind === CandidateOrTypeKind . Candidate ? createSignatureHelpItems ( candidateInfo . candidates , candidateInfo . resolvedSignature , argumentInfo , sourceFile , typeChecker ) : createTypeHelpItems ( candidateInfo . symbol , argumentInfo , sourceFile , typeChecker ) ) ; } const enum CandidateOrTypeKind { Candidate , Type } interface CandidateInfo { readonly kind : CandidateOrTypeKind . Candidate ; readonly candidates : readonly Signature [ ] ; readonly resolvedSignature : Signature ; } interface TypeInfo { readonly kind : CandidateOrTypeKind . Type ; readonly symbol : Symbol ; } function getCandidateOrTypeInfo ( { invocation , argumentCount } , checker , sourceFile , startingToken , onlyUseSyntacticOwners ) { switch ( invocation . kind ) { case InvocationKind . Call : { if ( onlyUseSyntacticOwners && ! isSyntacticOwner ( startingToken , invocation . node , sourceFile ) ) { return undefined ; } const candidates = [ ] ; const resolvedSignature = checker . getResolvedSignatureForSignatureHelp ( invocation . node , candidates , argumentCount ) ! ; return candidates . length === 0 ? undefined : { kind : CandidateOrTypeKind . Candidate , candidates , resolvedSignature } ; } case InvocationKind . TypeArgs : { const { called } = invocation ; if ( onlyUseSyntacticOwners && ! containsPrecedingToken ( startingToken , sourceFile , isIdentifier ( called ) ? called . parent : called ) ) { return undefined ; } const candidates = getPossibleGenericSignatures ( called , argumentCount , checker ) ; if ( candidates . length !== 0 ) return { kind : CandidateOrTypeKind . Candidate , candidates , resolvedSignature : first ( candidates ) } ; const symbol = checker . getSymbolAtLocation ( called ) ; return symbol && { kind : CandidateOrTypeKind . Type , symbol } ; } case InvocationKind . Contextual : return { kind : CandidateOrTypeKind . Candidate , candidates : [ invocation . signature ] , resolvedSignature : invocation . signature } ; default : return Debug . assertNever ( invocation ) ; } } function isSyntacticOwner ( startingToken , node , sourceFile ) { if ( ! isCallOrNewExpression ( node ) ) return false ; const invocationChildren = node . getChildren ( sourceFile ) ; switch ( startingToken . kind ) { case SyntaxKind . OpenParenToken : return contains ( invocationChildren , startingToken ) ; case SyntaxKind . CommaToken : { const containingList = findContainingList ( startingToken ) ; return ! ! containingList && contains ( invocationChildren , containingList ) ; } case SyntaxKind . LessThanToken : return containsPrecedingToken ( startingToken , sourceFile , node . expression ) ; default : return false ; } } function createJSSignatureHelpItems ( argumentInfo , program , cancellationToken ) { if ( argumentInfo . invocation . kind === InvocationKind . Contextual ) return undefined ; const expression = getExpressionFromInvocation ( argumentInfo . invocation ) ; const name = isPropertyAccessExpression ( expression ) ? expression . name . text : undefined ; const typeChecker = program . getTypeChecker ( ) ; return name === undefined ? undefined : firstDefined ( program . getSourceFiles ( ) , sourceFile => firstDefined ( sourceFile . getNamedDeclarations ( ) . get ( name ) , declaration => { const type = declaration . symbol && typeChecker . getTypeOfSymbolAtLocation ( declaration . symbol , declaration ) ; const callSignatures = type && type . getCallSignatures ( ) ; if ( callSignatures && callSignatures . length ) { return typeChecker . runWithCancellationToken ( cancellationToken , typeChecker => createSignatureHelpItems ( callSignatures , callSignatures [ 0 ] , argumentInfo , sourceFile , typeChecker , true ) ) ; } } ) ) ; } function containsPrecedingToken ( startingToken , sourceFile , container ) { const pos = startingToken . getFullStart ( ) ; let currentParent = startingToken . parent ; while ( currentParent ) { const precedingToken = findPrecedingToken ( pos , sourceFile , currentParent , true ) ; if ( precedingToken ) { return rangeContainsRange ( container , precedingToken ) ; } currentParent = currentParent . parent ; } return Debug . fail ( "Could not find preceding token" ) ; } export interface ArgumentInfoForCompletions { readonly invocation : CallLikeExpression ; readonly argumentIndex : number ; readonly argumentCount : number ; } export function getArgumentInfoForCompletions ( node , position , sourceFile ) { const info = getImmediatelyContainingArgumentInfo ( node , position , sourceFile ) ; return ! info || info . isTypeParameterList || info . invocation . kind !== InvocationKind . Call ? undefined : { invocation : info . invocation . node , argumentCount : info . argumentCount , argumentIndex : info . argumentIndex } ; } function getArgumentOrParameterListInfo ( node , sourceFile ) { const info = getArgumentOrParameterListAndIndex ( node , sourceFile ) ; if ( ! info ) return undefined ; const { list , argumentIndex } = info ; const argumentCount = getArgumentCount ( list ) ; if ( argumentIndex !== 0 ) { Debug . assertLessThan ( argumentIndex , argumentCount ) ; } const argumentsSpan = getApplicableSpanForArguments ( list , sourceFile ) ; return { list , argumentIndex , argumentCount , argumentsSpan } ; } function getArgumentOrParameterListAndIndex ( node , sourceFile ) { if ( node . kind === SyntaxKind . LessThanToken || node . kind === SyntaxKind . OpenParenToken ) { return { list : getChildListThatStartsWithOpenerToken ( node . parent , node , sourceFile ) , argumentIndex : 0 } ; } else { const list = findContainingList ( node ) ; return list && { list , argumentIndex : getArgumentIndex ( list , node ) } ; } } /**
     * Returns relevant information for the argument list and the current argument if we are
     * in the argument of an invocation; returns undefined otherwise.
     */ function getImmediatelyContainingArgumentInfo ( node , position , sourceFile ) { const { parent } = node ; if ( isCallOrNewExpression ( parent ) ) { const invocation = parent ; const info = getArgumentOrParameterListInfo ( node , sourceFile ) ; if ( ! info ) return undefined ; const { list , argumentIndex , argumentCount , argumentsSpan } = info ; const isTypeParameterList = ! ! parent . typeArguments && parent . typeArguments . pos === list . pos ; return { isTypeParameterList , invocation : { kind : InvocationKind . Call , node : invocation } , argumentsSpan , argumentIndex , argumentCount } ; } else if ( isNoSubstitutionTemplateLiteral ( node ) && isTaggedTemplateExpression ( parent ) ) { if ( isInsideTemplateLiteral ( node , position , sourceFile ) ) { return getArgumentListInfoForTemplate ( parent , 0 , sourceFile ) ; } return undefined ; } else if ( isTemplateHead ( node ) && parent . parent . kind === SyntaxKind . TaggedTemplateExpression ) { const templateExpression = parent as TemplateExpression ; const tagExpression = templateExpression . parent as TaggedTemplateExpression ; Debug . assert ( templateExpression . kind === SyntaxKind . TemplateExpression ) ; const argumentIndex = isInsideTemplateLiteral ( node , position , sourceFile ) ? 0 : 1 ; return getArgumentListInfoForTemplate ( tagExpression , argumentIndex , sourceFile ) ; } else if ( isTemplateSpan ( parent ) && isTaggedTemplateExpression ( parent . parent . parent ) ) { const templateSpan = parent ; const tagExpression = parent . parent . parent ; if ( isTemplateTail ( node ) && ! isInsideTemplateLiteral ( node , position , sourceFile ) ) { return undefined ; } const spanIndex = templateSpan . parent . templateSpans . indexOf ( templateSpan ) ; const argumentIndex = getArgumentIndexForTemplatePiece ( spanIndex , node , position , sourceFile ) ; return getArgumentListInfoForTemplate ( tagExpression , argumentIndex , sourceFile ) ; } else if ( isJsxOpeningLikeElement ( parent ) ) { const attributeSpanStart = parent . attributes . pos ; const attributeSpanEnd = skipTrivia ( sourceFile . text , parent . attributes . end , false ) ; return { isTypeParameterList : false , invocation : { kind : InvocationKind . Call , node : parent } , argumentsSpan : createTextSpan ( attributeSpanStart , attributeSpanEnd - attributeSpanStart ) , argumentIndex : 0 , argumentCount : 1 } ; } else { const typeArgInfo = getPossibleTypeArgumentsInfo ( node , sourceFile ) ; if ( typeArgInfo ) { const { called , nTypeArguments } = typeArgInfo ; const invocation = { kind : InvocationKind . TypeArgs , called } ; const argumentsSpan = createTextSpanFromBounds ( called . getStart ( sourceFile ) , node . end ) ; return { isTypeParameterList : true , invocation , argumentsSpan , argumentIndex : nTypeArguments , argumentCount : nTypeArguments + 1 } ; } return undefined ; } } function getImmediatelyContainingArgumentOrContextualParameterInfo ( node , position , sourceFile , checker ) { return tryGetParameterInfo ( node , position , sourceFile , checker ) || getImmediatelyContainingArgumentInfo ( node , position , sourceFile ) ; } function getHighestBinary ( b ) { return isBinaryExpression ( b . parent ) ? getHighestBinary ( b . parent ) : b ; } function countBinaryExpressionParameters ( b ) { return isBinaryExpression ( b . left ) ? countBinaryExpressionParameters ( b . left ) + 1 : 2 ; } function tryGetParameterInfo ( startingToken , _position , sourceFile , checker ) { const info = getContextualSignatureLocationInfo ( startingToken , sourceFile , checker ) ; if ( ! info ) return undefined ; const { contextualType , argumentIndex , argumentCount , argumentsSpan } = info ; const nonNullableContextualType = contextualType . getNonNullableType ( ) ; const signatures = nonNullableContextualType . getCallSignatures ( ) ; if ( signatures . length !== 1 ) return undefined ; const invocation = { kind : InvocationKind . Contextual , signature : first ( signatures ) , node : startingToken , symbol : chooseBetterSymbol ( nonNullableContextualType . symbol ) } ; return { isTypeParameterList : false , invocation , argumentsSpan , argumentIndex , argumentCount } ; } interface ContextualSignatureLocationInfo { readonly contextualType : Type ; readonly argumentIndex : number ; readonly argumentCount : number ; readonly argumentsSpan : TextSpan ; } function getContextualSignatureLocationInfo ( startingToken , sourceFile , checker ) { if ( startingToken . kind !== SyntaxKind . OpenParenToken && startingToken . kind !== SyntaxKind . CommaToken ) return undefined ; const { parent } = startingToken ; switch ( parent . kind ) { case SyntaxKind . ParenthesizedExpression : case SyntaxKind . MethodDeclaration : case SyntaxKind . FunctionExpression : case SyntaxKind . ArrowFunction : const info = getArgumentOrParameterListInfo ( startingToken , sourceFile ) ; if ( ! info ) return undefined ; const { argumentIndex , argumentCount , argumentsSpan } = info ; const contextualType = isMethodDeclaration ( parent ) ? checker . getContextualTypeForObjectLiteralElement ( parent ) : checker . getContextualType ( parent as ParenthesizedExpression | FunctionExpression | ArrowFunction ) ; return contextualType && { contextualType , argumentIndex , argumentCount , argumentsSpan } ; case SyntaxKind . BinaryExpression : { const highestBinary = getHighestBinary ( parent as BinaryExpression ) ; const contextualType = checker . getContextualType ( highestBinary ) ; const argumentIndex = startingToken . kind === SyntaxKind . OpenParenToken ? 0 : countBinaryExpressionParameters ( parent as BinaryExpression ) - 1 ; const argumentCount = countBinaryExpressionParameters ( highestBinary ) ; return contextualType && { contextualType , argumentIndex , argumentCount , argumentsSpan : createTextSpanFromNode ( parent ) } ; } default : return undefined ; } } function chooseBetterSymbol ( s ) { return s . name === InternalSymbolName . Type ? firstDefined ( s . declarations , d => isFunctionTypeNode ( d ) ? d . parent . symbol : undefined ) || s : s ; } function getArgumentIndex ( argumentsList , node ) { let argumentIndex = 0 ; for ( const child of argumentsList . getChildren ( ) ) { if ( child === node ) { break ; } if ( child . kind !== SyntaxKind . CommaToken ) { argumentIndex ++ ; } } return argumentIndex ; } function getArgumentCount ( argumentsList ) { const listChildren = argumentsList . getChildren ( ) ; let argumentCount = countWhere ( listChildren , arg => arg . kind !== SyntaxKind . CommaToken ) ; if ( listChildren . length > 0 && last ( listChildren ) . kind === SyntaxKind . CommaToken ) { argumentCount ++ ; } return argumentCount ; } function getArgumentIndexForTemplatePiece ( spanIndex , node , position , sourceFile ) { Debug . assert ( position >= node . getStart ( ) , "Assumed 'position' could not occur before node." ) ; if ( isTemplateLiteralToken ( node ) ) { if ( isInsideTemplateLiteral ( node , position , sourceFile ) ) { return 0 ; } return spanIndex + 2 ; } return spanIndex + 1 ; } function getArgumentListInfoForTemplate ( tagExpression , argumentIndex , sourceFile ) { const argumentCount = isNoSubstitutionTemplateLiteral ( tagExpression . template ) ? 1 : tagExpression . template . templateSpans . length + 1 ; if ( argumentIndex !== 0 ) { Debug . assertLessThan ( argumentIndex , argumentCount ) ; } return { isTypeParameterList : false , invocation : { kind : InvocationKind . Call , node : tagExpression } , argumentsSpan : getApplicableSpanForTaggedTemplate ( tagExpression , sourceFile ) , argumentIndex , argumentCount } ; } function getApplicableSpanForArguments ( argumentsList , sourceFile ) { const applicableSpanStart = argumentsList . getFullStart ( ) ; const applicableSpanEnd = skipTrivia ( sourceFile . text , argumentsList . getEnd ( ) , false ) ; return createTextSpan ( applicableSpanStart , applicableSpanEnd - applicableSpanStart ) ; } function getApplicableSpanForTaggedTemplate ( taggedTemplate , sourceFile ) { const template = taggedTemplate . template ; const applicableSpanStart = template . getStart ( ) ; let applicableSpanEnd = template . getEnd ( ) ; if ( template . kind === SyntaxKind . TemplateExpression ) { const lastSpan = last ( template . templateSpans ) ; if ( lastSpan . literal . getFullWidth ( ) === 0 ) { applicableSpanEnd = skipTrivia ( sourceFile . text , applicableSpanEnd , false ) ; } } return createTextSpan ( applicableSpanStart , applicableSpanEnd - applicableSpanStart ) ; } function getContainingArgumentInfo ( node , position , sourceFile , checker , isManuallyInvoked ) { for ( let n = node ; ! isSourceFile ( n ) && ( isManuallyInvoked || ! isBlock ( n ) ) ; n = n . parent ) { Debug . assert ( rangeContainsRange ( n . parent , n ) , "Not a subspan" , ( ) => `Child: ${ Debug . formatSyntaxKind ( n . kind ) }, parent: ${ Debug . formatSyntaxKind ( n . parent . kind ) }` ) ; const argumentInfo = getImmediatelyContainingArgumentOrContextualParameterInfo ( n , position , sourceFile , checker ) ; if ( argumentInfo ) { return argumentInfo ; } } return undefined ; } function getChildListThatStartsWithOpenerToken ( parent , openerToken , sourceFile ) { const children = parent . getChildren ( sourceFile ) ; const indexOfOpenerToken = children . indexOf ( openerToken ) ; Debug . assert ( indexOfOpenerToken >= 0 && children . length > indexOfOpenerToken + 1 ) ; return children [ indexOfOpenerToken + 1 ] ; } function getExpressionFromInvocation ( invocation ) { return invocation . kind === InvocationKind . Call ? getInvokedExpression ( invocation . node ) : invocation . called ; } function getEnclosingDeclarationFromInvocation ( invocation ) { return invocation . kind === InvocationKind . Call ? invocation . node : invocation . kind === InvocationKind . TypeArgs ? invocation . called : invocation . node ; } const signatureHelpNodeBuilderFlags = NodeBuilderFlags . OmitParameterModifiers | NodeBuilderFlags . IgnoreErrors | NodeBuilderFlags . UseAliasDefinedOutsideCurrentScope ; function createSignatureHelpItems ( candidates , resolvedSignature , { isTypeParameterList , argumentCount , argumentsSpan : applicableSpan , invocation , argumentIndex } , sourceFile , typeChecker , useFullPrefix ? ) { const enclosingDeclaration = getEnclosingDeclarationFromInvocation ( invocation ) ; const callTargetSymbol = invocation . kind === InvocationKind . Contextual ? invocation . symbol : ( typeChecker . getSymbolAtLocation ( getExpressionFromInvocation ( invocation ) ) || useFullPrefix && resolvedSignature . declaration ?. symbol ) ; const callTargetDisplayParts = callTargetSymbol ? symbolToDisplayParts ( typeChecker , callTargetSymbol , useFullPrefix ? sourceFile : undefined , undefined ) : emptyArray ; const items = map ( candidates , candidateSignature => getSignatureHelpItem ( candidateSignature , callTargetDisplayParts , isTypeParameterList , typeChecker , enclosingDeclaration , sourceFile ) ) ; if ( argumentIndex !== 0 ) { Debug . assertLessThan ( argumentIndex , argumentCount ) ; } let selectedItemIndex = 0 ; let itemsSeen = 0 ; for ( let i = 0 ; i < items . length ; i ++ ) { const item = items [ i ] ; if ( candidates [ i ] === resolvedSignature ) { selectedItemIndex = itemsSeen ; if ( item . length > 1 ) { let count = 0 ; for ( const i of item ) { if ( i . isVariadic || i . parameters . length >= argumentCount ) { selectedItemIndex = itemsSeen + count ; break ; } count ++ ; } } } itemsSeen += item . length ; } Debug . assert ( selectedItemIndex !== - 1 ) ; const help = { items : flatMapToMutable ( items , identity ) , applicableSpan , selectedItemIndex , argumentIndex , argumentCount } ; const selected = help . items [ selectedItemIndex ] ; if ( selected . isVariadic ) { const firstRest = findIndex ( selected . parameters , p => ! ! p . isRest ) ; if ( - 1 < firstRest && firstRest < selected . parameters . length - 1 ) { help . argumentIndex = selected . parameters . length ; } else { help . argumentIndex = Math . min ( help . argumentIndex , selected . parameters . length - 1 ) ; } } return help ; } function createTypeHelpItems ( symbol , { argumentCount , argumentsSpan : applicableSpan , invocation , argumentIndex } , sourceFile , checker ) { const typeParameters = checker . getLocalTypeParametersOfClassOrInterfaceOrTypeAlias ( symbol ) ; if ( ! typeParameters ) return undefined ; const items = [ getTypeHelpItem ( symbol , typeParameters , checker , getEnclosingDeclarationFromInvocation ( invocation ) , sourceFile ) ] ; return { items , applicableSpan , selectedItemIndex : 0 , argumentIndex , argumentCount } ; } function getTypeHelpItem ( symbol , typeParameters , checker , enclosingDeclaration , sourceFile ) { const typeSymbolDisplay = symbolToDisplayParts ( checker , symbol ) ; const printer = createPrinter ( { removeComments : true } ) ; const parameters = typeParameters . map ( t => createSignatureHelpParameterForTypeParameter ( t , checker , enclosingDeclaration , sourceFile , printer ) ) ; const documentation = symbol . getDocumentationComment ( checker ) ; const tags = symbol . getJsDocTags ( checker ) ; const prefixDisplayParts = [ ... typeSymbolDisplay , punctuationPart ( SyntaxKind . LessThanToken ) ] ; return { isVariadic : false , prefixDisplayParts , suffixDisplayParts : [ punctuationPart ( SyntaxKind . GreaterThanToken ) ] , separatorDisplayParts , parameters , documentation , tags } ; } const separatorDisplayParts = [ punctuationPart ( SyntaxKind . CommaToken ) , spacePart ( ) ] ; function getSignatureHelpItem ( candidateSignature , callTargetDisplayParts , isTypeParameterList , checker , enclosingDeclaration , sourceFile ) { const infos = ( isTypeParameterList ? itemInfoForTypeParameters : itemInfoForParameters ) ( candidateSignature , checker , enclosingDeclaration , sourceFile ) ; return map ( infos , ( { isVariadic , parameters , prefix , suffix } ) => { const prefixDisplayParts = [ ... callTargetDisplayParts , ... prefix ] ; const suffixDisplayParts = [ ... suffix , ... returnTypeToDisplayParts ( candidateSignature , enclosingDeclaration , checker ) ] ; const documentation = candidateSignature . getDocumentationComment ( checker ) ; const tags = candidateSignature . getJsDocTags ( ) ; return { isVariadic , prefixDisplayParts , suffixDisplayParts , separatorDisplayParts , parameters , documentation , tags } ; } ) ; } function returnTypeToDisplayParts ( candidateSignature , enclosingDeclaration , checker ) { return mapToDisplayParts ( writer => { writer . writePunctuation ( ":" ) ; writer . writeSpace ( " " ) ; const predicate = checker . getTypePredicateOfSignature ( candidateSignature ) ; if ( predicate ) { checker . writeTypePredicate ( predicate , enclosingDeclaration , undefined , writer ) ; } else { checker . writeType ( checker . getReturnTypeOfSignature ( candidateSignature ) , enclosingDeclaration , undefined , writer ) ; } } ) ; } interface SignatureHelpItemInfo { readonly isVariadic : boolean ; readonly parameters : SignatureHelpParameter [ ] ; readonly prefix : readonly SymbolDisplayPart [ ] ; readonly suffix : readonly SymbolDisplayPart [ ] ; } function itemInfoForTypeParameters ( candidateSignature , checker , enclosingDeclaration , sourceFile ) { const typeParameters = ( candidateSignature . target || candidateSignature ) . typeParameters ; const printer = createPrinter ( { removeComments : true } ) ; const parameters = ( typeParameters || emptyArray ) . map ( t => createSignatureHelpParameterForTypeParameter ( t , checker , enclosingDeclaration , sourceFile , printer ) ) ; const thisParameter = candidateSignature . thisParameter ? [ checker . symbolToParameterDeclaration ( candidateSignature . thisParameter , enclosingDeclaration , signatureHelpNodeBuilderFlags ) ! ] : [ ] ; return checker . getExpandedParameters ( candidateSignature ) . map ( paramList => { const params = factory . createNodeArray ( [ ... thisParameter , ... map ( paramList , param => checker . symbolToParameterDeclaration ( param , enclosingDeclaration , signatureHelpNodeBuilderFlags ) ! ) ] ) ; const parameterParts = mapToDisplayParts ( writer => { printer . writeList ( ListFormat . CallExpressionArguments , params , sourceFile , writer ) ; } ) ; return { isVariadic : false , parameters , prefix : [ punctuationPart ( SyntaxKind . LessThanToken ) ] , suffix : [ punctuationPart ( SyntaxKind . GreaterThanToken ) , ... parameterParts ] } ; } ) ; } function itemInfoForParameters ( candidateSignature , checker , enclosingDeclaration , sourceFile ) { const printer = createPrinter ( { removeComments : true } ) ; const typeParameterParts = mapToDisplayParts ( writer => { if ( candidateSignature . typeParameters && candidateSignature . typeParameters . length ) { const args = factory . createNodeArray ( candidateSignature . typeParameters . map ( p => checker . typeParameterToDeclaration ( p , enclosingDeclaration , signatureHelpNodeBuilderFlags ) ! ) ) ; printer . writeList ( ListFormat . TypeParameters , args , sourceFile , writer ) ; } } ) ; const lists = checker . getExpandedParameters ( candidateSignature ) ; const isVariadic = ! checker . hasEffectiveRestParameter ( candidateSignature ) ? _ => false : lists . length === 1 ? _ => true : pList => ! ! ( pList . length && ( pList [ pList . length - 1 ] as TransientSymbol ) . checkFlags & CheckFlags . RestParameter ) ; return lists . map ( parameterList => ( { isVariadic : isVariadic ( parameterList ) , parameters : parameterList . map ( p => createSignatureHelpParameterForParameter ( p , checker , enclosingDeclaration , sourceFile , printer ) ) , prefix : [ ... typeParameterParts , punctuationPart ( SyntaxKind . OpenParenToken ) ] , suffix : [ punctuationPart ( SyntaxKind . CloseParenToken ) ] } ) ) ; } function createSignatureHelpParameterForParameter ( parameter , checker , enclosingDeclaration , sourceFile , printer ) { const displayParts = mapToDisplayParts ( writer => { const param = checker . symbolToParameterDeclaration ( parameter , enclosingDeclaration , signatureHelpNodeBuilderFlags ) ! ; printer . writeNode ( EmitHint . Unspecified , param , sourceFile , writer ) ; } ) ; const isOptional = checker . isOptionalParameter ( parameter . valueDeclaration as ParameterDeclaration ) ; const isRest = ! ! ( ( parameter as TransientSymbol ) . checkFlags & CheckFlags . RestParameter ) ; return { name : parameter . name , documentation : parameter . getDocumentationComment ( checker ) , displayParts , isOptional , isRest } ; } function createSignatureHelpParameterForTypeParameter ( typeParameter , checker , enclosingDeclaration , sourceFile , printer ) { const displayParts = mapToDisplayParts ( writer => { const param = checker . typeParameterToDeclaration ( typeParameter , enclosingDeclaration , signatureHelpNodeBuilderFlags ) ! ; printer . writeNode ( EmitHint . Unspecified , param , sourceFile , writer ) ; } ) ; return { name : typeParameter . symbol . name , documentation : typeParameter . symbol . getDocumentationComment ( checker ) , displayParts , isOptional : false , isRest : false } ; } }