@@ -109,12 +109,14 @@ import {
109109 PrefixUnaryExpression ,
110110 PropertyDeclaration ,
111111 QuotePreference ,
112- SignatureDeclarationBase ,
113- skipParentheses ,
114- some ,
115- Symbol ,
116- SymbolFlags ,
117- SyntaxKind ,
112+ SignatureDeclarationBase ,
113+ Signature ,
114+ signatureHasRestParameter ,
115+ skipParentheses ,
116+ some ,
117+ Symbol ,
118+ SymbolFlags ,
119+ SyntaxKind ,
118120 TemplateLiteralLikeNode ,
119121 textSpanIntersectsWith ,
120122 tokenToString ,
@@ -292,44 +294,43 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
292294 }
293295 }
294296
295- function visitCallOrNewExpression ( expr : CallExpression | NewExpression ) {
296- const args = expr . arguments ;
297- if ( ! args || ! args . length ) {
298- return ;
299- }
300-
301- const signature = checker . getResolvedSignature ( expr ) ;
302- if ( signature === undefined ) return ;
303-
304- let signatureParamPos = 0 ;
305- for ( const originalArg of args ) {
306- const arg = skipParentheses ( originalArg ) ;
307- if ( shouldShowLiteralParameterNameHintsOnly ( preferences ) && ! isHintableLiteral ( arg ) ) {
308- signatureParamPos ++ ;
309- continue ;
310- }
311-
312- let spreadArgs = 0 ;
313- if ( isSpreadElement ( arg ) ) {
314- const spreadType = checker . getTypeAtLocation ( arg . expression ) ;
315- if ( checker . isTupleType ( spreadType ) ) {
316- const { elementFlags, fixedLength } = ( spreadType as TupleTypeReference ) . target ;
317- if ( fixedLength === 0 ) {
318- continue ;
319- }
320- const firstOptionalIndex = findIndex ( elementFlags , f => ! ( f & ElementFlags . Required ) ) ;
321- const requiredArgs = firstOptionalIndex < 0 ? fixedLength : firstOptionalIndex ;
322- if ( requiredArgs > 0 ) {
323- spreadArgs = firstOptionalIndex < 0 ? fixedLength : firstOptionalIndex ;
324- }
325- }
326- }
327-
328- const identifierInfo = checker . getParameterIdentifierInfoAtPosition ( signature , signatureParamPos ) ;
329- signatureParamPos = signatureParamPos + ( spreadArgs || 1 ) ;
330- if ( identifierInfo ) {
331- const { parameter, parameterName, isRestParameter : isFirstVariadicArgument } = identifierInfo ;
332- const isParameterNameNotSameAsArgument = preferences . includeInlayParameterNameHintsWhenArgumentMatchesName || ! identifierOrAccessExpressionPostfixMatchesParameterName ( arg , parameterName ) ;
297+ function visitCallOrNewExpression ( expr : CallExpression | NewExpression ) {
298+ const args = expr . arguments ;
299+ if ( ! args || ! args . length ) {
300+ return ;
301+ }
302+
303+ const signature = checker . getResolvedSignature ( expr ) ;
304+ if ( signature === undefined ) return ;
305+
306+ const argumentSpans = args . map ( arg => getArgumentSpan ( skipParentheses ( arg ) ) ) ;
307+ let totalArgumentPositions = 0 ;
308+ for ( const span of argumentSpans ) {
309+ totalArgumentPositions += span ;
310+ }
311+
312+ const nonRestParamCount = signature . parameters . length - ( signatureHasRestParameter ( signature ) ? 1 : 0 ) ;
313+ const restTupleInfo = getRestTupleInfo ( signature , nonRestParamCount , totalArgumentPositions ) ;
314+
315+ let signatureParamPos = 0 ;
316+ for ( let argIndex = 0 ; argIndex < args . length ; argIndex ++ ) {
317+ const originalArg = args [ argIndex ] ;
318+ const arg = skipParentheses ( originalArg ) ;
319+ const spreadArgs = argumentSpans [ argIndex ] ;
320+ if ( spreadArgs === 0 ) {
321+ continue ;
322+ }
323+ if ( shouldShowLiteralParameterNameHintsOnly ( preferences ) && ! isHintableLiteral ( arg ) ) {
324+ signatureParamPos += spreadArgs ;
325+ continue ;
326+ }
327+
328+ const parameterPos = getAdjustedParameterPosition ( signatureParamPos , restTupleInfo ) ;
329+ const identifierInfo = checker . getParameterIdentifierInfoAtPosition ( signature , parameterPos ) ;
330+ signatureParamPos = signatureParamPos + ( spreadArgs || 1 ) ;
331+ if ( identifierInfo ) {
332+ const { parameter, parameterName, isRestParameter : isFirstVariadicArgument } = identifierInfo ;
333+ const isParameterNameNotSameAsArgument = preferences . includeInlayParameterNameHintsWhenArgumentMatchesName || ! identifierOrAccessExpressionPostfixMatchesParameterName ( arg , parameterName ) ;
333334 if ( ! isParameterNameNotSameAsArgument && ! isFirstVariadicArgument ) {
334335 continue ;
335336 }
@@ -339,10 +340,68 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
339340 continue ;
340341 }
341342
342- addParameterHints ( name , parameter , originalArg . getStart ( ) , isFirstVariadicArgument ) ;
343- }
344- }
345- }
343+ addParameterHints ( name , parameter , originalArg . getStart ( ) , isFirstVariadicArgument ) ;
344+ }
345+ }
346+
347+ function getArgumentSpan ( arg : Expression ) {
348+ if ( isSpreadElement ( arg ) ) {
349+ const spreadType = checker . getTypeAtLocation ( arg . expression ) ;
350+ if ( checker . isTupleType ( spreadType ) ) {
351+ const { elementFlags, fixedLength } = ( spreadType as TupleTypeReference ) . target ;
352+ if ( fixedLength === 0 ) {
353+ return 0 ;
354+ }
355+ const firstOptionalIndex = findIndex ( elementFlags , f => ! ( f & ElementFlags . Required ) ) ;
356+ const requiredArgs = firstOptionalIndex < 0 ? fixedLength : firstOptionalIndex ;
357+ if ( requiredArgs > 0 ) {
358+ return requiredArgs ;
359+ }
360+ }
361+ }
362+ return 1 ;
363+ }
364+
365+ function getRestTupleInfo ( signature : Signature , paramCount : number , totalPositions : number ) {
366+ if ( ! signatureHasRestParameter ( signature ) ) {
367+ return undefined ;
368+ }
369+ const restParameter = signature . parameters [ paramCount ] ;
370+ if ( ! restParameter ) {
371+ return undefined ;
372+ }
373+ const restType = checker . getTypeOfSymbol ( restParameter ) ;
374+ if ( ! checker . isTupleType ( restType ) ) {
375+ return undefined ;
376+ }
377+ const elementFlags = ( restType as TupleTypeReference ) . target . elementFlags ;
378+ const restStartIndex = findIndex ( elementFlags , f => ! ! ( f & ElementFlags . Variable ) ) ;
379+ if ( restStartIndex < 0 ) {
380+ return undefined ;
381+ }
382+ const restTailCount = elementFlags . length - restStartIndex - 1 ;
383+ const restPositionsTotal = Math . max ( 0 , totalPositions - paramCount ) ;
384+ return { restStartIndex, restTailCount, restPositionsTotal, paramCount } ;
385+ }
386+
387+ function getAdjustedParameterPosition ( signatureParamPos : number , restInfo ?: { restStartIndex : number ; restTailCount : number ; restPositionsTotal : number ; paramCount : number ; } ) {
388+ if ( ! restInfo || signatureParamPos < restInfo . paramCount ) {
389+ return signatureParamPos ;
390+ }
391+ const restPosition = signatureParamPos - restInfo . paramCount ;
392+ if ( restPosition < restInfo . restStartIndex ) {
393+ return signatureParamPos ;
394+ }
395+ if ( restInfo . restTailCount > 0 && restInfo . restPositionsTotal >= restInfo . restTailCount ) {
396+ const tailStart = restInfo . restPositionsTotal - restInfo . restTailCount ;
397+ if ( restPosition >= tailStart ) {
398+ const tailIndex = restPosition - tailStart ;
399+ return restInfo . paramCount + restInfo . restStartIndex + 1 + tailIndex ;
400+ }
401+ }
402+ return restInfo . paramCount + restInfo . restStartIndex ;
403+ }
404+ }
346405
347406 function identifierOrAccessExpressionPostfixMatchesParameterName ( expr : Expression , parameterName : __String ) {
348407 if ( isIdentifier ( expr ) ) {
0 commit comments