diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index a2381d491ea58..c9025109d2491 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -1,7 +1,10 @@ import { __String, + ArrayTypeNode, ArrowFunction, CallExpression, + ConditionalTypeNode, + ConstructorTypeNode, createPrinterWithRemoveComments, createTextSpanFromNode, Debug, @@ -16,17 +19,24 @@ import { FunctionDeclaration, FunctionExpression, FunctionLikeDeclaration, + FunctionTypeNode, GetAccessorDeclaration, getEffectiveReturnTypeNode, getEffectiveTypeAnnotationNode, getLanguageVariant, getLeadingCommentRanges, + getNameOfDeclaration, hasContextSensitiveParameters, Identifier, + idText, + ImportTypeNode, + IndexedAccessTypeNode, + InferTypeNode, InlayHint, InlayHintDisplayPart, InlayHintKind, InlayHintsContext, + IntersectionTypeNode, isArrowFunction, isAssertionExpression, isBindingPattern, @@ -52,30 +62,48 @@ import { isTypeNode, isVarConst, isVariableDeclaration, + LiteralTypeNode, + MappedTypeNode, MethodDeclaration, + NamedTupleMember, NewExpression, Node, + NodeArray, NodeBuilderFlags, + NumericLiteral, + OptionalTypeNode, ParameterDeclaration, + ParenthesizedTypeNode, PrefixUnaryExpression, PropertyDeclaration, + PropertySignature, + QualifiedName, + RestTypeNode, Signature, skipParentheses, some, + StringLiteral, Symbol, SymbolFlags, SyntaxKind, textSpanIntersectsWith, + tokenToString, + TupleTypeNode, TupleTypeReference, Type, + TypeLiteralNode, + TypeOperatorNode, + TypeParameterDeclaration, + TypePredicateNode, + TypeQueryNode, + TypeReferenceNode, unescapeLeadingUnderscores, + UnionTypeNode, UserPreferences, usingSingleLineStringWriter, VariableDeclaration, } from "./_namespaces/ts"; -const maxTypeHintLength = 30; - const leadingParameterNameCommentRegexFactory = (name: string) => { return new RegExp(`^\\s?/\\*\\*?\\s?${name}\\s?\\*\\/\\s?$`); }; @@ -176,9 +204,10 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { }); } - function addTypeHints(text: string, position: number) { + function addTypeHints(hintText: string | InlayHintDisplayPart[], position: number) { result.push({ - text: `: ${text.length > maxTypeHintLength ? text.substr(0, maxTypeHintLength - "...".length) + "..." : text}`, + text: typeof hintText === "string" ? `: ${hintText}` : "", + displayParts: typeof hintText === "string" ? undefined : [{ text: ": " }, ...hintText], position, kind: InlayHintKind.Type, whitespaceBefore: true, @@ -224,13 +253,14 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { return; } - const typeDisplayString = printTypeInSingleLine(declarationType); - if (typeDisplayString) { - const isVariableNameMatchesType = preferences.includeInlayVariableTypeHintsWhenTypeMatchesName === false && equateStringsCaseInsensitive(decl.name.getText(), typeDisplayString); + const hints = typeToInlayHintParts(declarationType); + if (hints) { + const hintText = typeof hints === "string" ? hints : hints.map(part => part.text).join(""); + const isVariableNameMatchesType = preferences.includeInlayVariableTypeHintsWhenTypeMatchesName === false && equateStringsCaseInsensitive(decl.name.getText(), hintText); if (isVariableNameMatchesType) { return; } - addTypeHints(typeDisplayString, decl.name.end); + addTypeHints(hints, decl.name.end); } } @@ -355,12 +385,10 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { return; } - const typeDisplayString = printTypeInSingleLine(returnType); - if (!typeDisplayString) { - return; + const hint = typeToInlayHintParts(returnType); + if (hint) { + addTypeHints(hint, getTypeAnnotationPosition(decl)); } - - addTypeHints(typeDisplayString, getTypeAnnotationPosition(decl)); } function getTypeAnnotationPosition(decl: FunctionDeclaration | ArrowFunction | FunctionExpression | MethodDeclaration | GetAccessorDeclaration) { @@ -422,6 +450,308 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] { }); } + function typeToInlayHintParts(type: Type): InlayHintDisplayPart[] | string { + if (!shouldUseInteractiveInlayHints(preferences)) { + return printTypeInSingleLine(type); + } + + const flags = NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.AllowUniqueESSymbolType | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope; + const typeNode = checker.typeToTypeNode(type, /*enclosingDeclaration*/ undefined, flags); + Debug.assertIsDefined(typeNode, "should always get typenode"); + + const parts: InlayHintDisplayPart[] = []; + visitForDisplayParts(typeNode); + return parts; + + function visitForDisplayParts(node: Node) { + if (!node) { + return; + } + + const tokenString = tokenToString(node.kind); + if (tokenString) { + parts.push({ text: tokenString }); + return; + } + + switch (node.kind) { + case SyntaxKind.Identifier: + const identifier = node as Identifier; + const identifierText = idText(identifier); + const name = identifier.symbol && identifier.symbol.declarations && identifier.symbol.declarations.length && getNameOfDeclaration(identifier.symbol.declarations[0]); + if (name) { + parts.push(getNodeDisplayPart(identifierText, name)); + } + else { + parts.push({ text: identifierText }); + } + break; + case SyntaxKind.NumericLiteral: + parts.push({ text: (node as NumericLiteral).text }); + break; + case SyntaxKind.StringLiteral: + parts.push({ text: `"${(node as StringLiteral).text}"` }); + break; + case SyntaxKind.QualifiedName: + const qualifiedName = node as QualifiedName; + visitForDisplayParts(qualifiedName.left); + parts.push({ text: "." }); + visitForDisplayParts(qualifiedName.right); + break; + case SyntaxKind.TypePredicate: + const predicate = node as TypePredicateNode; + if (predicate.assertsModifier) { + parts.push({ text: "asserts " }); + } + visitForDisplayParts(predicate.parameterName); + if (predicate.type) { + parts.push({ text: " is " }); + visitForDisplayParts(predicate.type); + } + break; + case SyntaxKind.TypeReference: + const typeReference = node as TypeReferenceNode; + visitForDisplayParts(typeReference.typeName); + if (typeReference.typeArguments) { + parts.push({ text: "<" }); + visitDisplayPartList(typeReference.typeArguments, ", "); + parts.push({ text: ">" }); + } + break; + case SyntaxKind.TypeParameter: + const typeParameter = node as TypeParameterDeclaration; + if (typeParameter.modifiers) { + visitDisplayPartList(typeParameter.modifiers, " "); + } + visitForDisplayParts(typeParameter.name); + if (typeParameter.constraint) { + parts.push({ text: " extends " }); + visitForDisplayParts(typeParameter.constraint); + } + if (typeParameter.default) { + parts.push({ text: " = " }); + visitForDisplayParts(typeParameter.default); + } + break; + case SyntaxKind.Parameter: + const parameter = node as ParameterDeclaration; + if (parameter.modifiers) { + visitDisplayPartList(parameter.modifiers, " "); + } + if (parameter.dotDotDotToken) { + parts.push({ text: "..." }); + } + visitForDisplayParts(parameter.name); + if (parameter.questionToken) { + parts.push({ text: "?" }); + } + if (parameter.type) { + parts.push({ text: ": " }); + visitForDisplayParts(parameter.type); + } + break; + case SyntaxKind.ConstructorType: + const constructorType = node as ConstructorTypeNode; + parts.push({ text: "new " }); + if (constructorType.typeParameters) { + parts.push({ text: "<" }); + visitDisplayPartList(constructorType.typeParameters, ", "); + parts.push({ text: ">" }); + } + parts.push({ text: "(" }); + visitDisplayPartList(constructorType.parameters, ", "); + parts.push({ text: ")" }); + parts.push({ text: " => " }); + visitForDisplayParts(constructorType.type); + break; + case SyntaxKind.TypeQuery: + const typeQuery = node as TypeQueryNode; + parts.push({ text: "typeof " }); + visitForDisplayParts(typeQuery.exprName); + if (typeQuery.typeArguments) { + parts.push({ text: "<" }); + visitDisplayPartList(typeQuery.typeArguments, ", "); + parts.push({ text: ">" }); + } + break; + case SyntaxKind.TypeLiteral: + const typeLiteral = node as TypeLiteralNode; + parts.push({ text: "{" }); + if (typeLiteral.members.length) { + parts.push({ text: " " }); + visitDisplayPartList(typeLiteral.members, "; "); + parts.push({ text: " " }); + } + parts.push({ text: "}" }); + break; + case SyntaxKind.ArrayType: + visitForDisplayParts((node as ArrayTypeNode).elementType); + parts.push({ text: "[]" }); + break; + case SyntaxKind.TupleType: + parts.push({ text: "[" }); + visitDisplayPartList((node as TupleTypeNode).elements, ", "); + parts.push({ text: "]" }); + break; + case SyntaxKind.NamedTupleMember: + const member = node as NamedTupleMember; + if (member.dotDotDotToken) { + parts.push({ text: "..." }); + } + visitForDisplayParts(member.name); + if (member.questionToken) { + parts.push({ text: "?" }); + } + parts.push({ text: ": " }); + visitForDisplayParts(member.type); + break; + case SyntaxKind.OptionalType: + visitForDisplayParts((node as OptionalTypeNode).type); + parts.push({ text: "?" }); + break; + case SyntaxKind.RestType: + parts.push({ text: "..." }); + visitForDisplayParts((node as RestTypeNode).type); + break; + case SyntaxKind.UnionType: + visitDisplayPartList((node as UnionTypeNode).types, " | "); + break; + case SyntaxKind.IntersectionType: + visitDisplayPartList((node as IntersectionTypeNode).types, " & "); + break; + case SyntaxKind.ConditionalType: + const conditionalType = node as ConditionalTypeNode; + visitForDisplayParts(conditionalType.checkType); + parts.push({ text: " extends " }); + visitForDisplayParts(conditionalType.extendsType); + parts.push({ text: " ? " }); + visitForDisplayParts(conditionalType.trueType); + parts.push({ text: " : " }); + visitForDisplayParts(conditionalType.falseType); + break; + case SyntaxKind.InferType: + parts.push({ text: "infer " }); + visitForDisplayParts((node as InferTypeNode).typeParameter); + break; + case SyntaxKind.ParenthesizedType: + parts.push({ text: "(" }); + visitForDisplayParts((node as ParenthesizedTypeNode).type); + parts.push({ text: ")" }); + break; + case SyntaxKind.TypeOperator: + const typeOperator = node as TypeOperatorNode; + parts.push({ text: `${tokenToString(typeOperator.operator)} ` }); + visitForDisplayParts(typeOperator.type); + break; + case SyntaxKind.IndexedAccessType: + const indexedAccess = node as IndexedAccessTypeNode; + visitForDisplayParts(indexedAccess.objectType); + parts.push({ text: "[" }); + visitForDisplayParts(indexedAccess.indexType); + parts.push({ text: "]" }); + break; + case SyntaxKind.MappedType: + const mappedType = node as MappedTypeNode; + parts.push({ text: "{ " }); + if (mappedType.readonlyToken) { + if (mappedType.readonlyToken.kind === SyntaxKind.PlusToken) { + parts.push({ text: "+" }); + } + else if (mappedType.readonlyToken.kind === SyntaxKind.MinusToken) { + parts.push({ text: "-" }); + } + parts.push({ text: "readonly " }); + } + parts.push({ text: "[" }); + visitForDisplayParts(mappedType.typeParameter); + if (mappedType.nameType) { + parts.push({ text: " as " }); + visitForDisplayParts(mappedType.nameType); + } + parts.push({ text: "]" }); + if (mappedType.questionToken) { + if (mappedType.questionToken.kind === SyntaxKind.PlusToken) { + parts.push({ text: "+" }); + } + else if (mappedType.questionToken.kind === SyntaxKind.MinusToken) { + parts.push({ text: "-" }); + } + parts.push({ text: "?" }); + } + parts.push({ text: ": " }); + if (mappedType.type) { + visitForDisplayParts(mappedType.type); + } + parts.push({ text: "; }" }); + break; + case SyntaxKind.LiteralType: + visitForDisplayParts((node as LiteralTypeNode).literal); + break; + case SyntaxKind.FunctionType: + const functionType = node as FunctionTypeNode; + if (functionType.typeParameters) { + parts.push({ text: "<" }); + visitDisplayPartList(functionType.typeParameters, ", "); + parts.push({ text: ">" }); + } + parts.push({ text: "(" }); + visitDisplayPartList(functionType.parameters, ", "); + parts.push({ text: ")" }); + parts.push({ text: " => " }); + visitForDisplayParts(functionType.type); + break; + case SyntaxKind.ImportType: + const importType = node as ImportTypeNode; + if (importType.isTypeOf) { + parts.push({ text: "typeof " }); + } + parts.push({ text: "import(" }); + visitForDisplayParts(importType.argument); + if (importType.assertions) { + parts.push({ text: ", { assert: " }); + visitDisplayPartList(importType.assertions.assertClause.elements, ", "); + parts.push({ text: " }" }); + } + parts.push({ text: ")" }); + if (importType.qualifier) { + parts.push({ text: "." }); + visitForDisplayParts(importType.qualifier); + } + if (importType.typeArguments) { + parts.push({ text: "<" }); + visitDisplayPartList(importType.typeArguments, ", "); + parts.push({ text: ">" }); + } + break; + case SyntaxKind.PropertySignature: + const propertySignature = node as PropertySignature; + if (propertySignature.modifiers) { + visitDisplayPartList(propertySignature.modifiers, " "); + } + visitForDisplayParts(propertySignature.name); + if (propertySignature.questionToken) { + parts.push({ text: "?" }); + } + if (propertySignature.type) { + parts.push({ text: ": " }); + visitForDisplayParts(propertySignature.type); + } + break; + default: + Debug.failBadSyntaxKind(node); + } + } + + function visitDisplayPartList(nodes: NodeArray, separator: string) { + nodes.forEach((node, index) => { + if (index > 0) { + parts.push({ text: separator }); + } + visitForDisplayParts(node); + }); + } + } + function isUndefined(name: __String) { return name === "undefined"; } diff --git a/tests/baselines/reference/inlayHintsFunctionParameterTypes1.baseline b/tests/baselines/reference/inlayHintsFunctionParameterTypes1.baseline index 31c95617ae2ce..af66775ae95dc 100644 --- a/tests/baselines/reference/inlayHintsFunctionParameterTypes1.baseline +++ b/tests/baselines/reference/inlayHintsFunctionParameterTypes1.baseline @@ -46,7 +46,7 @@ foo2((a) => { }) foo3(a => { ^ { - "text": ": (c: (d: 2 | 3) => void) => ...", + "text": ": (c: (d: 2 | 3) => void) => void", "position": 331, "kind": "Type", "whitespaceBefore": true diff --git a/tests/baselines/reference/inlayHintsInterativeAnyParameter1.baseline b/tests/baselines/reference/inlayHintsInteractiveAnyParameter1.baseline similarity index 76% rename from tests/baselines/reference/inlayHintsInterativeAnyParameter1.baseline rename to tests/baselines/reference/inlayHintsInteractiveAnyParameter1.baseline index 6d4d794d58e4a..82f85a385b924 100644 --- a/tests/baselines/reference/inlayHintsInterativeAnyParameter1.baseline +++ b/tests/baselines/reference/inlayHintsInteractiveAnyParameter1.baseline @@ -12,7 +12,7 @@ foo(1); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter1.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter1.ts" }, { "text": ":" @@ -34,7 +34,7 @@ foo(''); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter1.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter1.ts" }, { "text": ":" @@ -56,7 +56,7 @@ foo(true); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter1.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter1.ts" }, { "text": ":" @@ -78,7 +78,7 @@ foo((1)); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter1.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter1.ts" }, { "text": ":" @@ -100,7 +100,7 @@ foo(foo(1)); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter1.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter1.ts" }, { "text": ":" diff --git a/tests/baselines/reference/inlayHintsInterativeAnyParameter2.baseline b/tests/baselines/reference/inlayHintsInteractiveAnyParameter2.baseline similarity index 77% rename from tests/baselines/reference/inlayHintsInterativeAnyParameter2.baseline rename to tests/baselines/reference/inlayHintsInteractiveAnyParameter2.baseline index 510267abb4adb..9709ddac5c2b5 100644 --- a/tests/baselines/reference/inlayHintsInterativeAnyParameter2.baseline +++ b/tests/baselines/reference/inlayHintsInteractiveAnyParameter2.baseline @@ -12,7 +12,7 @@ foo(1); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -34,7 +34,7 @@ foo(''); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -56,7 +56,7 @@ foo(true); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -78,7 +78,7 @@ foo(() => 1); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -100,7 +100,7 @@ foo(function () { return 1 }); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -122,7 +122,7 @@ foo({}); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -144,7 +144,7 @@ foo({ a: 1 }); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -166,7 +166,7 @@ foo([]); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -188,7 +188,7 @@ foo([1]); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -210,7 +210,7 @@ foo(foo); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -232,7 +232,7 @@ foo((1)); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -254,7 +254,7 @@ foo(foo(1)); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" @@ -276,7 +276,7 @@ foo(foo(1)); "start": 14, "length": 1 }, - "file": "/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts" + "file": "/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts" }, { "text": ":" diff --git a/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes1.baseline b/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes1.baseline new file mode 100644 index 0000000000000..af66775ae95dc --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes1.baseline @@ -0,0 +1,80 @@ +const f1: F1 = (a, b) => { } + ^ +{ + "text": ": string", + "position": 58, + "kind": "Type", + "whitespaceBefore": true +} + +const f1: F1 = (a, b) => { } + ^ +{ + "text": ": number", + "position": 61, + "kind": "Type", + "whitespaceBefore": true +} + +const f2: F1 = (a, b: number) => { } + ^ +{ + "text": ": string", + "position": 87, + "kind": "Type", + "whitespaceBefore": true +} + +foo1((a) => { }) + ^ +{ + "text": ": string", + "position": 157, + "kind": "Type", + "whitespaceBefore": true +} + +foo2((a) => { }) + ^ +{ + "text": ": 2 | 3", + "position": 232, + "kind": "Type", + "whitespaceBefore": true +} + +foo3(a => { + ^ +{ + "text": ": (c: (d: 2 | 3) => void) => void", + "position": 331, + "kind": "Type", + "whitespaceBefore": true +} + + a(d => {}) + ^ +{ + "text": ": 2 | 3", + "position": 344, + "kind": "Type", + "whitespaceBefore": true +} + +foo4(1, a => { }) + ^ +{ + "text": ": number", + "position": 409, + "kind": "Type", + "whitespaceBefore": true +} + +const foo5: F2 = (a) => { } + ^ +{ + "text": ": { a: number; b: string; }", + "position": 492, + "kind": "Type", + "whitespaceBefore": true +} \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes2.baseline b/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes2.baseline new file mode 100644 index 0000000000000..6136dbb8025a6 --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes2.baseline @@ -0,0 +1 @@ +=== No inlay hints === \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes3.baseline b/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes3.baseline new file mode 100644 index 0000000000000..33923495c7bbd --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes3.baseline @@ -0,0 +1,17 @@ + bar: function (x?): void { + ^ +{ + "text": ": boolean", + "position": 87, + "kind": "Type", + "whitespaceBefore": true +} + + set foo(value) { this.#value = value; } + ^ +{ + "text": ": number", + "position": 250, + "kind": "Type", + "whitespaceBefore": true +} \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes4.baseline b/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes4.baseline new file mode 100644 index 0000000000000..6136dbb8025a6 --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveFunctionParameterTypes4.baseline @@ -0,0 +1 @@ +=== No inlay hints === \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveImportType1.baseline b/tests/baselines/reference/inlayHintsInteractiveImportType1.baseline new file mode 100644 index 0000000000000..6136dbb8025a6 --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveImportType1.baseline @@ -0,0 +1 @@ +=== No inlay hints === \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveImportType2.baseline b/tests/baselines/reference/inlayHintsInteractiveImportType2.baseline new file mode 100644 index 0000000000000..787ee663b8350 --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveImportType2.baseline @@ -0,0 +1,33 @@ +function bar () { return require('./a').a; } + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "1" + } + ], + "position": 58, + "kind": "Type", + "whitespaceBefore": true +} + +const d = bar() + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "1" + } + ], + "position": 111, + "kind": "Type", + "whitespaceBefore": true +} \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveJsDocParameterNames.baseline b/tests/baselines/reference/inlayHintsInteractiveJsDocParameterNames.baseline new file mode 100644 index 0000000000000..af633a5b9752d --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveJsDocParameterNames.baseline @@ -0,0 +1,43 @@ +y.foo(1, 2) + ^ +{ + "text": "", + "position": 88, + "kind": "Parameter", + "whitespaceAfter": true, + "displayParts": [ + { + "text": "a", + "span": { + "start": 40, + "length": 1 + }, + "file": "/a.js" + }, + { + "text": ":" + } + ] +} + +y.foo(1, 2) + ^ +{ + "text": "", + "position": 91, + "kind": "Parameter", + "whitespaceAfter": true, + "displayParts": [ + { + "text": "b", + "span": { + "start": 51, + "length": 1 + }, + "file": "/a.js" + }, + { + "text": ":" + } + ] +} \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveMultifile1.baseline b/tests/baselines/reference/inlayHintsInteractiveMultifile1.baseline new file mode 100644 index 0000000000000..809e9d6eda115 --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveMultifile1.baseline @@ -0,0 +1,128 @@ +async function foo () { + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "Promise", + "span": { + "start": -1, + "length": 7 + }, + "file": "lib.d.ts" + }, + { + "text": "<" + }, + { + "text": "Foo", + "span": { + "start": 17, + "length": 3 + }, + "file": "/a.ts" + }, + { + "text": ">" + } + ], + "position": 21, + "kind": "Type", + "whitespaceBefore": true +} + +function bar () { return import('./a') } + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "Promise", + "span": { + "start": -1, + "length": 7 + }, + "file": "lib.d.ts" + }, + { + "text": "<" + }, + { + "text": "typeof " + }, + { + "text": "import(" + }, + { + "text": "\"/a\"" + }, + { + "text": ")" + }, + { + "text": ">" + } + ], + "position": 83, + "kind": "Type", + "whitespaceBefore": true +} + +async function main () { + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "Promise", + "span": { + "start": -1, + "length": 7 + }, + "file": "lib.d.ts" + }, + { + "text": "<" + }, + { + "text": "void" + }, + { + "text": ">" + } + ], + "position": 131, + "kind": "Type", + "whitespaceBefore": true +} + + const a = await foo() + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "Foo", + "span": { + "start": 17, + "length": 3 + }, + "file": "/a.ts" + } + ], + "position": 145, + "kind": "Type", + "whitespaceBefore": true +} \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveReturnType.baseline b/tests/baselines/reference/inlayHintsInteractiveReturnType.baseline new file mode 100644 index 0000000000000..bf401880c3d24 --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveReturnType.baseline @@ -0,0 +1,84 @@ +function foo1 () { + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "number" + } + ], + "position": 16, + "kind": "Type", + "whitespaceBefore": true +} + + foo() { + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "number" + } + ], + "position": 95, + "kind": "Type", + "whitespaceBefore": true +} + +const a = () => 1 + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "number" + } + ], + "position": 135, + "kind": "Type", + "whitespaceBefore": true +} + +const b = function () { return 1 } + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "number" + } + ], + "position": 162, + "kind": "Type", + "whitespaceBefore": true +} + +const c = (b) => 1 + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "number" + } + ], + "position": 189, + "kind": "Type", + "whitespaceBefore": true +} \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveVariableTypes1.baseline b/tests/baselines/reference/inlayHintsInteractiveVariableTypes1.baseline new file mode 100644 index 0000000000000..18fe7f7b62db1 --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveVariableTypes1.baseline @@ -0,0 +1,25 @@ + const m = () => 123; + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "(" + }, + { + "text": ")" + }, + { + "text": " => " + }, + { + "text": "number" + } + ], + "position": 331, + "kind": "Type", + "whitespaceBefore": true +} \ No newline at end of file diff --git a/tests/baselines/reference/inlayHintsInteractiveVariableTypes2.baseline b/tests/baselines/reference/inlayHintsInteractiveVariableTypes2.baseline new file mode 100644 index 0000000000000..bc18a904cd289 --- /dev/null +++ b/tests/baselines/reference/inlayHintsInteractiveVariableTypes2.baseline @@ -0,0 +1,103 @@ +const array = [1, 2] + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "number" + }, + { + "text": "[]" + } + ], + "position": 45, + "kind": "Type", + "whitespaceBefore": true +} + +const a = object; + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "{" + }, + { + "text": " " + }, + { + "text": "foo" + }, + { + "text": ": " + }, + { + "text": "number" + }, + { + "text": "; " + }, + { + "text": "bar" + }, + { + "text": ": " + }, + { + "text": "number" + }, + { + "text": " " + }, + { + "text": "}" + } + ], + "position": 62, + "kind": "Type", + "whitespaceBefore": true +} + +const b = array; + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "number" + }, + { + "text": "[]" + } + ], + "position": 128, + "kind": "Type", + "whitespaceBefore": true +} + +const x = foo(1) + ^ +{ + "text": "", + "displayParts": [ + { + "text": ": " + }, + { + "text": "1" + } + ], + "position": 244, + "kind": "Type", + "whitespaceBefore": true +} \ No newline at end of file diff --git a/tests/cases/fourslash/inlayHintsInterativeAnyParameter1.ts b/tests/cases/fourslash/inlayHintsInteractiveAnyParameter1.ts similarity index 100% rename from tests/cases/fourslash/inlayHintsInterativeAnyParameter1.ts rename to tests/cases/fourslash/inlayHintsInteractiveAnyParameter1.ts diff --git a/tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts b/tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts similarity index 100% rename from tests/cases/fourslash/inlayHintsInterativeAnyParameter2.ts rename to tests/cases/fourslash/inlayHintsInteractiveAnyParameter2.ts diff --git a/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes1.ts b/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes1.ts new file mode 100644 index 0000000000000..79e2d31a50f92 --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes1.ts @@ -0,0 +1,30 @@ +/// + +//// type F1 = (a: string, b: number) => void +//// const f1: F1 = (a, b) => { } +//// const f2: F1 = (a, b: number) => { } + +//// function foo1 (cb: (a: string) => void) {} +//// foo1((a) => { }) + +//// function foo2 (cb: (a: Exclude<1 | 2 | 3, 1>) => void) {} +//// foo2((a) => { }) + +//// function foo3 (a: (b: (c: (d: Exclude<1 | 2 | 3, 1>) => void) => void) => void) {} +//// foo3(a => { +//// a(d => {}) +//// }) + +//// function foo4(v: T, a: (v: T) => void) {} +//// foo4(1, a => { }) + +//// type F2 = (a: { +//// a: number +//// b: string +//// }) => void +//// const foo5: F2 = (a) => { } + +verify.baselineInlayHints(undefined, { + includeInlayFunctionParameterTypeHints: true, + interactiveInlayHints: true, +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes2.ts b/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes2.ts new file mode 100644 index 0000000000000..9d49369685a1a --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes2.ts @@ -0,0 +1,26 @@ +/// + +////class C {} +////namespace N { export class Foo {} } +////interface Foo {} + +////function f1(a = 1) {} +////function f2(a = "a") {} +////function f3(a = true) {} + +////function f4(a = { } as Foo) {} +////function f5(a = {}) {} +////function f6(a = {} as const) {} +////function f7(a = (({} as const))) {} + +////function f8(a = new C()) {} +////function f9(a = new N.C()) {} +////function f10(a = ((((new C()))))) {} + +////function f11(a = { a: 1, b: 1 }) {} +////function f12(a = ((({ a: 1, b: 1 })))) {} + +verify.baselineInlayHints(undefined, { + includeInlayFunctionParameterTypeHints: true, + interactiveInlayHints: true +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes3.ts b/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes3.ts new file mode 100644 index 0000000000000..7010f180b06ad --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes3.ts @@ -0,0 +1,22 @@ +/// + +////interface IFoo { +//// bar(x?: boolean): void; +////} +//// +////const a: IFoo = { +//// bar: function (x?): void { +//// throw new Error("Function not implemented."); +//// } +////} + +////class Foo { +//// #value = 0; +//// get foo(): number { return this.#value; } +//// set foo(value) { this.#value = value; } +////} + +verify.baselineInlayHints(undefined, { + includeInlayFunctionParameterTypeHints: true, + interactiveInlayHints: true +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes4.ts b/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes4.ts new file mode 100644 index 0000000000000..ed137af592de1 --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveFunctionParameterTypes4.ts @@ -0,0 +1,18 @@ +/// + +// @allowJs: true +// @checkJs: true +// @Filename: /a.js +////class Foo { +//// #value = 0; +//// get foo() { return this.#value; } +//// /** +//// * @param {number} value +//// */ +//// set foo(value) { this.#value = value; } +////} + +verify.baselineInlayHints(undefined, { + includeInlayFunctionParameterTypeHints: true, + interactiveInlayHints: true +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveImportType1.ts b/tests/cases/fourslash/inlayHintsInteractiveImportType1.ts new file mode 100644 index 0000000000000..63c108e9f636b --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveImportType1.ts @@ -0,0 +1,16 @@ +/// + +// @allowJs: true +// @checkJs: true + +// @Filename: /a.js +//// module.exports.a = 1 + +// @Filename: /b.js +//// const a = require('./a'); + +goTo.file('/b.js') +verify.baselineInlayHints(undefined, { + includeInlayVariableTypeHints: true, + interactiveInlayHints: true +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveImportType2.ts b/tests/cases/fourslash/inlayHintsInteractiveImportType2.ts new file mode 100644 index 0000000000000..e56b6ea97065c --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveImportType2.ts @@ -0,0 +1,20 @@ +/// + +// @allowJs: true +// @checkJs: true + +// @Filename: /a.js +//// module.exports.a = 1 + +// @Filename: /b.js +//// function foo () { return require('./a'); } +//// function bar () { return require('./a').a; } +//// const c = foo() +//// const d = bar() + +goTo.file('/b.js') +verify.baselineInlayHints(undefined, { + includeInlayVariableTypeHints: true, + includeInlayFunctionLikeReturnTypeHints: true, + interactiveInlayHints: true +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveJsDocParameterNames.ts b/tests/cases/fourslash/inlayHintsInteractiveJsDocParameterNames.ts new file mode 100644 index 0000000000000..d97e51ebd9727 --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveJsDocParameterNames.ts @@ -0,0 +1,25 @@ +/// + +// @allowJs: true +// @checkJs: true + +// @Filename: /a.js +//// var x +//// x.foo(1, 2); + +//// /** +//// * @type {{foo: (a: number, b: number) => void}} +//// */ +//// var y +//// y.foo(1, 2) + +//// /** +//// * @type {string} +//// */ +//// var z = "" + +goTo.file('/a.js') +verify.baselineInlayHints(undefined, { + includeInlayParameterNameHints: "literals", + interactiveInlayHints: true +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveMultifile1.ts b/tests/cases/fourslash/inlayHintsInteractiveMultifile1.ts new file mode 100644 index 0000000000000..db990eebd1ffa --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveMultifile1.ts @@ -0,0 +1,21 @@ +/// + +// @Filename: /a.ts +//// export interface Foo { a: string } + +// @Filename: /b.ts +//// async function foo () { +//// return {} as any as import('./a').Foo +//// } +//// function bar () { return import('./a') } +//// async function main () { +//// const a = await foo() +//// const b = await bar() +//// } + +goTo.file('/b.ts') +verify.baselineInlayHints(undefined, { + includeInlayVariableTypeHints: true, + includeInlayFunctionLikeReturnTypeHints: true, + interactiveInlayHints: true +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveReturnType.ts b/tests/cases/fourslash/inlayHintsInteractiveReturnType.ts new file mode 100644 index 0000000000000..5bb31f39cb366 --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveReturnType.ts @@ -0,0 +1,27 @@ +/// + +//// function foo1 () { +//// return 1 +//// } + +//// function foo2 (): number { +//// return 1 +//// } + +//// class C { +//// foo() { +//// return 1 +//// } +//// } + +//// const a = () => 1 + +//// const b = function () { return 1 } + +//// const c = (b) => 1 +//// const d = b => 1 + +verify.baselineInlayHints(undefined, { + includeInlayFunctionLikeReturnTypeHints: true, + interactiveInlayHints: true, +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveVariableTypes1.ts b/tests/cases/fourslash/inlayHintsInteractiveVariableTypes1.ts new file mode 100644 index 0000000000000..50d60bd03ccfe --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveVariableTypes1.ts @@ -0,0 +1,29 @@ +/// + +////class C {} +////namespace N { export class Foo {} } +////interface Foo {} + +////const a = "a"; +////const b = 1; +////const c = true; + +////const d = {} as Foo; +////const e = {}; +////const f = {} as const; +////const g = (({} as const)); + +////const h = new C(); +////const i = new N.C(); +////const j = ((((new C())))); + +////const k = { a: 1, b: 1 }; +////const l = ((({ a: 1, b: 1 }))); + +//// const m = () => 123; +//// const n; + +verify.baselineInlayHints(undefined, { + includeInlayVariableTypeHints: true, + interactiveInlayHints: true +}); diff --git a/tests/cases/fourslash/inlayHintsInteractiveVariableTypes2.ts b/tests/cases/fourslash/inlayHintsInteractiveVariableTypes2.ts new file mode 100644 index 0000000000000..343c121b789ae --- /dev/null +++ b/tests/cases/fourslash/inlayHintsInteractiveVariableTypes2.ts @@ -0,0 +1,18 @@ +/// + +//// const object = { foo: 1, bar: 2 } +//// const array = [1, 2] +//// const a = object; +//// const { foo, bar } = object; +//// const {} = object; +//// const b = array; +//// const [ first, second ] = array; +//// const [] = array; + +//// declare function foo(t: T): T +//// const x = foo(1) + +verify.baselineInlayHints(undefined, { + includeInlayVariableTypeHints: true, + interactiveInlayHints: true +});