namespace ts . InlayHints { const maxHintsLength = 30 ; const leadingParameterNameCommentRegexFactory = ( name ) => { return new RegExp ( `^\\s?/\\*\\*?\\s?${ name }\\s?\\*\\/\\s?$` ) ; } ; function shouldShowParameterNameHints ( preferences ) { return preferences . includeInlayParameterNameHints === "literals" || preferences . includeInlayParameterNameHints === "all" ; } function shouldShowLiteralParameterNameHintsOnly ( preferences ) { return preferences . includeInlayParameterNameHints === "literals" ; } export function provideInlayHints ( context ) { const { file , program , span , cancellationToken , preferences } = context ; const sourceFileText = file . text ; const compilerOptions = program . getCompilerOptions ( ) ; const checker = program . getTypeChecker ( ) ; const result = [ ] ; visitor ( file ) ; return result ; function visitor ( node ) { if ( ! node || node . getFullWidth ( ) === 0 ) { return ; } switch ( node . kind ) { case SyntaxKind . ModuleDeclaration : case SyntaxKind . ClassDeclaration : case SyntaxKind . InterfaceDeclaration : case SyntaxKind . FunctionDeclaration : case SyntaxKind . ClassExpression : case SyntaxKind . FunctionExpression : case SyntaxKind . MethodDeclaration : case SyntaxKind . ArrowFunction : cancellationToken . throwIfCancellationRequested ( ) ; } if ( ! textSpanIntersectsWith ( span , node . pos , node . getFullWidth ( ) ) ) { return ; } if ( isTypeNode ( node ) ) { return ; } if ( preferences . includeInlayVariableTypeHints && isVariableDeclaration ( node ) ) { visitVariableLikeDeclaration ( node ) ; } else if ( preferences . includeInlayPropertyDeclarationTypeHints && isPropertyDeclaration ( node ) ) { visitVariableLikeDeclaration ( node ) ; } else if ( preferences . includeInlayEnumMemberValueHints && isEnumMember ( node ) ) { visitEnumMember ( node ) ; } else if ( shouldShowParameterNameHints ( preferences ) && ( isCallExpression ( node ) || isNewExpression ( node ) ) ) { visitCallOrNewExpression ( node ) ; } else { if ( preferences . includeInlayFunctionParameterTypeHints && isFunctionLikeDeclaration ( node ) && hasContextSensitiveParameters ( node ) ) { visitFunctionLikeForParameterType ( node ) ; } if ( preferences . includeInlayFunctionLikeReturnTypeHints && isSignatureSupportingReturnAnnotation ( node ) ) { visitFunctionDeclarationLikeForReturnType ( node ) ; } } return forEachChild ( node , visitor ) ; } function isSignatureSupportingReturnAnnotation ( node ) { return isArrowFunction ( node ) || isFunctionExpression ( node ) || isFunctionDeclaration ( node ) || isMethodDeclaration ( node ) || isGetAccessorDeclaration ( node ) ; } function addParameterHints ( text , position , isFirstVariadicArgument ) { result . push ( { text : `${ isFirstVariadicArgument ? "..." : "" }${ truncation ( text , maxHintsLength ) }:` , position , kind : InlayHintKind . Parameter , whitespaceAfter : true , } ) ; } function addTypeHints ( text , position ) { result . push ( { text : `: ${ truncation ( text , maxHintsLength ) }` , position , kind : InlayHintKind . Type , whitespaceBefore : true , } ) ; } function addEnumMemberValueHints ( text , position ) { result . push ( { text : `= ${ truncation ( text , maxHintsLength ) }` , position , kind : InlayHintKind . Enum , whitespaceBefore : true , } ) ; } function visitEnumMember ( member ) { if ( member . initializer ) { return ; } const enumValue = checker . getConstantValue ( member ) ; if ( enumValue !== undefined ) { addEnumMemberValueHints ( enumValue . toString ( ) , member . end ) ; } } function isModuleReferenceType ( type ) { return type . symbol && ( type . symbol . flags & SymbolFlags . Module ) ; } function visitVariableLikeDeclaration ( decl ) { if ( ! decl . initializer || isBindingPattern ( decl . name ) ) { return ; } const effectiveTypeAnnotation = getEffectiveTypeAnnotationNode ( decl ) ; if ( effectiveTypeAnnotation ) { return ; } const declarationType = checker . getTypeAtLocation ( decl ) ; if ( isModuleReferenceType ( declarationType ) ) { return ; } const typeDisplayString = printTypeInSingleLine ( declarationType ) ; if ( typeDisplayString ) { addTypeHints ( typeDisplayString , decl . name . end ) ; } } function visitCallOrNewExpression ( expr ) { const args = expr . arguments ; if ( ! args || ! args . length ) { return ; } const candidates = [ ] ; const signature = checker . getResolvedSignatureForSignatureHelp ( expr , candidates ) ; if ( ! signature || ! candidates . length ) { return ; } for ( let i = 0 ; i < args . length ; ++ i ) { const originalArg = args [ i ] ; const arg = skipParentheses ( originalArg ) ; if ( shouldShowLiteralParameterNameHintsOnly ( preferences ) && ! isHintableLiteral ( arg ) ) { continue ; } const identifierNameInfo = checker . getParameterIdentifierNameAtPosition ( signature , i ) ; if ( identifierNameInfo ) { const [ parameterName , isFirstVariadicArgument ] = identifierNameInfo ; const isParameterNameNotSameAsArgument = preferences . includeInlayParameterNameHintsWhenArgumentMatchesName || ! identifierOrAccessExpressionPostfixMatchesParameterName ( arg , parameterName ) ; if ( ! isParameterNameNotSameAsArgument && ! isFirstVariadicArgument ) { continue ; } const name = unescapeLeadingUnderscores ( parameterName ) ; if ( leadingCommentsContainsParameterName ( arg , name ) ) { continue ; } addParameterHints ( name , originalArg . getStart ( ) , isFirstVariadicArgument ) ; } } } function identifierOrAccessExpressionPostfixMatchesParameterName ( expr , parameterName ) { if ( isIdentifier ( expr ) ) { return expr . text === parameterName ; } if ( isPropertyAccessExpression ( expr ) ) { return expr . name . text === parameterName ; } return false ; } function leadingCommentsContainsParameterName ( node , name ) { if ( ! isIdentifierText ( name , compilerOptions . target , getLanguageVariant ( file . scriptKind ) ) ) { return false ; } const ranges = getLeadingCommentRanges ( sourceFileText , node . pos ) ; if ( ! ranges ?. length ) { return false ; } const regex = leadingParameterNameCommentRegexFactory ( name ) ; return some ( ranges , range => regex . test ( sourceFileText . substring ( range . pos , range . end ) ) ) ; } function isHintableLiteral ( node ) { switch ( node . kind ) { case SyntaxKind . PrefixUnaryExpression : { const operand = ( node as PrefixUnaryExpression ) . operand ; return isLiteralExpression ( operand ) || isIdentifier ( operand ) && isInfinityOrNaNString ( operand . escapedText ) ; } case SyntaxKind . TrueKeyword : case SyntaxKind . FalseKeyword : case SyntaxKind . NullKeyword : case SyntaxKind . NoSubstitutionTemplateLiteral : case SyntaxKind . TemplateExpression : return true ; case SyntaxKind . Identifier : { const name = ( node as Identifier ) . escapedText ; return isUndefined ( name ) || isInfinityOrNaNString ( name ) ; } } return isLiteralExpression ( node ) ; } function visitFunctionDeclarationLikeForReturnType ( decl ) { if ( isArrowFunction ( decl ) ) { if ( ! findChildOfKind ( decl , SyntaxKind . OpenParenToken , file ) ) { return ; } } const effectiveTypeAnnotation = getEffectiveReturnTypeNode ( decl ) ; if ( effectiveTypeAnnotation || ! decl . body ) { return ; } const signature = checker . getSignatureFromDeclaration ( decl ) ; if ( ! signature ) { return ; } const returnType = checker . getReturnTypeOfSignature ( signature ) ; if ( isModuleReferenceType ( returnType ) ) { return ; } const typeDisplayString = printTypeInSingleLine ( returnType ) ; if ( ! typeDisplayString ) { return ; } addTypeHints ( typeDisplayString , getTypeAnnotationPosition ( decl ) ) ; } function getTypeAnnotationPosition ( decl ) { const closeParenToken = findChildOfKind ( decl , SyntaxKind . CloseParenToken , file ) ; if ( closeParenToken ) { return closeParenToken . end ; } return decl . parameters . end ; } function visitFunctionLikeForParameterType ( node ) { const signature = checker . getSignatureFromDeclaration ( node ) ; if ( ! signature ) { return ; } for ( let i = 0 ; i < node . parameters . length && i < signature . parameters . length ; ++ i ) { const param = node . parameters [ i ] ; const effectiveTypeAnnotation = getEffectiveTypeAnnotationNode ( param ) ; if ( effectiveTypeAnnotation ) { continue ; } const typeDisplayString = getParameterDeclarationTypeDisplayString ( signature . parameters [ i ] ) ; if ( ! typeDisplayString ) { continue ; } addTypeHints ( typeDisplayString , param . name . end ) ; } } function getParameterDeclarationTypeDisplayString ( symbol ) { const valueDeclaration = symbol . valueDeclaration ; if ( ! valueDeclaration || ! isParameter ( valueDeclaration ) ) { return undefined ; } const signatureParamType = checker . getTypeOfSymbolAtLocation ( symbol , valueDeclaration ) ; if ( isModuleReferenceType ( signatureParamType ) ) { return undefined ; } return printTypeInSingleLine ( signatureParamType ) ; } function truncation ( text , maxLength ) { if ( text . length > maxLength ) { return text . substr ( 0 , maxLength - "..." . length ) + "..." ; } return text ; } function printTypeInSingleLine ( type ) { const flags = NodeBuilderFlags . IgnoreErrors | TypeFormatFlags . AllowUniqueESSymbolType | TypeFormatFlags . UseAliasDefinedOutsideCurrentScope ; const options = { removeComments : true } ; const printer = createPrinter ( options ) ; return usingSingleLineStringWriter ( writer => { const typeNode = checker . typeToTypeNode ( type , undefined , flags , writer ) ; Debug . assertIsDefined ( typeNode , "should always get typenode" ) ; printer . writeNode ( EmitHint . Unspecified , typeNode , file , writer ) ; } ) ; } function isUndefined ( name ) { return name === "undefined" ; } } }