Skip to content

Include Default Parameter Values in Signature Help #52819

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
16 changes: 14 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ import {
getJSDocDeprecatedTag,
getJSDocEnumTag,
getJSDocHost,
getJSDocInitializerParameter,
getJSDocParameterTags,
getJSDocRoot,
getJSDocSatisfiesExpressionType,
Expand Down Expand Up @@ -7344,6 +7345,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext, preserveModifierFlags?: boolean, privateSymbolVisitor?: (s: Symbol) => void, bundledImports?: boolean): ParameterDeclaration {
const includeParameterInitializers = !!(context.flags & NodeBuilderFlags.IncludeParameterInitializers);
let parameterDeclaration: ParameterDeclaration | JSDocParameterTag | undefined = getDeclarationOfKind<ParameterDeclaration>(parameterSymbol, SyntaxKind.Parameter);
if (!parameterDeclaration && !isTransientSymbol(parameterSymbol)) {
parameterDeclaration = getDeclarationOfKind<JSDocParameterTag>(parameterSymbol, SyntaxKind.JSDocParameterTag);
Expand All @@ -7355,6 +7357,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
const parameterTypeNode = serializeTypeForDeclaration(context, parameterType, parameterSymbol, context.enclosingDeclaration, privateSymbolVisitor, bundledImports);

let initializer: Expression | undefined;
if(includeParameterInitializers) {
if (tryCast(parameterDeclaration, isParameter)) {
initializer = parameterDeclaration?.initializer;
}
if (parameterDeclaration) {
initializer = getJSDocInitializerParameter(parameterDeclaration) ?? initializer;
}
}

const modifiers = !(context.flags & NodeBuilderFlags.OmitParameterModifiers) && preserveModifierFlags && parameterDeclaration && canHaveModifiers(parameterDeclaration) ? map(getModifiers(parameterDeclaration), factory.cloneNode) : undefined;
const isRest = parameterDeclaration && isRestParameter(parameterDeclaration) || getCheckFlags(parameterSymbol) & CheckFlags.RestParameter;
const dotDotDotToken = isRest ? factory.createToken(SyntaxKind.DotDotDotToken) : undefined;
Expand All @@ -7372,7 +7384,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
name,
questionToken,
parameterTypeNode,
/*initializer*/ undefined);
initializer);
context.approximateLength += symbolName(parameterSymbol).length + 3;
return parameterNode;

Expand All @@ -7389,7 +7401,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
visited.dotDotDotToken,
visited.propertyName,
visited.name,
/*initializer*/ undefined);
includeParameterInitializers ? visited.initializer : undefined);
}
if (!nodeIsSynthesized(visited)) {
visited = factory.cloneNode(visited);
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5127,12 +5127,13 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
}

// @api
function createJSDocParameterTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>): JSDocParameterTag {
function createJSDocParameterTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>, initializer?: Expression): JSDocParameterTag {
const node = createBaseJSDocTagDeclaration<JSDocParameterTag>(SyntaxKind.JSDocParameterTag, tagName ?? createIdentifier("param"), comment);
node.typeExpression = typeExpression;
node.name = name;
node.isNameFirst = !!isNameFirst;
node.isBracketed = isBracketed;
node.initializer = initializer;
return node;
}

Expand Down
11 changes: 6 additions & 5 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8973,7 +8973,7 @@ namespace Parser {
return token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined;
}

function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } {
function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean, initializer: ts.Expression | undefined } {
// Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar'
const isBracketed = parseOptionalJsdoc(SyntaxKind.OpenBracketToken);
if (isBracketed) {
Expand All @@ -8985,17 +8985,18 @@ namespace Parser {
if (isBackquoted) {
parseExpectedTokenJSDoc(SyntaxKind.BacktickToken);
}
let initializer: ts.Expression | undefined;
if (isBracketed) {
skipWhitespace();
// May have an optional default, e.g. '[foo = 42]'
if (parseOptionalToken(SyntaxKind.EqualsToken)) {
parseExpression();
initializer = parseExpression();
}

parseExpected(SyntaxKind.CloseBracketToken);
}

return { name, isBracketed };
return { name, isBracketed, initializer };
}

function isObjectOrObjectArrayTypeReference(node: TypeNode): boolean {
Expand All @@ -9014,7 +9015,7 @@ namespace Parser {
let isNameFirst = !typeExpression;
skipWhitespaceOrAsterisk();

const { name, isBracketed } = parseBracketNameInPropertyAndParamTag();
const { name, isBracketed, initializer } = parseBracketNameInPropertyAndParamTag();
const indentText = skipWhitespaceOrAsterisk();

if (isNameFirst && !lookAhead(parseJSDocLinkPrefix)) {
Expand All @@ -9030,7 +9031,7 @@ namespace Parser {
}
const result = target === PropertyLikeParse.Property
? factory.createJSDocPropertyTag(tagName, name, isBracketed, typeExpression, isNameFirst, comment)
: factory.createJSDocParameterTag(tagName, name, isBracketed, typeExpression, isNameFirst, comment);
: factory.createJSDocParameterTag(tagName, name, isBracketed, typeExpression, isNameFirst, comment, initializer);
return finishNode(result, start);
}

Expand Down
4 changes: 3 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4071,6 +4071,7 @@ export interface JSDocPropertyTag extends JSDocPropertyLikeTag {

export interface JSDocParameterTag extends JSDocPropertyLikeTag {
readonly kind: SyntaxKind.JSDocParameterTag;
readonly initializer?: Expression
}

export interface JSDocTypeLiteral extends JSDocType, Declaration {
Expand Down Expand Up @@ -5286,6 +5287,7 @@ export const enum NodeBuilderFlags {
UseSingleQuotesForStringLiteralType = 1 << 28, // Use single quotes for string literal type
NoTypeReduction = 1 << 29, // Don't call getReducedType
OmitThisParameter = 1 << 25,
IncludeParameterInitializers = 1 << 31, // Wether to include initializers into function signature, which is used to display signature help

// Error handling
AllowThisInObjectLiteral = 1 << 15,
Expand Down Expand Up @@ -8565,7 +8567,7 @@ export interface NodeFactory {
updateJSDocTemplateTag(node: JSDocTemplateTag, tagName: Identifier | undefined, constraint: JSDocTypeExpression | undefined, typeParameters: readonly TypeParameterDeclaration[], comment: string | NodeArray<JSDocComment> | undefined): JSDocTemplateTag;
createJSDocTypedefTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression | JSDocTypeLiteral, fullName?: Identifier | JSDocNamespaceDeclaration, comment?: string | NodeArray<JSDocComment>): JSDocTypedefTag;
updateJSDocTypedefTag(node: JSDocTypedefTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | JSDocTypeLiteral | undefined, fullName: Identifier | JSDocNamespaceDeclaration | undefined, comment: string | NodeArray<JSDocComment> | undefined): JSDocTypedefTag;
createJSDocParameterTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>): JSDocParameterTag;
createJSDocParameterTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>, initializer?: Expression): JSDocParameterTag;
updateJSDocParameterTag(node: JSDocParameterTag, tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression: JSDocTypeExpression | undefined, isNameFirst: boolean, comment: string | NodeArray<JSDocComment> | undefined): JSDocParameterTag;
createJSDocPropertyTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>): JSDocPropertyTag;
updateJSDocPropertyTag(node: JSDocPropertyTag, tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression: JSDocTypeExpression | undefined, isNameFirst: boolean, comment: string | NodeArray<JSDocComment> | undefined): JSDocPropertyTag;
Expand Down
10 changes: 10 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9760,6 +9760,16 @@ export function isJSDocOptionalParameter(node: ParameterDeclaration) {
isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType));
}

/** @internal */
export function getJSDocInitializerParameter(node: ParameterDeclaration | JSDocParameterTag) {
if (isJSDocParameterTag(node)) {
return node.initializer;
}

const bracketed = getJSDocParameterTags(node).find(({ isBracketed }) => isBracketed);
return bracketed?.initializer;
}

/** @internal */
export function isOptionalDeclaration(declaration: Declaration): boolean {
switch (declaration.kind) {
Expand Down
2 changes: 1 addition & 1 deletion src/services/signatureHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ function getEnclosingDeclarationFromInvocation(invocation: Invocation): Node {
return invocation.kind === InvocationKind.Call ? invocation.node : invocation.kind === InvocationKind.TypeArgs ? invocation.called : invocation.node;
}

const signatureHelpNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
const signatureHelpNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope | NodeBuilderFlags.IncludeParameterInitializers;
function createSignatureHelpItems(
candidates: readonly Signature[],
resolvedSignature: Signature,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,16 @@
"escapedText": "name1"
},
"isNameFirst": false,
"isBracketed": true
"isBracketed": true,
"initializer": {
"kind": "NumericLiteral",
"pos": 33,
"end": 35,
"modifierFlagsCache": 0,
"transformFlags": 0,
"text": "1",
"numericLiteralFlags": 0
}
},
"length": 1,
"pos": 8,
Expand Down
4 changes: 3 additions & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5964,6 +5964,7 @@ declare namespace ts {
}
interface JSDocParameterTag extends JSDocPropertyLikeTag {
readonly kind: SyntaxKind.JSDocParameterTag;
readonly initializer?: Expression;
}
interface JSDocTypeLiteral extends JSDocType, Declaration {
readonly kind: SyntaxKind.JSDocTypeLiteral;
Expand Down Expand Up @@ -6444,6 +6445,7 @@ declare namespace ts {
UseSingleQuotesForStringLiteralType = 268435456,
NoTypeReduction = 536870912,
OmitThisParameter = 33554432,
IncludeParameterInitializers = -2147483648,
AllowThisInObjectLiteral = 32768,
AllowQualifiedNameInPlaceOfIdentifier = 65536,
AllowAnonymousIdentifier = 131072,
Expand Down Expand Up @@ -7799,7 +7801,7 @@ declare namespace ts {
updateJSDocTemplateTag(node: JSDocTemplateTag, tagName: Identifier | undefined, constraint: JSDocTypeExpression | undefined, typeParameters: readonly TypeParameterDeclaration[], comment: string | NodeArray<JSDocComment> | undefined): JSDocTemplateTag;
createJSDocTypedefTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression | JSDocTypeLiteral, fullName?: Identifier | JSDocNamespaceDeclaration, comment?: string | NodeArray<JSDocComment>): JSDocTypedefTag;
updateJSDocTypedefTag(node: JSDocTypedefTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | JSDocTypeLiteral | undefined, fullName: Identifier | JSDocNamespaceDeclaration | undefined, comment: string | NodeArray<JSDocComment> | undefined): JSDocTypedefTag;
createJSDocParameterTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>): JSDocParameterTag;
createJSDocParameterTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>, initializer?: Expression): JSDocParameterTag;
updateJSDocParameterTag(node: JSDocParameterTag, tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression: JSDocTypeExpression | undefined, isNameFirst: boolean, comment: string | NodeArray<JSDocComment> | undefined): JSDocParameterTag;
createJSDocPropertyTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>): JSDocPropertyTag;
updateJSDocPropertyTag(node: JSDocPropertyTag, tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression: JSDocTypeExpression | undefined, isNameFirst: boolean, comment: string | NodeArray<JSDocComment> | undefined): JSDocPropertyTag;
Expand Down
4 changes: 3 additions & 1 deletion tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,7 @@ declare namespace ts {
}
interface JSDocParameterTag extends JSDocPropertyLikeTag {
readonly kind: SyntaxKind.JSDocParameterTag;
readonly initializer?: Expression;
}
interface JSDocTypeLiteral extends JSDocType, Declaration {
readonly kind: SyntaxKind.JSDocTypeLiteral;
Expand Down Expand Up @@ -2469,6 +2470,7 @@ declare namespace ts {
UseSingleQuotesForStringLiteralType = 268435456,
NoTypeReduction = 536870912,
OmitThisParameter = 33554432,
IncludeParameterInitializers = -2147483648,
AllowThisInObjectLiteral = 32768,
AllowQualifiedNameInPlaceOfIdentifier = 65536,
AllowAnonymousIdentifier = 131072,
Expand Down Expand Up @@ -3824,7 +3826,7 @@ declare namespace ts {
updateJSDocTemplateTag(node: JSDocTemplateTag, tagName: Identifier | undefined, constraint: JSDocTypeExpression | undefined, typeParameters: readonly TypeParameterDeclaration[], comment: string | NodeArray<JSDocComment> | undefined): JSDocTemplateTag;
createJSDocTypedefTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression | JSDocTypeLiteral, fullName?: Identifier | JSDocNamespaceDeclaration, comment?: string | NodeArray<JSDocComment>): JSDocTypedefTag;
updateJSDocTypedefTag(node: JSDocTypedefTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | JSDocTypeLiteral | undefined, fullName: Identifier | JSDocNamespaceDeclaration | undefined, comment: string | NodeArray<JSDocComment> | undefined): JSDocTypedefTag;
createJSDocParameterTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>): JSDocParameterTag;
createJSDocParameterTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>, initializer?: Expression): JSDocParameterTag;
updateJSDocParameterTag(node: JSDocParameterTag, tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression: JSDocTypeExpression | undefined, isNameFirst: boolean, comment: string | NodeArray<JSDocComment> | undefined): JSDocParameterTag;
createJSDocPropertyTag(tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, isNameFirst?: boolean, comment?: string | NodeArray<JSDocComment>): JSDocPropertyTag;
updateJSDocPropertyTag(node: JSDocPropertyTag, tagName: Identifier | undefined, name: EntityName, isBracketed: boolean, typeExpression: JSDocTypeExpression | undefined, isNameFirst: boolean, comment: string | NodeArray<JSDocComment> | undefined): JSDocPropertyTag;
Expand Down
Loading