blob: 41873161cc68c69ea8d0a99b536655cb7d75c583 [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/doc"
"go/token"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/godoc/dochtml/internal/render"
)
// 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
}
vars, err := variables(p.Vars, fset)
if err != nil {
return nil, err
}
return append(append(append(
constants(p.Consts), vars...), functions(p, fset)...), typs...), nil
}
func constants(consts []*doc.Value) []*internal.Symbol {
var syms []*internal.Symbol
for _, c := range consts {
for _, n := range c.Names {
if n == "_" {
continue
}
syms = append(syms, &internal.Symbol{
SymbolMeta: internal.SymbolMeta{
Name: n,
Synopsis: "const " + n,
Section: internal.SymbolSectionConstants,
Kind: internal.SymbolKindConstant,
},
})
}
}
return syms
}
func variables(vars []*doc.Value, fset *token.FileSet) (_ []*internal.Symbol, err error) {
defer derrors.Wrap(&err, "variables")
var syms []*internal.Symbol
for _, v := range vars {
specs := v.Decl.Specs
for _, spec := range specs {
valueSpec := spec.(*ast.ValueSpec) // must succeed; we can't mix types in one GenDecl.
for _, ident := range valueSpec.Names {
if ident.Name == "_" {
continue
}
vs := *valueSpec
if len(valueSpec.Names) != 0 {
vs.Names = []*ast.Ident{ident}
}
syn := render.ConstOrVarSynopsis(&vs, fset, token.VAR, "", 0, 0)
syms = append(syms,
&internal.Symbol{
SymbolMeta: internal.SymbolMeta{
Name: ident.Name,
Synopsis: syn,
Section: internal.SymbolSectionVariables,
Kind: internal.SymbolKindVariable,
},
})
}
}
}
return syms, nil
}
func functions(p *doc.Package, fset *token.FileSet) []*internal.Symbol {
var syms []*internal.Symbol
for _, f := range p.Funcs {
syms = append(syms, &internal.Symbol{
SymbolMeta: internal.SymbolMeta{
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{
SymbolMeta: internal.SymbolMeta{
Name: typ.Name,
Synopsis: render.OneLineNodeDepth(fset, spec, 0),
Section: internal.SymbolSectionTypes,
Kind: internal.SymbolKindType,
},
}
fields := fieldsForType(typ.Name, spec, fset)
if err != nil {
return nil, err
}
syms = append(syms, t)
vars, err := variablesForType(typ, fset)
if err != nil {
return nil, err
}
t.Children = append(append(append(append(append(
t.Children,
constantsForType(typ)...),
vars...),
functionsForType(typ, fset)...),
fields...),
mthds...)
}
return syms, nil
}
func constantsForType(t *doc.Type) []*internal.SymbolMeta {
consts := constants(t.Consts)
var typConsts []*internal.SymbolMeta
for _, c := range consts {
c2 := c.SymbolMeta
c2.ParentName = t.Name
c2.Section = internal.SymbolSectionTypes
typConsts = append(typConsts, &c2)
}
return typConsts
}
func variablesForType(t *doc.Type, fset *token.FileSet) (_ []*internal.SymbolMeta, err error) {
vars, err := variables(t.Vars, fset)
if err != nil {
return nil, err
}
var typVars []*internal.SymbolMeta
for _, v := range vars {
v2 := v.SymbolMeta
v2.ParentName = t.Name
v2.Section = internal.SymbolSectionTypes
typVars = append(typVars, &v2)
}
return typVars, nil
}
func functionsForType(t *doc.Type, fset *token.FileSet) []*internal.SymbolMeta {
var syms []*internal.SymbolMeta
for _, f := range t.Funcs {
syms = append(syms, &internal.SymbolMeta{
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.SymbolMeta {
st, ok := spec.Type.(*ast.StructType)
if !ok {
return nil
}
var syms []*internal.SymbolMeta
for _, f := range st.Fields.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.
for _, n := range f.Names {
synopsis := fmt.Sprintf("%s %s", n, render.OneLineNodeDepth(fset, f.Type, 0))
name := typName + "." + n.Name
syms = append(syms, &internal.SymbolMeta{
Name: name,
ParentName: typName,
Kind: internal.SymbolKindField,
Synopsis: synopsis,
Section: internal.SymbolSectionTypes,
})
}
}
return syms
}
func methodsForType(t *doc.Type, spec *ast.TypeSpec, fset *token.FileSet) ([]*internal.SymbolMeta, error) {
var syms []*internal.SymbolMeta
for _, m := range t.Methods {
syms = append(syms, &internal.SymbolMeta{
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 {
name := t.Name + "." + n.Name
synopsis := render.OneLineField(fset, m, 0)
syms = append(syms, &internal.SymbolMeta{
Name: name,
ParentName: t.Name,
Kind: internal.SymbolKindMethod,
Synopsis: synopsis,
Section: internal.SymbolSectionTypes,
})
}
}
}
return syms, nil
}