vendor tsgo
This commit is contained in:
597
tools/tsgo/internal/checker/nodebuilder_hover.go
Normal file
597
tools/tsgo/internal/checker/nodebuilder_hover.go
Normal file
@@ -0,0 +1,597 @@
|
||||
package checker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/collections"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/jsnum"
|
||||
"github.com/microsoft/typescript-go/internal/nodebuilder"
|
||||
"github.com/microsoft/typescript-go/internal/scanner"
|
||||
)
|
||||
|
||||
// isExpanding returns whether the node builder context is operating in hover-expansion mode.
|
||||
func isExpanding(ctx *NodeBuilderContext) bool {
|
||||
return ctx.maxExpansionDepth != -1
|
||||
}
|
||||
|
||||
// expandSymbolForHover produces declaration nodes (class, interface, enum, module) for a symbol
|
||||
// for expandable hover. This is a focused alternative to the full symbolTableToDeclarationStatements
|
||||
// machinery used by declaration emit — it directly builds the declaration nodes hover needs
|
||||
// without the declaration-emit scaffolding (deferred privates, symbol name remapping, export
|
||||
// modifier computation, alias resolution, visited symbols tracking).
|
||||
func (b *NodeBuilderImpl) expandSymbolForHover(symbol *ast.Symbol) []*ast.Node {
|
||||
var results []*ast.Node
|
||||
if symbol.Flags&ast.SymbolFlagsEnum != 0 {
|
||||
if node := b.expandEnumDecl(symbol); node != nil {
|
||||
results = append(results, node)
|
||||
}
|
||||
}
|
||||
if symbol.Flags&ast.SymbolFlagsClass != 0 {
|
||||
if node := b.expandClassDecl(symbol); node != nil {
|
||||
results = append(results, node)
|
||||
}
|
||||
}
|
||||
// Module/namespace before interface (matching Strada ordering for merged declarations)
|
||||
if symbol.Flags&(ast.SymbolFlagsValueModule|ast.SymbolFlagsNamespaceModule) != 0 {
|
||||
if node := b.expandModuleDecl(symbol); node != nil {
|
||||
results = append(results, node)
|
||||
}
|
||||
}
|
||||
if symbol.Flags&ast.SymbolFlagsInterface != 0 && symbol.Flags&ast.SymbolFlagsClass == 0 {
|
||||
if node := b.expandInterfaceDecl(symbol); node != nil {
|
||||
results = append(results, node)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// expandEnumDecl produces an EnumDeclaration node with all members.
|
||||
func (b *NodeBuilderImpl) expandEnumDecl(symbol *ast.Symbol) *ast.Node {
|
||||
name := ast.SymbolName(symbol)
|
||||
b.ctx.approximateLength += 9 + len(name)
|
||||
memberProps := core.Filter(b.ch.getPropertiesOfType(b.ch.getTypeOfSymbol(symbol)), func(p *ast.Symbol) bool {
|
||||
return p.Flags&ast.SymbolFlagsEnumMember != 0
|
||||
})
|
||||
var members []*ast.Node
|
||||
for i, p := range memberProps {
|
||||
if b.checkTruncationLengthIfExpanding() && i+3 < len(memberProps)-1 {
|
||||
b.ctx.expansionTruncated = true
|
||||
members = append(members, b.f.NewEnumMember(b.f.NewStringLiteral(fmt.Sprintf(" ... %d more ... ", len(memberProps)-i-1), 0), nil))
|
||||
last := memberProps[len(memberProps)-1]
|
||||
members = append(members, b.f.NewEnumMember(b.f.NewIdentifier(last.Name), b.enumMemberInitializer(last)))
|
||||
break
|
||||
}
|
||||
memberDecl := core.Find(p.Declarations, ast.IsEnumMember)
|
||||
var initializer *ast.Node
|
||||
if memberDecl != nil && memberDecl.AsEnumMember().Initializer != nil {
|
||||
initializer = b.f.DeepCloneNode(memberDecl.AsEnumMember().Initializer)
|
||||
} else {
|
||||
initializer = b.enumMemberInitializer(p)
|
||||
}
|
||||
b.ctx.approximateLength += 4 + len(p.Name)
|
||||
if initializer != nil {
|
||||
b.ctx.approximateLength += 5 // " = " + value estimate
|
||||
}
|
||||
members = append(members, b.f.NewEnumMember(b.f.NewIdentifier(p.Name), initializer))
|
||||
}
|
||||
|
||||
constModifier := ast.ModifierFlagsNone
|
||||
if isConstEnumSymbol(symbol) {
|
||||
constModifier = ast.ModifierFlagsConst
|
||||
}
|
||||
var mods *ast.ModifierList
|
||||
if constModifier != 0 {
|
||||
mods = b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(constModifier, b.f.NewModifier))
|
||||
}
|
||||
return b.f.NewEnumDeclaration(mods, b.f.NewIdentifier(name), b.f.NewNodeList(members))
|
||||
}
|
||||
|
||||
func (b *NodeBuilderImpl) enumMemberInitializer(p *ast.Symbol) *ast.Node {
|
||||
memberDecl := core.Find(p.Declarations, ast.IsEnumMember)
|
||||
if memberDecl == nil {
|
||||
return nil
|
||||
}
|
||||
val := b.ch.GetConstantValue(memberDecl)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
return b.f.NewStringLiteral(v, 0)
|
||||
case jsnum.Number:
|
||||
return b.f.NewNumericLiteral(v.String(), 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// expandClassDecl produces a ClassDeclaration node with heritage clauses and members.
|
||||
func (b *NodeBuilderImpl) expandClassDecl(symbol *ast.Symbol) *ast.Node {
|
||||
name := ast.SymbolName(symbol)
|
||||
b.ctx.approximateLength += 9 + len(name)
|
||||
|
||||
classLikeDeclarations := core.Filter(symbol.Declarations, ast.IsClassLike)
|
||||
originalDecl := core.FirstOrNil(classLikeDeclarations)
|
||||
oldEnclosing := b.ctx.enclosingDeclaration
|
||||
if originalDecl != nil {
|
||||
b.ctx.enclosingDeclaration = originalDecl
|
||||
}
|
||||
defer func() { b.ctx.enclosingDeclaration = oldEnclosing }()
|
||||
|
||||
localParams := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
|
||||
typeParamDecls := core.Map(localParams, func(p *Type) *ast.Node { return b.typeParameterToDeclaration(p) })
|
||||
|
||||
declaredType := b.ch.getDeclaredTypeOfClassOrInterface(symbol)
|
||||
classType := b.ch.getTypeWithThisArgument(declaredType, nil, false)
|
||||
baseTypes := b.ch.getBaseTypes(b.ch.getTargetType(classType))
|
||||
staticType := b.ch.getTypeOfSymbol(symbol)
|
||||
isClass := staticType.symbol != nil && staticType.symbol.ValueDeclaration != nil && ast.IsClassLike(staticType.symbol.ValueDeclaration)
|
||||
var staticBaseType *Type
|
||||
if isClass {
|
||||
staticBaseType = b.ch.getBaseConstructorTypeOfClass(declaredType)
|
||||
} else {
|
||||
staticBaseType = b.ch.anyType
|
||||
}
|
||||
|
||||
// Heritage clauses
|
||||
heritageClauses := b.hoverHeritageClauses(classLikeDeclarations)
|
||||
|
||||
// Instance members via addPropertyToElementList (reusing existing serialization),
|
||||
// then convert TypeElements to ClassElements and add class-specific modifiers
|
||||
allProps := b.ch.getPropertiesOfType(classType)
|
||||
symbolProps := b.filterInheritedProperties(classType, baseTypes, allProps)
|
||||
publicProps := core.Filter(symbolProps, func(s *ast.Symbol) bool { return !isHashPrivate(s) })
|
||||
hasPrivate := core.Some(symbolProps, isHashPrivate)
|
||||
|
||||
var instanceMembers []*ast.Node
|
||||
instanceMembers = b.serializePropertiesWithTruncation(publicProps, instanceMembers)
|
||||
instanceMembers = typeElementsToClassElements(b.f, instanceMembers)
|
||||
instanceMembers = b.addClassModifiers(instanceMembers, false)
|
||||
|
||||
// Static members
|
||||
staticProps := core.Filter(b.ch.getPropertiesOfType(staticType), func(p *ast.Symbol) bool {
|
||||
return p.Flags&ast.SymbolFlagsPrototype == 0 && p.Name != "prototype" && !b.isNamespaceMember(p)
|
||||
})
|
||||
var staticMembers []*ast.Node
|
||||
staticMembers = b.serializePropertiesWithTruncation(staticProps, staticMembers)
|
||||
staticMembers = typeElementsToClassElements(b.f, staticMembers)
|
||||
staticMembers = b.addClassModifiers(staticMembers, true)
|
||||
|
||||
// Hash-private members
|
||||
var privateMembers []*ast.Node
|
||||
if hasPrivate {
|
||||
privateMembers = b.serializePropertiesWithTruncation(core.Filter(symbolProps, isHashPrivate), privateMembers)
|
||||
privateMembers = typeElementsToClassElements(b.f, privateMembers)
|
||||
}
|
||||
|
||||
// Constructors
|
||||
constructors := b.serializeConstructors(staticType, staticBaseType, isClass, symbol)
|
||||
|
||||
// Index signatures
|
||||
indexSigs := b.serializeIndexSignaturesOfType(classType, core.FirstOrNil(baseTypes))
|
||||
|
||||
allMembers := make([]*ast.Node, 0, len(indexSigs)+len(staticMembers)+len(constructors)+len(instanceMembers)+len(privateMembers))
|
||||
allMembers = append(allMembers, indexSigs...)
|
||||
allMembers = append(allMembers, staticMembers...)
|
||||
allMembers = append(allMembers, constructors...)
|
||||
allMembers = append(allMembers, instanceMembers...)
|
||||
allMembers = append(allMembers, privateMembers...)
|
||||
|
||||
return b.f.NewClassDeclaration(nil, b.f.NewIdentifier(name), b.f.NewNodeList(typeParamDecls), b.f.NewNodeList(heritageClauses), b.f.NewNodeList(allMembers))
|
||||
}
|
||||
|
||||
// addClassModifiers post-processes class member nodes to add class-specific modifiers
|
||||
// (private, protected, public, abstract, static) based on the original symbol declarations.
|
||||
func (b *NodeBuilderImpl) addClassModifiers(members []*ast.Node, isStatic bool) []*ast.Node {
|
||||
for i, m := range members {
|
||||
// Find the symbol for this member by matching the property name
|
||||
var memberSymbol *ast.Symbol
|
||||
memberName := m.Name()
|
||||
if memberName != nil {
|
||||
if sym, ok := b.idToSymbol[memberName]; ok {
|
||||
memberSymbol = sym
|
||||
}
|
||||
}
|
||||
if memberSymbol == nil {
|
||||
continue
|
||||
}
|
||||
modFlags := getDeclarationModifierFlagsFromSymbol(memberSymbol) &^ ast.ModifierFlagsAsync
|
||||
if isStatic {
|
||||
modFlags |= ast.ModifierFlagsStatic
|
||||
}
|
||||
if modFlags != 0 && ast.CanHaveModifiers(m) {
|
||||
existing := m.ModifierFlags()
|
||||
if modFlags != existing {
|
||||
members[i] = ast.ReplaceModifiers(b.f, m, b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(modFlags|existing, b.f.NewModifier)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return members
|
||||
}
|
||||
|
||||
// typeElementsToClassElements converts TypeElement nodes (PropertySignature, MethodSignature)
|
||||
// to their ClassElement equivalents (PropertyDeclaration, MethodDeclaration) so they can be
|
||||
// used as members of a ClassDeclaration. Nodes that are already ClassElements pass through unchanged.
|
||||
func typeElementsToClassElements(f *ast.NodeFactory, members []*ast.Node) []*ast.Node {
|
||||
for i, m := range members {
|
||||
switch m.Kind {
|
||||
case ast.KindPropertySignature:
|
||||
ps := m.AsPropertySignatureDeclaration()
|
||||
members[i] = f.NewPropertyDeclaration(m.Modifiers(), ps.Name(), ps.QuestionToken(), ps.Type, nil)
|
||||
case ast.KindMethodSignature:
|
||||
ms := m.AsMethodSignatureDeclaration()
|
||||
members[i] = f.NewMethodDeclaration(m.Modifiers(), nil, ms.Name(), ms.QuestionToken(), ms.TypeParameters, ms.Parameters, ms.Type, nil, nil)
|
||||
}
|
||||
}
|
||||
return members
|
||||
}
|
||||
|
||||
// expandInterfaceDecl produces an InterfaceDeclaration with members.
|
||||
// Reuses addPropertyToElementList for property serialization and
|
||||
// signatureToSignatureDeclarationHelper for signatures.
|
||||
func (b *NodeBuilderImpl) expandInterfaceDecl(symbol *ast.Symbol) *ast.Node {
|
||||
name := ast.SymbolName(symbol)
|
||||
b.ctx.approximateLength += 14 + len(name)
|
||||
|
||||
interfaceType := b.ch.getDeclaredTypeOfClassOrInterface(symbol)
|
||||
interfaceDeclarations := core.Filter(symbol.Declarations, ast.IsInterfaceDeclaration)
|
||||
localParams := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
|
||||
typeParamDecls := core.Map(localParams, func(p *Type) *ast.Node { return b.typeParameterToDeclaration(p) })
|
||||
baseTypes := b.ch.getBaseTypes(interfaceType)
|
||||
var baseType *Type
|
||||
if len(baseTypes) > 0 {
|
||||
baseType = b.ch.getIntersectionType(baseTypes)
|
||||
}
|
||||
|
||||
// Members: reuse existing serialization functions
|
||||
resolved := b.ch.resolveStructuredTypeMembers(interfaceType)
|
||||
var members []*ast.Node
|
||||
|
||||
// Index signatures, filtering those identical to base
|
||||
members = append(members, b.serializeIndexSignaturesOfType(interfaceType, baseType)...)
|
||||
// Construct signatures (skip abstract)
|
||||
for _, sig := range resolved.ConstructSignatures() {
|
||||
if sig.flags&SignatureFlagsAbstract != 0 {
|
||||
continue
|
||||
}
|
||||
members = append(members, b.signatureToSignatureDeclarationHelper(sig, ast.KindConstructSignature, nil))
|
||||
}
|
||||
// Call signatures
|
||||
for _, sig := range resolved.CallSignatures() {
|
||||
members = append(members, b.signatureToSignatureDeclarationHelper(sig, ast.KindCallSignature, nil))
|
||||
}
|
||||
// Properties, filtering inherited
|
||||
filteredProps := b.filterInheritedProperties(interfaceType, baseTypes, resolved.properties)
|
||||
members = b.serializePropertiesWithTruncation(filteredProps, members)
|
||||
|
||||
// Heritage clauses
|
||||
heritageClauses := b.hoverHeritageClauses(interfaceDeclarations)
|
||||
|
||||
return b.f.NewInterfaceDeclaration(nil, b.f.NewIdentifier(name), b.f.NewNodeList(typeParamDecls), b.f.NewNodeList(heritageClauses), b.f.NewNodeList(members))
|
||||
}
|
||||
|
||||
func (b *NodeBuilderImpl) hoverHeritageClauses(declarations []*ast.Node) []*ast.Node {
|
||||
var extendsTypes []*ast.Node
|
||||
var implementsTypes []*ast.Node
|
||||
for _, declaration := range declarations {
|
||||
for _, heritageElement := range ast.GetExtendsHeritageClauseElements(declaration) {
|
||||
extendsTypes = append(extendsTypes, b.f.DeepCloneNode(heritageElement.AsNode()))
|
||||
}
|
||||
for _, heritageElement := range ast.GetImplementsHeritageClauseElements(declaration) {
|
||||
implementsTypes = append(implementsTypes, b.f.DeepCloneNode(heritageElement.AsNode()))
|
||||
}
|
||||
}
|
||||
|
||||
var heritageClauses []*ast.Node
|
||||
if len(extendsTypes) > 0 {
|
||||
heritageClauses = append(heritageClauses, b.f.NewHeritageClause(ast.KindExtendsKeyword, b.f.NewNodeList(extendsTypes)))
|
||||
}
|
||||
if len(implementsTypes) > 0 {
|
||||
heritageClauses = append(heritageClauses, b.f.NewHeritageClause(ast.KindImplementsKeyword, b.f.NewNodeList(implementsTypes)))
|
||||
}
|
||||
return heritageClauses
|
||||
}
|
||||
|
||||
// serializePropertiesWithTruncation iterates properties using addPropertyToElementList,
|
||||
// with truncation checks matching Strada's createTypeNodesFromResolvedType behavior.
|
||||
func (b *NodeBuilderImpl) serializePropertiesWithTruncation(properties []*ast.Symbol, elements []*ast.Node) []*ast.Node {
|
||||
properties = core.Filter(properties, func(p *ast.Symbol) bool {
|
||||
return p.Flags&ast.SymbolFlagsPrototype == 0
|
||||
})
|
||||
for i, p := range properties {
|
||||
if b.checkTruncationLengthIfExpanding() && (i+3 < len(properties)-1) {
|
||||
b.ctx.expansionTruncated = true
|
||||
text := fmt.Sprintf("... %d more ...", len(properties)-i-1)
|
||||
elements = append(elements, b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier(text), nil, nil, nil))
|
||||
elements = b.addPropertyToElementList(properties[len(properties)-1], elements)
|
||||
break
|
||||
}
|
||||
elements = b.addPropertyToElementList(p, elements)
|
||||
}
|
||||
return elements
|
||||
}
|
||||
|
||||
// serializeConstructors builds constructor signature(s) for a class, with base type filtering.
|
||||
func (b *NodeBuilderImpl) serializeConstructors(staticType *Type, staticBaseType *Type, isClass bool, symbol *ast.Symbol) []*ast.Node {
|
||||
isNonConstructable := !isClass &&
|
||||
symbol.ValueDeclaration != nil &&
|
||||
ast.IsInJSFile(symbol.ValueDeclaration) &&
|
||||
len(b.ch.getSignaturesOfType(staticType, SignatureKindConstruct)) == 0
|
||||
if isNonConstructable {
|
||||
b.ctx.approximateLength += 21
|
||||
modifiers := ast.CreateModifiersFromModifierFlags(ast.ModifierFlagsPrivate, b.f.NewModifier)
|
||||
return []*ast.Node{b.f.NewConstructorDeclaration(b.f.NewModifierList(modifiers), nil, b.f.NewNodeList(nil), nil, nil, nil)}
|
||||
}
|
||||
signatures := b.ch.getSignaturesOfType(staticType, SignatureKindConstruct)
|
||||
if staticBaseType != nil {
|
||||
baseSigs := b.ch.getSignaturesOfType(staticBaseType, SignatureKindConstruct)
|
||||
if len(baseSigs) == 0 && core.Every(signatures, func(sig *Signature) bool { return len(sig.parameters) == 0 }) {
|
||||
return nil
|
||||
}
|
||||
if len(baseSigs) == len(signatures) {
|
||||
allMatch := true
|
||||
for i := range baseSigs {
|
||||
if b.ch.compareSignaturesIdentical(signatures[i], baseSigs[i], false, false, true, b.ch.compareTypesIdentical) != TernaryTrue {
|
||||
allMatch = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allMatch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var privateProtected ast.ModifierFlags
|
||||
for _, sig := range signatures {
|
||||
if sig.declaration != nil {
|
||||
privateProtected |= sig.declaration.ModifierFlags() & (ast.ModifierFlagsPrivate | ast.ModifierFlagsProtected)
|
||||
}
|
||||
}
|
||||
if privateProtected != 0 {
|
||||
return []*ast.Node{b.f.NewConstructorDeclaration(
|
||||
b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(privateProtected, b.f.NewModifier)),
|
||||
nil, b.f.NewNodeList(nil), nil, nil, nil,
|
||||
)}
|
||||
}
|
||||
} else if core.Every(signatures, func(sig *Signature) bool { return len(sig.parameters) == 0 }) {
|
||||
return nil
|
||||
}
|
||||
var result []*ast.Node
|
||||
for _, sig := range signatures {
|
||||
b.ctx.approximateLength++
|
||||
result = append(result, b.signatureToSignatureDeclarationHelper(sig, ast.KindConstructor, nil))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// serializeIndexSignaturesOfType builds index signature declarations, filtering those identical to baseType.
|
||||
func (b *NodeBuilderImpl) serializeIndexSignaturesOfType(input *Type, baseType *Type) []*ast.Node {
|
||||
var result []*ast.Node
|
||||
for _, info := range b.ch.getIndexInfosOfType(input) {
|
||||
if baseType != nil {
|
||||
baseInfo := b.ch.getIndexInfoOfType(baseType, info.keyType)
|
||||
if baseInfo != nil && b.ch.isTypeIdenticalTo(info.valueType, baseInfo.valueType) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
result = append(result, b.indexInfoToIndexSignatureDeclarationHelper(info, nil))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// serializeNamespaceMember produces the appropriate declaration node for a namespace member
|
||||
// based on its symbol flags (type alias, enum, class, interface, nested namespace, or variable).
|
||||
func (b *NodeBuilderImpl) serializeNamespaceMember(resolved *ast.Symbol, name string) *ast.Node {
|
||||
switch {
|
||||
case resolved.Flags&ast.SymbolFlagsTypeAlias != 0:
|
||||
return b.serializeTypeAliasForNamespace(resolved, name)
|
||||
case resolved.Flags&ast.SymbolFlagsEnum != 0:
|
||||
return b.expandEnumDecl(resolved)
|
||||
case resolved.Flags&ast.SymbolFlagsClass != 0:
|
||||
return b.expandClassDecl(resolved)
|
||||
case resolved.Flags&ast.SymbolFlagsInterface != 0:
|
||||
return b.expandInterfaceDecl(resolved)
|
||||
case resolved.Flags&(ast.SymbolFlagsValueModule|ast.SymbolFlagsNamespaceModule) != 0:
|
||||
return b.expandModuleDecl(resolved)
|
||||
default:
|
||||
t := b.ch.getWidenedType(b.ch.getTypeOfSymbol(resolved))
|
||||
b.ctx.approximateLength += len(name) + 5
|
||||
return b.f.NewVariableStatement(
|
||||
nil,
|
||||
b.f.NewVariableDeclarationList(
|
||||
b.f.NewNodeList([]*ast.Node{
|
||||
b.f.NewVariableDeclaration(b.f.NewIdentifier(name), nil, b.serializeTypeForDeclaration(nil, t, resolved, true), nil),
|
||||
}),
|
||||
ast.NodeFlagsLet,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// expandModuleDecl produces a ModuleDeclaration with exported members.
|
||||
func (b *NodeBuilderImpl) expandModuleDecl(symbol *ast.Symbol) *ast.Node {
|
||||
exports := b.ch.getExportsOfSymbol(symbol)
|
||||
var members []*ast.Symbol
|
||||
for _, sym := range exports {
|
||||
// Filter to namespace-relevant members
|
||||
if !b.isNamespaceMember(sym) {
|
||||
continue
|
||||
}
|
||||
if !scanner.IsIdentifierText(sym.Name, core.LanguageVariantStandard) {
|
||||
continue
|
||||
}
|
||||
members = append(members, sym)
|
||||
}
|
||||
b.ch.sortSymbols(members)
|
||||
b.ctx.approximateLength += 14
|
||||
|
||||
// Use the same name as symbol display.
|
||||
oldFlags := b.ctx.flags
|
||||
defer func() { b.ctx.flags = oldFlags }()
|
||||
b.ctx.flags |= nodebuilder.FlagsWriteTypeParametersInQualifiedName | nodebuilder.Flags(SymbolFormatFlagsUseOnlyExternalAliasing)
|
||||
localName := b.symbolToNode(symbol, ast.SymbolFlagsAll)
|
||||
b.ctx.flags = oldFlags
|
||||
|
||||
type hoverStatement struct {
|
||||
node *ast.Node
|
||||
isLocal bool // local declarations (e.g. alias targets) should not get export modifier
|
||||
}
|
||||
var bodyStmts []hoverStatement
|
||||
var emittedLocals collections.Set[*ast.Symbol]
|
||||
for i := 0; i < len(members); i++ {
|
||||
m := members[i]
|
||||
if b.checkTruncationLengthIfExpanding() && i+3 < len(members)-1 {
|
||||
b.ctx.expansionTruncated = true
|
||||
bodyStmts = append(bodyStmts, hoverStatement{node: b.f.NewExpressionStatement(b.f.NewIdentifier(fmt.Sprintf("... (%d more) ...", len(members)-i-1)))})
|
||||
i = len(members) - 2 // skip to last member after i++ at end of iteration
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle alias/re-export symbols
|
||||
if m.Flags&ast.SymbolFlagsAlias != 0 {
|
||||
aliasDecl := b.ch.getDeclarationOfAliasSymbol(m)
|
||||
target := b.ch.getMergedSymbol(b.ch.getTargetOfAliasDeclaration(aliasDecl))
|
||||
if target != nil {
|
||||
// If the alias target is a local symbol (not itself an export), emit its declaration first
|
||||
if target.Flags&(ast.SymbolFlagsBlockScopedVariable|ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsProperty) != 0 {
|
||||
if emittedLocals.AddIfAbsent(target) {
|
||||
localType := b.ch.getWidenedType(b.ch.getTypeOfSymbol(target))
|
||||
b.ctx.approximateLength += len(target.Name) + 5
|
||||
localStmt := b.f.NewVariableStatement(nil,
|
||||
b.f.NewVariableDeclarationList(b.f.NewNodeList([]*ast.Node{
|
||||
b.f.NewVariableDeclaration(b.f.NewIdentifier(target.Name), nil, b.serializeTypeForDeclaration(nil, localType, target, true), nil),
|
||||
}), ast.NodeFlagsLet))
|
||||
bodyStmts = append(bodyStmts, hoverStatement{node: localStmt, isLocal: true})
|
||||
}
|
||||
}
|
||||
targetName := target.Name
|
||||
b.ctx.approximateLength += 16 + len(m.Name)
|
||||
var propertyName *ast.Node
|
||||
if m.Name != targetName {
|
||||
propertyName = b.f.NewIdentifier(targetName)
|
||||
}
|
||||
stmt := b.f.NewExportDeclaration(
|
||||
nil, false,
|
||||
b.f.NewNamedExports(b.f.NewNodeList([]*ast.Node{
|
||||
b.f.NewExportSpecifier(false, propertyName, b.f.NewIdentifier(m.Name)),
|
||||
})),
|
||||
nil, nil,
|
||||
)
|
||||
bodyStmts = append(bodyStmts, hoverStatement{node: stmt})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
resolved := b.ch.resolveSymbol(m)
|
||||
|
||||
// Handle functions as function declarations
|
||||
if resolved.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 {
|
||||
t := b.ch.getTypeOfSymbol(resolved)
|
||||
sigs := b.ch.getSignaturesOfType(t, SignatureKindCall)
|
||||
for _, sig := range sigs {
|
||||
b.ctx.approximateLength++
|
||||
decl := b.signatureToSignatureDeclarationHelper(sig, ast.KindFunctionDeclaration, &SignatureToSignatureDeclarationOptions{
|
||||
name: b.f.NewIdentifier(m.Name),
|
||||
})
|
||||
bodyStmts = append(bodyStmts, hoverStatement{node: decl})
|
||||
}
|
||||
// If the function also has namespace characteristics, emit an empty namespace.
|
||||
merged := b.ch.getMergedSymbol(resolved)
|
||||
hasModuleExports := merged.Flags&(ast.SymbolFlagsValueModule|ast.SymbolFlagsNamespaceModule) != 0 && merged.Exports != nil && len(merged.Exports) != 0
|
||||
if !hasModuleExports {
|
||||
bodyStmts = append(bodyStmts, hoverStatement{node: b.f.NewModuleDeclaration(nil, ast.KindNamespaceKeyword, b.f.NewIdentifier(m.Name), b.f.NewModuleBlock(b.f.NewNodeList(nil)))})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle remaining member kinds (type alias, enum, class, interface, namespace, variable)
|
||||
if node := b.serializeNamespaceMember(resolved, m.Name); node != nil {
|
||||
bodyStmts = append(bodyStmts, hoverStatement{node: node})
|
||||
}
|
||||
}
|
||||
|
||||
// Add export modifier to exported statements (skip local declarations and ExportDeclarations).
|
||||
for i := range bodyStmts {
|
||||
s := &bodyStmts[i]
|
||||
if s.isLocal || ast.IsExportDeclaration(s.node) {
|
||||
continue
|
||||
}
|
||||
if ast.CanHaveModifiers(s.node) {
|
||||
mf := s.node.ModifierFlags() | ast.ModifierFlagsExport
|
||||
s.node = ast.ReplaceModifiers(b.f, s.node, b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(mf, b.f.NewModifier)))
|
||||
}
|
||||
}
|
||||
|
||||
// Collect nodes, stripping export if all statements are exported.
|
||||
bodyStatements := make([]*ast.Node, len(bodyStmts))
|
||||
for i := range bodyStmts {
|
||||
bodyStatements[i] = bodyStmts[i].node
|
||||
}
|
||||
allExported := len(bodyStatements) > 0 && core.Every(bodyStatements, func(d *ast.Node) bool {
|
||||
return ast.HasSyntacticModifier(d, ast.ModifierFlagsExport)
|
||||
})
|
||||
if allExported {
|
||||
for i, stmt := range bodyStatements {
|
||||
if ast.CanHaveModifiers(stmt) {
|
||||
mf := stmt.ModifierFlags() &^ ast.ModifierFlagsExport
|
||||
bodyStatements[i] = ast.ReplaceModifiers(b.f, stmt, b.f.NewModifierList(ast.CreateModifiersFromModifierFlags(mf, b.f.NewModifier)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyword := ast.KindNamespaceKeyword
|
||||
if !ast.IsIdentifier(localName) {
|
||||
keyword = ast.KindModuleKeyword
|
||||
}
|
||||
return b.f.NewModuleDeclaration(nil, keyword, localName, b.f.NewModuleBlock(b.f.NewNodeList(bodyStatements)))
|
||||
}
|
||||
|
||||
// serializeTypeAliasForNamespace produces a TypeAliasDeclaration for a type alias inside a namespace body.
|
||||
func (b *NodeBuilderImpl) serializeTypeAliasForNamespace(symbol *ast.Symbol, name string) *ast.Node {
|
||||
aliasType := b.ch.getDeclaredTypeOfTypeAlias(symbol)
|
||||
typeParams := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
|
||||
typeParamDecls := core.Map(typeParams, func(p *Type) *ast.Node { return b.typeParameterToDeclaration(p) })
|
||||
restoreFlags := b.saveRestoreFlags()
|
||||
b.ctx.flags |= nodebuilder.FlagsInTypeAlias
|
||||
typeNode := b.typeToTypeNode(aliasType)
|
||||
restoreFlags()
|
||||
b.ctx.approximateLength += 8 + len(name)
|
||||
return b.f.NewTypeAliasDeclaration(nil, b.f.NewIdentifier(name), b.f.NewNodeList(typeParamDecls), typeNode)
|
||||
}
|
||||
|
||||
// filterInheritedProperties removes properties already present in base types.
|
||||
func (b *NodeBuilderImpl) filterInheritedProperties(t *Type, baseTypes []*Type, properties []*ast.Symbol) []*ast.Symbol {
|
||||
if len(baseTypes) == 0 {
|
||||
return properties
|
||||
}
|
||||
// Build a lookup from property name to symbol for parent-identity comparison.
|
||||
propsByName := make(map[string]*ast.Symbol, len(properties))
|
||||
for _, p := range properties {
|
||||
propsByName[p.Name] = p
|
||||
}
|
||||
// Collect names of properties inherited unchanged from base types.
|
||||
var inherited collections.Set[string]
|
||||
for _, base := range baseTypes {
|
||||
baseWithThis := b.ch.getTypeWithThisArgument(base, b.ch.getTargetType(t).AsInterfaceType().thisType, false)
|
||||
for _, prop := range b.ch.getPropertiesOfType(baseWithThis) {
|
||||
if existing, ok := propsByName[prop.Name]; ok && prop.Parent == existing.Parent {
|
||||
inherited.Add(prop.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if inherited.Len() == 0 {
|
||||
return properties
|
||||
}
|
||||
return core.Filter(properties, func(p *ast.Symbol) bool {
|
||||
return !inherited.Has(p.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func (b *NodeBuilderImpl) isNamespaceMember(p *ast.Symbol) bool {
|
||||
return p.Flags&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias) != 0 ||
|
||||
!(p.Flags&ast.SymbolFlagsPrototype != 0 || p.Name == "prototype" || (p.ValueDeclaration != nil && ast.HasStaticModifier(p.ValueDeclaration) && ast.IsClassLike(p.ValueDeclaration.Parent)))
|
||||
}
|
||||
|
||||
func isHashPrivate(s *ast.Symbol) bool {
|
||||
return s.ValueDeclaration != nil && s.ValueDeclaration.Name() != nil && ast.IsPrivateIdentifier(s.ValueDeclaration.Name())
|
||||
}
|
||||
Reference in New Issue
Block a user