diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index b37a9be01c2a0..1e579e5cc0cf1 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -8,11 +8,7 @@ namespace ts.InlayHints { }; function shouldShowParameterNameHints(preferences: InlayHintsOptions) { - return preferences.includeInlayParameterNameHints === "literals" || preferences.includeInlayParameterNameHints === "all"; - } - - function shouldShowLiteralParameterNameHintsOnly(preferences: InlayHintsOptions) { - return preferences.includeInlayParameterNameHints === "literals"; + return !!preferences.includeInlayParameterNameHints && preferences.includeInlayParameterNameHints !== "none"; } export function provideInlayHints(context: InlayHintsContext): InlayHint[] { @@ -156,9 +152,9 @@ namespace ts.InlayHints { for (let i = 0; i < args.length; ++i) { const originalArg = args[i]; const arg = skipParentheses(originalArg); - if (shouldShowLiteralParameterNameHintsOnly(preferences) && !isHintableExpression(arg)) { - continue; - } + + if ((preferences.includeInlayParameterNameHints === "literals" && !isLiteralLike(arg)) || + (preferences.includeInlayParameterNameHints === "primitiveLiterals" && !isPrimitiveLiteral(arg))) continue; const identifierNameInfo = checker.getParameterIdentifierNameAtPosition(signature, i); if (identifierNameInfo) { @@ -202,28 +198,40 @@ namespace ts.InlayHints { return some(ranges, range => regex.test(sourceFileText.substring(range.pos, range.end))); } - function isHintableExpression(node: Node) { + function isLiteralLike(node: Node): boolean { 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.ArrayLiteralExpression: + case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.NullKeyword: + case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateExpression: return true; - case SyntaxKind.Identifier: { - const name = (node as Identifier).escapedText; - return isUndefined(name) || isInfinityOrNaNString(name); - } + case SyntaxKind.PrefixUnaryExpression: + return isLiteralLike(skipParentheses((node as PrefixUnaryExpression).operand)); + default: + return isPrimitiveLiteral(node); + } + } + + function isPrimitiveLiteral(node: Node): boolean { + switch (node.kind) { + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + return true; + case SyntaxKind.Identifier: + return isUndefined(node) || isInfinityOrNaNString((node as Identifier).escapedText); + case SyntaxKind.PrefixUnaryExpression: + const operand = skipParentheses((node as PrefixUnaryExpression).operand); + return !(operand.kind === SyntaxKind.NullKeyword || operand.kind === SyntaxKind.BigIntLiteral || isUndefined(operand)) && isPrimitiveLiteral(operand); + default: + return false; } - return isLiteralExpression(node); } function visitFunctionDeclarationLikeForReturnType(decl: FunctionDeclaration | ArrowFunction | FunctionExpression | MethodDeclaration | GetAccessorDeclaration) { @@ -320,8 +328,8 @@ namespace ts.InlayHints { }); } - function isUndefined(name: __String) { - return name === "undefined"; + function isUndefined(node: Node) { + return isIdentifier(node) && node.escapedText === "undefined"; } } } diff --git a/src/services/types.ts b/src/services/types.ts index 4ad9ddfa88946..9c3d89b90c942 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -579,7 +579,7 @@ namespace ts { } export interface InlayHintsOptions extends UserPreferences { - readonly includeInlayParameterNameHints?: "none" | "literals" | "all"; + readonly includeInlayParameterNameHints?: "none" | "literals" | "primitiveLiterals" | "all"; readonly includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean; readonly includeInlayFunctionParameterTypeHints?: boolean, readonly includeInlayVariableTypeHints?: boolean; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 14de9571dc63a..ce5b11b3b0926 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5766,7 +5766,7 @@ declare namespace ts { includeInsertTextCompletions?: boolean; } interface InlayHintsOptions extends UserPreferences { - readonly includeInlayParameterNameHints?: "none" | "literals" | "all"; + readonly includeInlayParameterNameHints?: "none" | "literals" | "primitiveLiterals" | "all"; readonly includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean; readonly includeInlayFunctionParameterTypeHints?: boolean; readonly includeInlayVariableTypeHints?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index a83b83826341f..9ce3f4d784541 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5766,7 +5766,7 @@ declare namespace ts { includeInsertTextCompletions?: boolean; } interface InlayHintsOptions extends UserPreferences { - readonly includeInlayParameterNameHints?: "none" | "literals" | "all"; + readonly includeInlayParameterNameHints?: "none" | "literals" | "primitiveLiterals" | "all"; readonly includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean; readonly includeInlayFunctionParameterTypeHints?: boolean; readonly includeInlayVariableTypeHints?: boolean; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 3a1ff85432604..5cea0480a1e8b 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -645,7 +645,7 @@ declare namespace FourSlashInterface { readonly importModuleSpecifierEnding?: "minimal" | "index" | "js"; } interface InlayHintsOptions extends UserPreferences { - readonly includeInlayParameterNameHints?: "none" | "literals" | "all"; + readonly includeInlayParameterNameHints?: "none" | "literals" | "primitiveLiterals" | "all"; readonly includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean; readonly includeInlayFunctionParameterTypeHints?: boolean; readonly includeInlayVariableTypeHints?: boolean; diff --git a/tests/cases/fourslash/inlayHintsShouldWork65.ts b/tests/cases/fourslash/inlayHintsShouldWork65.ts new file mode 100644 index 0000000000000..e0a57eb541281 --- /dev/null +++ b/tests/cases/fourslash/inlayHintsShouldWork65.ts @@ -0,0 +1,85 @@ +/// + +////function foo( +//// a: number, +//// b: number, +//// c: number, +//// d: number, +//// e: number, +//// f: number, +//// g: number, +//// h: number, +//// i: number +////) {} +//// +////foo( +//// /*a*/+"", +//// /*b*/+``, +//// /*c*/+{}, +//// /*d*/+[], +//// /*e*/+/a/, +//// /*f*/+Infinity, +//// /*g*/+NaN, +//// /*h*/+function() {}, +//// /*i*/+(() => {}), +////); + +const [a, b, c, d, e, f, g, h, i] = test.markers(); +verify.getInlayHints([ + { + text: "a:", + position: a.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "b:", + position: b.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "c:", + position: c.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "d:", + position: d.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "e:", + position: e.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "f:", + position: f.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "g:", + position: g.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "h:", + position: h.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "i:", + position: i.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + } +], undefined, { + includeInlayParameterNameHints: "literals" +}); diff --git a/tests/cases/fourslash/inlayHints_primitiveLiterals1.ts b/tests/cases/fourslash/inlayHints_primitiveLiterals1.ts new file mode 100644 index 0000000000000..9d0643a5c0adb --- /dev/null +++ b/tests/cases/fourslash/inlayHints_primitiveLiterals1.ts @@ -0,0 +1,86 @@ +/// + +////function foo( +//// a: string, +//// b: undefined, +//// c: null, +//// d: boolean, +//// e: boolean, +//// f: number, +//// g: number, +//// h: number, +//// i: bigint +////) { +////} +//// +////foo( +//// /*a*/"hello", +//// /*b*/undefined, +//// /*c*/null, +//// /*d*/true, +//// /*e*/false, +//// /*f*/Infinity, +//// /*g*/-Infinity, +//// /*h*/NaN, +//// /*i*/123n +////); + +const [a, b, c, d, e, f, g, h, i] = test.markers(); +verify.getInlayHints([ + { + text: "a:", + position: a.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "b:", + position: b.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "c:", + position: c.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "d:", + position: d.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "e:", + position: e.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "f:", + position: f.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "g:", + position: g.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "h:", + position: h.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + }, + { + text: "i:", + position: i.position, + kind: ts.InlayHintKind.Parameter, + whitespaceAfter: true + } +], undefined, { + includeInlayParameterNameHints: "primitiveLiterals" +}); diff --git a/tests/cases/fourslash/inlayHints_primitiveLiterals2.ts b/tests/cases/fourslash/inlayHints_primitiveLiterals2.ts new file mode 100644 index 0000000000000..88dc68e00528d --- /dev/null +++ b/tests/cases/fourslash/inlayHints_primitiveLiterals2.ts @@ -0,0 +1,25 @@ +/// + +////function foo( +//// a: number[], +//// b: { x: number; y: number; }, +//// c: () => void, +//// d: () => void, +//// e: RegExp, +//// f: string, +//// g: string +////) {} +//// +////foo( +//// /*a*/[1], +//// /*b*/{ x: 1, y: 1 }, +//// /*c*/() => {}, +//// /*d*/function () {}, +//// /*e*//foo/i, +//// /*f*/`` +//// /*g*/`${1} ${2} ${3}` +////); + +verify.getInlayHints([], undefined, { + includeInlayParameterNameHints: "primitiveLiterals" +});