blob: 0f3255210ea7570442a8c0a15e0bc8d6bb27dc62 [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dochtml
import (
"fmt"
"go/ast"
"go/token"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/godoc/dochtml/internal/render"
"golang.org/x/pkgsite/internal/godoc/internal/doc"
)
// GetSymbols renders package documentation HTML for the
// provided file set and package, in separate parts.
//
// If any of the rendered documentation part HTML sizes exceeds the specified
// limit, an error with ErrTooLarge in its chain will be returned.
func GetSymbols(p *doc.Package, fset *token.FileSet) (_ []*internal.Symbol, err error) {
defer derrors.Wrap(&err, "GetSymbols for %q", p.ImportPath)
if docIsEmpty(p) {
return nil, nil
}
typs, err := types(p, fset)
if err != nil {
return nil, err
}
return append(append(append(
constants(p, fset), variables(p, fset)...), functions(p, fset)...), typs...), nil
}
func constants(p *doc.Package, fset *token.FileSet) []*internal.Symbol {
var syms []*internal.Symbol
for _, c := range p.Consts {
for _, n := range c.Names {
syms = append(syms, &internal.Symbol{
Name: n,
Synopsis: render.OneLineNodeDepth(fset, c.Decl, 0),
Section: internal.SymbolSectionConstants,
Kind: internal.SymbolKindConstant,
})
}
}
return syms
}
func variables(p *doc.Package, fset *token.FileSet) []*internal.Symbol {
var syms []*internal.Symbol
for _, v := range p.Vars {
for _, n := range v.Names {
syms = append(syms, &internal.Symbol{
Name: n,
Synopsis: render.OneLineNodeDepth(fset, v.Decl, 0),
Section: internal.SymbolSectionVariables,
Kind: internal.SymbolKindVariable,
})
}
}
return syms
}
func functions(p *doc.Package, fset *token.FileSet) []*internal.Symbol {
var syms []*internal.Symbol
for _, f := range p.Funcs {
syms = append(syms, &internal.Symbol{
Name: f.Name,
Synopsis: render.OneLineNodeDepth(fset, f.Decl, 0),
Section: internal.SymbolSectionFunctions,
Kind: internal.SymbolKindFunction,
})
}
return syms
}
func types(p *doc.Package, fset *token.FileSet) ([]*internal.Symbol, error) {
var syms []*internal.Symbol
for _, typ := range p.Types {
specs := typ.Decl.Specs
if len(specs) != 1 {
return nil, fmt.Errorf("unexpected number of t.Decl.Specs: %d (wanted len = 1)", len(typ.Decl.Specs))
}
spec, ok := specs[0].(*ast.TypeSpec)
if !ok {
return nil, fmt.Errorf("unexpected type for Spec node: %q", typ.Name)
}
mthds, err := methodsForType(typ, spec, fset)
if err != nil {
return nil, err
}
t := &internal.Symbol{
Name: typ.Name,
Synopsis: render.OneLineNodeDepth(fset, spec, 0),
Section: internal.SymbolSectionTypes,
Kind: internal.SymbolKindType,
}
syms = append(syms, t)
t.Children = append(append(append(append(append(
t.Children,
constantsForType(typ, fset)...),
variablesForType(typ, fset)...),
functionsForType(typ, fset)...),
fieldsForType(typ.Name, spec, fset)...),
mthds...)
}
return syms, nil
}
func constantsForType(t *doc.Type, fset *token.FileSet) []*internal.Symbol {
var syms []*internal.Symbol
for _, c := range t.Consts {
for _, n := range c.Names {
syms = append(syms, &internal.Symbol{
Name: n,
ParentName: t.Name,
Kind: internal.SymbolKindConstant,
Synopsis: render.OneLineNodeDepth(fset, c.Decl, 0),
Section: internal.SymbolSectionTypes,
})
}
}
return syms
}
func variablesForType(t *doc.Type, fset *token.FileSet) []*internal.Symbol {
var syms []*internal.Symbol
for _, v := range t.Vars {
for _, n := range v.Names {
syms = append(syms, &internal.Symbol{
Name: n,
ParentName: t.Name,
Kind: internal.SymbolKindVariable,
Synopsis: render.OneLineNodeDepth(fset, v.Decl, 0),
Section: internal.SymbolSectionTypes,
})
}
}
return syms
}
func functionsForType(t *doc.Type, fset *token.FileSet) []*internal.Symbol {
var syms []*internal.Symbol
for _, f := range t.Funcs {
syms = append(syms, &internal.Symbol{
Name: f.Name,
ParentName: t.Name,
Kind: internal.SymbolKindFunction,
Synopsis: render.OneLineNodeDepth(fset, f.Decl, 0),
Section: internal.SymbolSectionTypes,
})
}
return syms
}
func fieldsForType(typName string, spec *ast.TypeSpec, fset *token.FileSet) []*internal.Symbol {
st, ok := spec.Type.(*ast.StructType)
if !ok {
return nil
}
var syms []*internal.Symbol
for _, f := range st.Fields.List {
for _, n := range f.Names {
syms = append(syms, &internal.Symbol{
Name: typName + "." + n.Name,
ParentName: typName,
Kind: internal.SymbolKindField,
Synopsis: render.OneLineNodeDepth(fset, n, 0),
Section: internal.SymbolSectionTypes,
})
}
}
return syms
}
func methodsForType(t *doc.Type, spec *ast.TypeSpec, fset *token.FileSet) ([]*internal.Symbol, error) {
var syms []*internal.Symbol
for _, m := range t.Methods {
syms = append(syms, &internal.Symbol{
Name: t.Name + "." + m.Name,
ParentName: t.Name,
Kind: internal.SymbolKindMethod,
Synopsis: render.OneLineNodeDepth(fset, m.Decl, 0),
Section: internal.SymbolSectionTypes,
})
}
if st, ok := spec.Type.(*ast.InterfaceType); ok {
for _, m := range st.Methods.List {
// It's not possible for there to be more than one name.
// FieldList is also used by go/ast for st.Methods, which is the
// only reason this type is a list.
if len(m.Names) > 1 {
return nil, fmt.Errorf("len(m.Names) = %d; expected 0 or 1", len(m.Names))
}
for _, n := range m.Names {
syms = append(syms, &internal.Symbol{
Name: t.Name + "." + n.Name,
ParentName: t.Name,
Kind: internal.SymbolKindMethod,
Synopsis: render.OneLineNodeDepth(fset, n, 0),
Section: internal.SymbolSectionTypes,
})
}
}
}
return syms, nil
}