Skip to content

Display JSDoc documentation in Quick Info #1018

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

Merged
merged 8 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/api/encoder/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ func getChildrenPropertyMask(node *ast.Node) uint8 {
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
case ast.KindJSDocTemplateTag:
n := node.AsJSDocTemplateTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Constraint != nil) << 1) | (boolToByte(n.TypeParameters() != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Constraint != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
case ast.KindJSDocReturnTag:
n := node.AsJSDocReturnTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
Expand Down
18 changes: 10 additions & 8 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ func (n *Node) Text() string {
return n.AsJsxNamespacedName().Namespace.Text() + ":" + n.AsJsxNamespacedName().name.Text()
case KindRegularExpressionLiteral:
return n.AsRegularExpressionLiteral().Text
case KindJSDocText:
return n.AsJSDocText().Text
}
panic(fmt.Sprintf("Unhandled case in Node.Text: %T", n.data))
}
Expand Down Expand Up @@ -429,6 +431,8 @@ func (n *Node) TypeParameterList() *NodeList {
return n.AsInterfaceDeclaration().TypeParameters
case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration:
return n.AsTypeAliasDeclaration().TypeParameters
case KindJSDocTemplateTag:
return n.AsJSDocTemplateTag().TypeParameters
default:
funcLike := n.FunctionLikeData()
if funcLike != nil {
Expand Down Expand Up @@ -9108,39 +9112,37 @@ func IsJSDocUnknownTag(node *Node) bool {
type JSDocTemplateTag struct {
JSDocTagBase
Constraint *Node
typeParameters *TypeParameterList
TypeParameters *TypeParameterList
}

func (f *NodeFactory) NewJSDocTemplateTag(tagName *IdentifierNode, constraint *Node, typeParameters *TypeParameterList, comment *NodeList) *Node {
data := &JSDocTemplateTag{}
data.TagName = tagName
data.Constraint = constraint
data.typeParameters = typeParameters
data.TypeParameters = typeParameters
data.Comment = comment
return f.newNode(KindJSDocTemplateTag, data)
}

func (f *NodeFactory) UpdateJSDocTemplateTag(node *JSDocTemplateTag, tagName *IdentifierNode, constraint *Node, typeParameters *TypeParameterList, comment *NodeList) *Node {
if tagName != node.TagName || constraint != node.Constraint || typeParameters != node.typeParameters || comment != node.Comment {
if tagName != node.TagName || constraint != node.Constraint || typeParameters != node.TypeParameters || comment != node.Comment {
return updateNode(f.NewJSDocTemplateTag(tagName, constraint, typeParameters, comment), node.AsNode(), f.hooks)
}
return node.AsNode()
}

func (node *JSDocTemplateTag) ForEachChild(v Visitor) bool {
return visit(v, node.TagName) || visit(v, node.Constraint) || visitNodeList(v, node.typeParameters) || visitNodeList(v, node.Comment)
return visit(v, node.TagName) || visit(v, node.Constraint) || visitNodeList(v, node.TypeParameters) || visitNodeList(v, node.Comment)
}

func (node *JSDocTemplateTag) VisitEachChild(v *NodeVisitor) *Node {
return v.Factory.UpdateJSDocTemplateTag(node, v.visitNode(node.TagName), v.visitNode(node.Constraint), v.visitNodes(node.typeParameters), v.visitNodes(node.Comment))
return v.Factory.UpdateJSDocTemplateTag(node, v.visitNode(node.TagName), v.visitNode(node.Constraint), v.visitNodes(node.TypeParameters), v.visitNodes(node.Comment))
}

func (node *JSDocTemplateTag) Clone(f NodeFactoryCoercible) *Node {
return cloneNode(f.AsNodeFactory().NewJSDocTemplateTag(node.TagName, node.Constraint, node.TypeParameters(), node.Comment), node.AsNode(), f.AsNodeFactory().hooks)
return cloneNode(f.AsNodeFactory().NewJSDocTemplateTag(node.TagName, node.Constraint, node.TypeParameters, node.Comment), node.AsNode(), f.AsNodeFactory().hooks)
}

func (node *JSDocTemplateTag) TypeParameters() *TypeParameterList { return node.typeParameters }

// JSDocPropertyTag
type JSDocPropertyTag struct {
JSDocTagBase
Expand Down
13 changes: 13 additions & 0 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -3303,3 +3303,16 @@ func IsTypeDeclarationName(name *Node) bool {
IsTypeDeclaration(name.Parent) &&
GetNameOfDeclaration(name.Parent) == name
}

func IsRightSideOfQualifiedNameOrPropertyAccess(node *Node) bool {
parent := node.Parent
switch parent.Kind {
case KindQualifiedName:
return parent.AsQualifiedName().Right == node
case KindPropertyAccessExpression:
return parent.AsPropertyAccessExpression().Name() == node
case KindMetaProperty:
return parent.AsMetaProperty().Name() == node
}
return false
}
4 changes: 2 additions & 2 deletions internal/binder/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1562,8 +1562,8 @@ func (b *Binder) bindChildren(node *ast.Node) {
// and set it before we descend into nodes that could actually be part of an assignment pattern.
b.inAssignmentPattern = false
if b.checkUnreachable(node) {
b.bindEachChild(node)
b.setJSDocParents(node)
b.bindEachChild(node)
b.inAssignmentPattern = saveInAssignmentPattern
return
}
Expand All @@ -1574,6 +1574,7 @@ func (b *Binder) bindChildren(node *ast.Node) {
hasFlowNodeData.FlowNode = b.currentFlow
}
}
b.setJSDocParents(node)
switch node.Kind {
case ast.KindWhileStatement:
b.bindWhileStatement(node)
Expand Down Expand Up @@ -1653,7 +1654,6 @@ func (b *Binder) bindChildren(node *ast.Node) {
default:
b.bindEachChild(node)
}
b.setJSDocParents(node)
b.inAssignmentPattern = saveInAssignmentPattern
}

Expand Down
10 changes: 5 additions & 5 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -13692,7 +13692,7 @@ func (c *Checker) getSymbolOfPartOfRightHandSideOfImportEquals(entityName *ast.N
// import a = |b|; // Namespace
// import a = |b.c|; // Value, type, namespace
// import a = |b.c|.d; // Namespace
if entityName.Kind == ast.KindIdentifier && isRightSideOfQualifiedNameOrPropertyAccess(entityName) {
if entityName.Kind == ast.KindIdentifier && ast.IsRightSideOfQualifiedNameOrPropertyAccess(entityName) {
entityName = entityName.Parent // QualifiedName
}
// Check for case 1 and 3 in the above example
Expand Down Expand Up @@ -15201,7 +15201,7 @@ func (c *Checker) GetTypeOfSymbolAtLocation(symbol *ast.Symbol, location *ast.No
// of the expression (which will reflect control flow analysis). If the expression indeed
// resolved to the given symbol, return the narrowed type.
if ast.IsIdentifier(location) || ast.IsPrivateIdentifier(location) {
if isRightSideOfQualifiedNameOrPropertyAccess(location) {
if ast.IsRightSideOfQualifiedNameOrPropertyAccess(location) {
location = location.Parent
}
if ast.IsExpressionNode(location) && (!ast.IsAssignmentTarget(location) || isWriteAccess(location)) {
Expand Down Expand Up @@ -21554,7 +21554,7 @@ func (c *Checker) getUnresolvedSymbolForEntityName(name *ast.Node) *ast.Symbol {
result = c.newSymbolEx(ast.SymbolFlagsTypeAlias, text, ast.CheckFlagsUnresolved)
c.unresolvedSymbols[path] = result
result.Parent = parentSymbol
c.declaredTypeLinks.Get(result).declaredType = c.unresolvedType
c.typeAliasLinks.Get(result).declaredType = c.unresolvedType
}
return result
}
Expand Down Expand Up @@ -29682,7 +29682,7 @@ func (c *Checker) getSymbolOfNameOrPropertyAccessExpression(name *ast.Node) *ast
}
}

for isRightSideOfQualifiedNameOrPropertyAccess(name) {
for ast.IsRightSideOfQualifiedNameOrPropertyAccess(name) {
name = name.Parent
}

Expand Down Expand Up @@ -29975,7 +29975,7 @@ func (c *Checker) getApplicableIndexSymbol(t *Type, keyType *Type) *ast.Symbol {
}

func (c *Checker) getRegularTypeOfExpression(expr *ast.Node) *Type {
if isRightSideOfQualifiedNameOrPropertyAccess(expr) {
if ast.IsRightSideOfQualifiedNameOrPropertyAccess(expr) {
expr = expr.Parent
}
return c.getRegularTypeOfLiteralType(c.getTypeOfExpression(expr))
Expand Down
2 changes: 1 addition & 1 deletion internal/checker/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func (c *Checker) SignatureToStringEx(signature *Signature, enclosingDeclaration
}

func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration *ast.Node, flags TypeFormatFlags) string {
isConstructor := signature.flags&SignatureFlagsConstruct != 0
isConstructor := signature.flags&SignatureFlagsConstruct != 0 && flags&TypeFormatFlagsWriteCallStyleSignature == 0
var sigOutput ast.Kind
if flags&TypeFormatFlagsWriteArrowStyleSignature != 0 {
if isConstructor {
Expand Down
3 changes: 2 additions & 1 deletion internal/checker/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const (
TypeFormatFlagsUseSingleQuotesForStringLiteralType TypeFormatFlags = 1 << 28 // Use single quotes for string literal type
TypeFormatFlagsNoTypeReduction TypeFormatFlags = 1 << 29 // Don't call getReducedType
TypeFormatFlagsOmitThisParameter TypeFormatFlags = 1 << 25
TypeFormatFlagsWriteCallStyleSignature TypeFormatFlags = 1 << 27 // Write construct signatures as call style signatures
// Error Handling
TypeFormatFlagsAllowUniqueESSymbolType TypeFormatFlags = 1 << 20 // This is bit 20 to align with the same bit in `NodeBuilderFlags`
// TypeFormatFlags exclusive
Expand Down Expand Up @@ -776,7 +777,7 @@ func (t *LiteralType) Value() any {
}

func (t *LiteralType) String() string {
return ValueToString(t)
return ValueToString(t.value)
}

// UniqueESSymbolTypeData
Expand Down
13 changes: 0 additions & 13 deletions internal/checker/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,19 +345,6 @@ func getExternalModuleRequireArgument(node *ast.Node) *ast.Node {
return nil
}

func isRightSideOfQualifiedNameOrPropertyAccess(node *ast.Node) bool {
parent := node.Parent
switch parent.Kind {
case ast.KindQualifiedName:
return parent.AsQualifiedName().Right == node
case ast.KindPropertyAccessExpression:
return parent.AsPropertyAccessExpression().Name() == node
case ast.KindMetaProperty:
return parent.AsMetaProperty().Name() == node
}
return false
}

func isRightSideOfAccessExpression(node *ast.Node) bool {
return node.Parent != nil && (ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Name() == node ||
ast.IsElementAccessExpression(node.Parent) && node.Parent.AsElementAccessExpression().ArgumentExpression == node)
Expand Down
Loading