| // 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 |
| } |