diff --git a/tsc/internal/checker/exports.go b/tsc/internal/checker/exports.go index c22901a84d2ab..919cb99de74fc 100644 --- a/tsc/internal/checker/exports.go +++ b/tsc/internal/checker/exports.go @@ -220,6 +220,10 @@ func IsTupleType(t *Type) bool { return isTupleType(t) } +func GetEndElementCount(t *TupleType, flags ElementFlags) int { + return getEndElementCount(t, flags) +} + func IsTupleTypeTarget(t *Type) bool { return isTupleType(t) && t.Target() == t } diff --git a/tsc/internal/fourslash/tests/inlayHintsRestTrailingSpreadEdgeCases_test.go b/tsc/internal/fourslash/tests/inlayHintsRestTrailingSpreadEdgeCases_test.go new file mode 100644 index 0000000000000..438834cf70f37 --- /dev/null +++ b/tsc/internal/fourslash/tests/inlayHintsRestTrailingSpreadEdgeCases_test.go @@ -0,0 +1,60 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/ls/lsutil" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +func TestInlayHintsOptionalTrailingTupleAfterSpread(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `declare function optional(...args: [...head: T, first?: number, second?: string]): void; +function forward(x: T) { + optional(...x, 1); + optional(...x, 1, "two"); +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifyNoErrors(t) + f.VerifyBaselineInlayHints(t, nil /*span*/, &lsutil.UserPreferences{InlayHints: lsutil.InlayHintsPreferences{IncludeInlayParameterNameHints: lsutil.IncludeInlayParameterNameHintsAll}}) +} + +func TestInlayHintsSingleRestWithSpreadAndSuffix(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `declare function f(...args: [...head: string[], tail: string]): void; +declare const xs: string[]; +f(...xs, "middle", "end");` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifyNoErrors(t) + f.VerifyBaselineInlayHints(t, nil /*span*/, &lsutil.UserPreferences{InlayHints: lsutil.InlayHintsPreferences{IncludeInlayParameterNameHints: lsutil.IncludeInlayParameterNameHintsAll}}) +} + +func TestInlayHintsOptionalTrailingAfterTupleSpread(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `declare function optional(...args: [...head: T, first?: number, second?: string]): void; +function forward(x: [...T]) { + optional(...x, 1); +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifyNoErrors(t) + f.VerifyBaselineInlayHints(t, nil /*span*/, &lsutil.UserPreferences{InlayHints: lsutil.InlayHintsPreferences{IncludeInlayParameterNameHints: lsutil.IncludeInlayParameterNameHintsAll}}) +} + +func TestInlayHintsOptionalParameterAfterTupleSpread(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `declare function g(a: number, b?: number, ...rest: string[]): void; +declare const x: [number, number?]; +g(...x, "end");` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifyNoErrors(t) + f.VerifyBaselineInlayHints(t, nil /*span*/, &lsutil.UserPreferences{InlayHints: lsutil.InlayHintsPreferences{IncludeInlayParameterNameHints: lsutil.IncludeInlayParameterNameHintsAll}}) +} diff --git a/tsc/internal/fourslash/tests/inlayHintsTupleRestTrailing_test.go b/tsc/internal/fourslash/tests/inlayHintsTupleRestTrailing_test.go new file mode 100644 index 0000000000000..e7e4f0887dc1f --- /dev/null +++ b/tsc/internal/fourslash/tests/inlayHintsTupleRestTrailing_test.go @@ -0,0 +1,32 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/ls/lsutil" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +func TestInlayHintsTupleRestTrailing(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `declare function fn(prefix: boolean, ...args: [first: number, ...middle: string[], last: string]): void; +fn(true, 1, "a", "b", "end"); +fn(true, 1, "end"); +declare function onlyTrailing(...args: [...head: number[], tail: string]): void; +onlyTrailing(1, 2, "end"); +onlyTrailing("end"); +declare function noTrailing(...args: [first: number, ...rest: string[]]): void; +noTrailing(1, "a", "b"); +declare function multi(...args: [first: number, ...left: T, ...right: U, penultimate: string, last: boolean]): void; +function combined(middle: [...T, ...U]) { + multi(1, ...middle, "end", true); +} +declare function skip({ x }: { x: number }, next: number): void; +skip({ x: 1 }, 2);` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifyNoErrors(t) + f.VerifyBaselineInlayHints(t, nil /*span*/, &lsutil.UserPreferences{InlayHints: lsutil.InlayHintsPreferences{IncludeInlayParameterNameHints: lsutil.IncludeInlayParameterNameHintsAll}}) +} diff --git a/tsc/internal/ls/inlay_hints.go b/tsc/internal/ls/inlay_hints.go index 0864f8df361e7..92e065c086fba 100644 --- a/tsc/internal/ls/inlay_hints.go +++ b/tsc/internal/ls/inlay_hints.go @@ -2,7 +2,6 @@ package ls import ( "context" - "slices" "strings" "unicode" @@ -157,37 +156,48 @@ func (s *inlayHintState) visitCallOrNewExpression(expr *ast.CallOrNewExpression) return } + // A spread makes the effective argument count unknown, but explicit arguments after the last + // spread still have a known offset from the end. + argumentCount := len(args) + lastSpreadIndex := -1 + for i, arg := range args { + if ast.IsSpreadElement(ast.SkipParentheses(arg)) { + argumentCount = -1 + lastSpreadIndex = i + } + } signatureParamPos := 0 - for _, originalArg := range args { + hasUncertainTupleSpread := false + for i, originalArg := range args { arg := ast.SkipParentheses(originalArg) - if shouldShowLiteralParameterNameHintsOnly(s.preferences) && !isHintableLiteral(arg) { - signatureParamPos++ - continue - } - spreadArgs := 0 + skipSpreadHint := false if ast.IsSpreadElement(arg) { spreadType := s.checker.GetTypeAtLocation(arg.Expression()) if spreadType.IsTupleType() { - elementFlags := spreadType.Target().AsTupleType().ElementFlags() - fixedLength := spreadType.Target().AsTupleType().FixedLength() - if fixedLength == 0 { - continue - } - firstOptionalIndex := slices.IndexFunc(elementFlags, func(f checker.ElementFlags) bool { - return f&checker.ElementFlagsRequired == 0 - }) - requiredArgs := core.IfElse(firstOptionalIndex < 0, fixedLength, firstOptionalIndex) - if requiredArgs > 0 { - spreadArgs = requiredArgs - } + tupleType := spreadType.Target().AsTupleType() + spreadArgs = getRequiredTupleElementCount(tupleType) + // Optional or variable tuple elements make subsequent positional hints ambiguous. + hasUncertainTupleSpread = hasUncertainTupleSpread || spreadArgs < len(tupleType.ElementInfos()) + skipSpreadHint = tupleType.FixedLength() == 0 } } + if shouldShowLiteralParameterNameHintsOnly(s.preferences) && !isHintableLiteral(arg) { + signatureParamPos++ + continue + } + if skipSpreadHint { + continue + } - identifierInfo := s.getParameterIdentifierInfoAtPosition(signature, signatureParamPos) + offsetFromEnd := -1 + if lastSpreadIndex >= 0 && i > lastSpreadIndex { + offsetFromEnd = len(args) - i + } + identifierInfo := s.getParameterIdentifierInfoAtPosition(signature, signatureParamPos, argumentCount, offsetFromEnd, hasUncertainTupleSpread && !ast.IsSpreadElement(arg)) signatureParamPos = signatureParamPos + core.IfElse(spreadArgs > 0, spreadArgs, 1) if identifierInfo == nil { - return + continue } parameter := identifierInfo.parameter @@ -831,10 +841,13 @@ type parameterInfo struct { isRestParameter bool } -func (s *inlayHintState) getParameterIdentifierInfoAtPosition(signature *checker.Signature, pos int) *parameterInfo { +func (s *inlayHintState) getParameterIdentifierInfoAtPosition(signature *checker.Signature, pos int, argumentCount int, offsetFromEnd int, uncertainTupleSpread bool) *parameterInfo { parameters := signature.Parameters() paramCount := len(parameters) - core.IfElse(signature.HasRestParameter(), 1, 0) if pos < paramCount { + if uncertainTupleSpread { + return nil + } param := parameters[pos] paramId := getParameterDeclarationIdentifier(param) if paramId == nil { @@ -859,14 +872,36 @@ func (s *inlayHintState) getParameterIdentifierInfoAtPosition(signature *checker restType := s.checker.GetTypeOfSymbol(restParameter) if restType.IsTupleType() { - associatedNames := make([]*ast.Node, 0, len(restType.Target().AsTupleType().ElementInfos())) - for _, elementInfo := range restType.Target().AsTupleType().ElementInfos() { - labeledElement := elementInfo.LabeledDeclaration() - associatedNames = append(associatedNames, labeledElement) - } + tupleType := restType.Target().AsTupleType() + elementInfos := tupleType.ElementInfos() index := pos - paramCount - if index < len(associatedNames) { - associatedName := associatedNames[index] + restArgumentCount := argumentCount - paramCount + firstVariableIndex := tupleType.FixedLength() + trailingCount := checker.GetEndElementCount(tupleType, checker.ElementFlagsFixed) + // Optional trailing elements may be omitted, so only required ones can be aligned from the end. + requiredTrailingCount := checker.GetEndElementCount(tupleType, checker.ElementFlagsRequired) + if uncertainTupleSpread && (offsetFromEnd <= 0 || offsetFromEnd > requiredTrailingCount) { + return nil + } + variableCount := len(elementInfos) - firstVariableIndex - trailingCount + if trailingCount > 0 && variableCount > 0 { + switch { + case offsetFromEnd > 0 && offsetFromEnd <= requiredTrailingCount: + index = len(elementInfos) - offsetFromEnd + case offsetFromEnd > trailingCount: + return nil + case argumentCount >= 0 && restArgumentCount >= firstVariableIndex+trailingCount: + trailingStart := restArgumentCount - trailingCount + switch { + case index >= trailingStart: + index = len(elementInfos) - (restArgumentCount - index) + case index >= firstVariableIndex && (variableCount > 1 || index > firstVariableIndex): + return nil + } + } + } + if index < len(elementInfos) { + associatedName := elementInfos[index].LabeledDeclaration() if associatedName != nil { debug.Assert(ast.IsIdentifier(associatedName.Name())) var isRestTupleElement bool @@ -886,6 +921,9 @@ func (s *inlayHintState) getParameterIdentifierInfoAtPosition(signature *checker return nil } + if uncertainTupleSpread { + return nil + } if pos == paramCount { return ¶meterInfo{ parameter: restId, diff --git a/tsc/internal/ls/signaturehelp.go b/tsc/internal/ls/signaturehelp.go index 78aa78490d4b8..19c9c87d3c273 100644 --- a/tsc/internal/ls/signaturehelp.go +++ b/tsc/internal/ls/signaturehelp.go @@ -1078,23 +1078,19 @@ func getSpreadElementCount(node *ast.SpreadElement, c *checker.Checker) int { if tupleType == nil { return 0 } - elementFlags := tupleType.ElementFlags() - fixedLength := tupleType.FixedLength() - if fixedLength == 0 { - return 0 - } - - firstOptionalIndex := core.FindIndex(elementFlags, func(f checker.ElementFlags) bool { - return (f&checker.ElementFlagsRequired == 0) - }) - if firstOptionalIndex < 0 { - return fixedLength - } - return firstOptionalIndex + return getRequiredTupleElementCount(tupleType) } return 0 } +func getRequiredTupleElementCount(tupleType *checker.TupleType) int { + fixedLength := tupleType.FixedLength() + firstOptionalIndex := core.FindIndex(tupleType.ElementInfos()[:fixedLength], func(info checker.TupleElementInfo) bool { + return info.TupleElementFlags()&checker.ElementFlagsRequired == 0 + }) + return core.IfElse(firstOptionalIndex < 0, fixedLength, firstOptionalIndex) +} + func getArgumentIndex(node *ast.Node, arguments *ast.NodeList, sourceFile *ast.SourceFile, c *checker.Checker) int { return getArgumentIndexOrCount(getTokenFromNodeList(arguments, node.Parent, sourceFile), node, c) } diff --git a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsInteractiveRestParameters2.baseline b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsInteractiveRestParameters2.baseline index f7f00df072f35..d1e67912a4693 100644 --- a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsInteractiveRestParameters2.baseline +++ b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsInteractiveRestParameters2.baseline @@ -255,38 +255,6 @@ "paddingRight": true } - foo(...x, 3); - ^ -{ - "position": { - "line": 11, - "character": 14 - }, - "label": [ - { - "value": "b", - "location": { - "uri": "file:///inlayHintsInteractiveRestParameters2.ts", - "range": { - "start": { - "line": 0, - "character": 25 - }, - "end": { - "line": 0, - "character": 26 - } - } - } - }, - { - "value": ":" - } - ], - "kind": 2, - "paddingRight": true -} - foo(...x, 3); ^ { diff --git a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalParameterAfterTupleSpread.baseline b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalParameterAfterTupleSpread.baseline new file mode 100644 index 0000000000000..d8b46d917f841 --- /dev/null +++ b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalParameterAfterTupleSpread.baseline @@ -0,0 +1,32 @@ +// === Inlay Hints === +g(...x, "end"); + ^ +{ + "position": { + "line": 2, + "character": 2 + }, + "label": [ + { + "value": "a", + "location": { + "uri": "file:///inlayHintsOptionalParameterAfterTupleSpread.ts", + "range": { + "start": { + "line": 0, + "character": 19 + }, + "end": { + "line": 0, + "character": 20 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalTrailingAfterTupleSpread.baseline b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalTrailingAfterTupleSpread.baseline new file mode 100644 index 0000000000000..1d8aa81fc427b --- /dev/null +++ b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalTrailingAfterTupleSpread.baseline @@ -0,0 +1,2 @@ +// === Inlay Hints === +=== No inlay hints === \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalTrailingTupleAfterSpread.baseline b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalTrailingTupleAfterSpread.baseline new file mode 100644 index 0000000000000..b812b30a245d3 --- /dev/null +++ b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsOptionalTrailingTupleAfterSpread.baseline @@ -0,0 +1,160 @@ +// === Inlay Hints === + optional(...x, 1); + ^ +{ + "position": { + "line": 2, + "character": 16 + }, + "label": [ + { + "value": "...head", + "location": { + "uri": "file:///inlayHintsOptionalTrailingTupleAfterSpread.ts", + "range": { + "start": { + "line": 0, + "character": 60 + }, + "end": { + "line": 0, + "character": 64 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + + optional(...x, 1); + ^ +{ + "position": { + "line": 2, + "character": 22 + }, + "label": [ + { + "value": "first", + "location": { + "uri": "file:///inlayHintsOptionalTrailingTupleAfterSpread.ts", + "range": { + "start": { + "line": 0, + "character": 69 + }, + "end": { + "line": 0, + "character": 74 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + + optional(...x, 1, "two"); + ^ +{ + "position": { + "line": 3, + "character": 16 + }, + "label": [ + { + "value": "...head", + "location": { + "uri": "file:///inlayHintsOptionalTrailingTupleAfterSpread.ts", + "range": { + "start": { + "line": 0, + "character": 60 + }, + "end": { + "line": 0, + "character": 64 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + + optional(...x, 1, "two"); + ^ +{ + "position": { + "line": 3, + "character": 22 + }, + "label": [ + { + "value": "first", + "location": { + "uri": "file:///inlayHintsOptionalTrailingTupleAfterSpread.ts", + "range": { + "start": { + "line": 0, + "character": 69 + }, + "end": { + "line": 0, + "character": 74 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + + optional(...x, 1, "two"); + ^ +{ + "position": { + "line": 3, + "character": 25 + }, + "label": [ + { + "value": "second", + "location": { + "uri": "file:///inlayHintsOptionalTrailingTupleAfterSpread.ts", + "range": { + "start": { + "line": 0, + "character": 85 + }, + "end": { + "line": 0, + "character": 91 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsRestParameters2.baseline b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsRestParameters2.baseline index 8fe8f3267f525..8ca414ac75cd9 100644 --- a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsRestParameters2.baseline +++ b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsRestParameters2.baseline @@ -255,38 +255,6 @@ "paddingRight": true } - foo(...x, 3); - ^ -{ - "position": { - "line": 11, - "character": 14 - }, - "label": [ - { - "value": "b", - "location": { - "uri": "file:///inlayHintsRestParameters2.ts", - "range": { - "start": { - "line": 0, - "character": 25 - }, - "end": { - "line": 0, - "character": 26 - } - } - } - }, - { - "value": ":" - } - ], - "kind": 2, - "paddingRight": true -} - foo(...x, 3); ^ { diff --git a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsSingleRestWithSpreadAndSuffix.baseline b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsSingleRestWithSpreadAndSuffix.baseline new file mode 100644 index 0000000000000..e70c044f810d4 --- /dev/null +++ b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsSingleRestWithSpreadAndSuffix.baseline @@ -0,0 +1,64 @@ +// === Inlay Hints === +f(...xs, "middle", "end"); + ^ +{ + "position": { + "line": 2, + "character": 2 + }, + "label": [ + { + "value": "...head", + "location": { + "uri": "file:///inlayHintsSingleRestWithSpreadAndSuffix.ts", + "range": { + "start": { + "line": 0, + "character": 32 + }, + "end": { + "line": 0, + "character": 36 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +f(...xs, "middle", "end"); + ^ +{ + "position": { + "line": 2, + "character": 19 + }, + "label": [ + { + "value": "tail", + "location": { + "uri": "file:///inlayHintsSingleRestWithSpreadAndSuffix.ts", + "range": { + "start": { + "line": 0, + "character": 48 + }, + "end": { + "line": 0, + "character": 52 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsTupleRestTrailing.baseline b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsTupleRestTrailing.baseline new file mode 100644 index 0000000000000..25c0274b2ca58 --- /dev/null +++ b/tsc/testdata/baselines/reference/fourslash/inlayHints/inlayHintsTupleRestTrailing.baseline @@ -0,0 +1,512 @@ +// === Inlay Hints === +fn(true, 1, "a", "b", "end"); + ^ +{ + "position": { + "line": 1, + "character": 3 + }, + "label": [ + { + "value": "prefix", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 0, + "character": 20 + }, + "end": { + "line": 0, + "character": 26 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +fn(true, 1, "a", "b", "end"); + ^ +{ + "position": { + "line": 1, + "character": 9 + }, + "label": [ + { + "value": "first", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 0, + "character": 47 + }, + "end": { + "line": 0, + "character": 52 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +fn(true, 1, "a", "b", "end"); + ^ +{ + "position": { + "line": 1, + "character": 12 + }, + "label": [ + { + "value": "...middle", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 0, + "character": 65 + }, + "end": { + "line": 0, + "character": 71 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +fn(true, 1, "a", "b", "end"); + ^ +{ + "position": { + "line": 1, + "character": 22 + }, + "label": [ + { + "value": "last", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 0, + "character": 83 + }, + "end": { + "line": 0, + "character": 87 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +fn(true, 1, "end"); + ^ +{ + "position": { + "line": 2, + "character": 3 + }, + "label": [ + { + "value": "prefix", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 0, + "character": 20 + }, + "end": { + "line": 0, + "character": 26 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +fn(true, 1, "end"); + ^ +{ + "position": { + "line": 2, + "character": 9 + }, + "label": [ + { + "value": "first", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 0, + "character": 47 + }, + "end": { + "line": 0, + "character": 52 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +fn(true, 1, "end"); + ^ +{ + "position": { + "line": 2, + "character": 12 + }, + "label": [ + { + "value": "last", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 0, + "character": 83 + }, + "end": { + "line": 0, + "character": 87 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +onlyTrailing(1, 2, "end"); + ^ +{ + "position": { + "line": 4, + "character": 13 + }, + "label": [ + { + "value": "...head", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 3, + "character": 43 + }, + "end": { + "line": 3, + "character": 47 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +onlyTrailing(1, 2, "end"); + ^ +{ + "position": { + "line": 4, + "character": 19 + }, + "label": [ + { + "value": "tail", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 3, + "character": 59 + }, + "end": { + "line": 3, + "character": 63 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +onlyTrailing("end"); + ^ +{ + "position": { + "line": 5, + "character": 13 + }, + "label": [ + { + "value": "tail", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 3, + "character": 59 + }, + "end": { + "line": 3, + "character": 63 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +noTrailing(1, "a", "b"); + ^ +{ + "position": { + "line": 7, + "character": 11 + }, + "label": [ + { + "value": "first", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 6, + "character": 38 + }, + "end": { + "line": 6, + "character": 43 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +noTrailing(1, "a", "b"); + ^ +{ + "position": { + "line": 7, + "character": 14 + }, + "label": [ + { + "value": "...rest", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 6, + "character": 56 + }, + "end": { + "line": 6, + "character": 60 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + + multi(1, ...middle, "end", true); + ^ +{ + "position": { + "line": 10, + "character": 16 + }, + "label": [ + { + "value": "first", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 8, + "character": 73 + }, + "end": { + "line": 8, + "character": 78 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + + multi(1, ...middle, "end", true); + ^ +{ + "position": { + "line": 10, + "character": 30 + }, + "label": [ + { + "value": "penultimate", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 8, + "character": 113 + }, + "end": { + "line": 8, + "character": 124 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + + multi(1, ...middle, "end", true); + ^ +{ + "position": { + "line": 10, + "character": 37 + }, + "label": [ + { + "value": "last", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 8, + "character": 134 + }, + "end": { + "line": 8, + "character": 138 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} + +skip({ x: 1 }, 2); + ^ +{ + "position": { + "line": 13, + "character": 15 + }, + "label": [ + { + "value": "next", + "location": { + "uri": "file:///inlayHintsTupleRestTrailing.ts", + "range": { + "start": { + "line": 12, + "character": 44 + }, + "end": { + "line": 12, + "character": 48 + } + } + } + }, + { + "value": ":" + } + ], + "kind": 2, + "paddingRight": true +} \ No newline at end of file