Copyright 2019 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 renders Go package documentation into HTML. This package and its API are under development (see golang.org/issue/39883). The plan is to iterate on the development internally for x/pkgsite needs first, before factoring it out somewhere non-internal where its API can no longer be easily modified.
package dochtml

import (
	
	
	
	
	
	
	
	
	

	
	
	
	
	
	
	
	
	
)

ErrTooLarge represents an error where the rendered documentation HTML size exceeded the specified limit. See the RenderOptions.Limit field.
	ErrTooLarge = errors.New("rendered documentation HTML size exceeded the specified limit")
)
ModuleInfo contains all the information a package needs about the module it belongs to in order to render its documentation.
ModulePackages is the set of all full package paths in the module.
RenderOptions are options for Render.
FileLinkFunc optionally specifies a function that returns a URL where file should be linked to. file is the name component of a .go file in the package, including the .go qualifier. As a special case, FileLinkFunc may return the empty string to indicate that a given file should not be linked.
ModInfo optionally specifies information about the module the package belongs to in order to render module-related documentation.
	ModInfo *ModuleInfo
	Limit   int64 // If zero, a default limit of 10 megabytes is used.
}
templateData holds the data passed to the HTML templates in this package.
Parts contains HTML for each part of the documentation.
type Parts struct {
	Body          safehtml.HTML // main body of doc
	Outline       safehtml.HTML // outline for large screens
	MobileOutline safehtml.HTML // outline for mobile
	Links         []render.Link // "Links" section of package doc
}
Render 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 ( context.Context,  *token.FileSet,  *doc.Package,  RenderOptions) ( *Parts,  error) {
	defer derrors.Wrap(&, "dochtml.RenderParts")

	if .Limit == 0 {
		const  = 1000 * 1000
		.Limit = 10 * 
	}

Simpler to clear the fields here than to add experiment checks in the templates.
		for ,  := range .Consts {
			.IsDeprecated = false
		}
		for ,  := range .Vars {
			.IsDeprecated = false
		}
		for ,  := range .Funcs {
			.IsDeprecated = false
		}
		for ,  := range .Types {
			.IsDeprecated = false
			for ,  := range .Funcs {
				.IsDeprecated = false
			}
			for ,  := range .Methods {
				.IsDeprecated = false
			}
		}
	}

	, ,  := renderInfo(, , , )
	 = .Package
	if docIsEmpty() {
		return &Parts{}, nil
	}

	 := func( *template.Template) safehtml.HTML {
		if  != nil {
			return safehtml.HTML{}
		}
		 := template.Must(.Clone()).Funcs()
		var  safehtml.HTML
		,  = executeToHTMLWithLimit(, , .Limit)
		return 
	}

	 := &Parts{
		Body:          (bodyTemplate),
		Outline:       (outlineTemplate),
links must be called after body, because the call to render_doc_extract_links in body.tmpl creates the links.
		Links: (),
	}
	if  != nil {
		return nil, 
	}
	return , nil
}
An item is rendered as one piece of documentation. It is essentially a union of the Value, Type and Func types from internal/doc, along with additional information for HTML rendering, like class names.
type item struct {
	Doc                          string
	Decl                         ast.Decl   // GenDecl for consts, vars and types; FuncDecl for functions
	Name                         string     // for types and functions; empty for consts and vars
	FullName                     string     // for methods, the type name + "." + Name; else same as Name
	HeaderStart                  string     // text of header, before source link
	Examples                     []*example // for types and functions; empty for vars and consts
	IsDeprecated                 bool
HTML-specific values, for types and functions
	Kind        string // for data-kind attribute
	HeaderClass string // class for header
}

func ( *doc.Package,  map[string][]*example) (, , ,  []*item) {
	 = valuesToItems(.Consts)
	 = valuesToItems(.Vars)
	 = funcsToItems(.Funcs, "Documentation-functionHeader", "", )
	for ,  := range .Types {
		 = append(, typeToItem(, ))
	}
	return , , , 
}

func ( []*doc.Value) []*item {
	var  []*item
	for ,  := range  {
		 = append(, valueToItem())
	}
	return 
}

func ( *doc.Value) *item {
	return &item{
		Doc:          .Doc,
		Decl:         .Decl,
		IsDeprecated: .IsDeprecated,
	}
}

func ( []*doc.Func, ,  string,  map[string][]*example) []*item {
	var  []*item
	for ,  := range  {
		 := .Name
		if  != "" {
			 =  + "." + .Name
		}
		 := "function"
		 := "func"
		if .Recv != "" {
			 = "method"
			 += " (" + .Recv + ")"
		}
		 := &item{
			Doc:          .Doc,
			Decl:         .Decl,
			Name:         .Name,
			FullName:     ,
			HeaderStart:  ,
			IsDeprecated: .IsDeprecated,
			Examples:     [],
			Kind:         ,
			HeaderClass:  ,
		}
		 = append(, )
	}
	return 
}

func ( *doc.Type,  map[string][]*example) *item {
	return &item{
		Name:         .Name,
		FullName:     .Name,
		Doc:          .Doc,
		Decl:         .Decl,
		HeaderStart:  "type",
		IsDeprecated: .IsDeprecated,
		Kind:         "type",
		HeaderClass:  "Documentation-typeHeader",
		Examples:     [.Name],
		Consts:       valuesToItems(.Consts),
		Vars:         valuesToItems(.Vars),
		Funcs:        funcsToItems(.Funcs, "Documentation-typeFuncHeader", "", ),
		Methods:      funcsToItems(.Methods, "Documentation-typeMethodHeader", .Name, ),
	}
}

func ( *doc.Package) bool {
	return .Doc == "" &&
		len(.Examples) == 0 &&
		len(.Consts) == 0 &&
		len(.Vars) == 0 &&
		len(.Types) == 0 &&
		len(.Funcs) == 0
}
renderInfo returns the functions and data needed to render the doc.
Make a copy to avoid modifying caller's *doc.Package.
	 := *
	 = &
When rendering documentation for commands, display the package comment and notes, but no declarations.
Clear top-level declarations.
		.Consts = nil
		.Types = nil
		.Vars = nil
		.Funcs = nil
		.Examples = nil
	}
Remove everything from the notes section that is not a bug. This includes TODOs and other arbitrary notes.
	for  := range .Notes {
		if  == "BUG" {
			continue
		}
		delete(.Notes, )
	}

	 := render.New(, , , &render.Options{
Use the same module version for imported packages that belong to the same module.
			 := 
			if .ModInfo != nil {
				 = versionedPkgPath(, .ModInfo)
			}
			return "/" + 
		},
		DisableHotlinking: true,
		EnableCommandTOC:  true,
	})

	 := func( string) safehtml.HTML {
		return linkHTML(, .FileLinkFunc(), "Documentation-file")
	}
	 := func( string,  ast.Node) safehtml.HTML {
		return linkHTML(, .SourceLinkFunc(), "Documentation-source")
	}
	 := func( string) safehtml.HTML {
		return safehtml.HTMLEscaped(.SinceVersionFunc())
	}
	 := map[string]interface{}{
		"render_short_synopsis":    .ShortSynopsis,
		"render_synopsis":          .Synopsis,
		"render_doc":               .DocHTML,
		"render_doc_extract_links": .DocHTMLExtractLinks,
		"render_decl":              .DeclHTML,
		"render_code":              .CodeHTML,
		"file_link":                ,
		"source_link":              ,
		"since_version":            ,
	}
	 := collectExamples()
	 := templateData{
		Package:     ,
		RootURL:     "/pkg",
		Examples:    ,
		NoteHeaders: buildNoteHeaders(.Notes),
	}
	.Consts, .Vars, .Funcs, .Types = packageToItems(, .Map)
	return , , .Links
}
executeToHTMLWithLimit executes tmpl on data and returns the result as a safehtml.HTML. It returns an error if the size of the result exceeds limit.
func ( *template.Template,  interface{},  int64) (safehtml.HTML, error) {
	 := &limitBuffer{B: new(bytes.Buffer), Remain: }
	 := .Execute(, )
	if .Remain < 0 {
		return safehtml.HTML{}, fmt.Errorf("dochtml.Render: %w", ErrTooLarge)
	} else if  != nil {
		return safehtml.HTML{}, fmt.Errorf("dochtml.Render: %v", )
	}
This is safe because we're executing a safehtml template and not modifying the result afterwards. We're just doing what safehtml/template.Template.ExecuteToHTML does (https://github.com/google/safehtml/blob/b8ae3e5e1ce3/template/template.go#L136).
linkHTML returns an HTML-formatted name linked to the given URL. The class argument is the class of the 'a' tag. If url is the empty string, the name is not linked.
func (, ,  string) safehtml.HTML {
	if  == "" {
		return safehtml.HTMLEscaped()
	}
	return render.ExecuteToHTML(render.LinkTemplate, render.Link{Class: , Href: , Text: })
}
examples is an internal representation of all package examples.
type examples struct {
	List []*example            // sorted by ParentID
	Map  map[string][]*example // keyed by top-level ID (e.g., "NewRing" or "PubSub.Receive") or empty string for package examples
}
example is an internal representation of a single example.
type example struct {
	*doc.Example
	ID       safehtml.Identifier // ID of example
	ParentID string              // ID of top-level declaration this example is attached to
	Suffix   string              // optional suffix name in title case
}
Code returns an printer.CommentedNode if ex.Comments is non-nil, otherwise it returns ex.Code as is.
func ( *example) () interface{} {
	if len(.Comments) > 0 {
		return &printer.CommentedNode{Node: .Example.Code, Comments: .Comments}
	}
	return .Example.Code
}
WalkExamples calls fn for each Example in p, setting id to the name of the parent structure.
func ( *doc.Package,  func( string,  *doc.Example)) {
	for ,  := range .Examples {
		("", )
	}
	for ,  := range .Funcs {
		for ,  := range .Examples {
			(.Name, )
		}
	}
	for ,  := range .Types {
		for ,  := range .Examples {
			(.Name, )
		}
		for ,  := range .Funcs {
			for ,  := range .Examples {
				(.Name, )
			}
		}
		for ,  := range .Methods {
			for ,  := range .Examples {
				(.Name+"."+.Name, )
			}
		}
	}
}
collectExamples extracts examples from p into the internal examples representation.
func ( *doc.Package) *examples {
	 := &examples{
		List: nil,
		Map:  make(map[string][]*example),
	}
	WalkExamples(, func( string,  *doc.Example) {
		 := strings.Title(.Suffix)
		 := &example{
			Example:  ,
			ID:       exampleID(, ),
			ParentID: ,
			Suffix:   ,
		}
		.List = append(.List, )
		.Map[] = append(.Map[], )
	})
TODO: Break ties by sorting by suffix, unless not needed because of upstream slice order.
		return .List[].ParentID < .List[].ParentID
	})
	return 
}

func (,  string) safehtml.Identifier {
	switch {
	case  == "" &&  == "":
		return safehtml.IdentifierFromConstant("example-package")
	case  == "" &&  != "":
		render.ValidateGoDottedExpr()
		return legacyconversions.RiskilyAssumeIdentifier("example-package-" + )
	case  != "" &&  == "":
		render.ValidateGoDottedExpr()
		return legacyconversions.RiskilyAssumeIdentifier("example-" + )
	case  != "" &&  != "":
		render.ValidateGoDottedExpr()
		render.ValidateGoDottedExpr()
		return legacyconversions.RiskilyAssumeIdentifier("example-" +  + "-" + )
	default:
		panic("unreachable")
	}
}
noteHeader contains informations the template needs to render the note related HTML tags in documentation page.
buildNoteHeaders constructs note headers from note markers. It returns a map from each marker to its corresponding noteHeader.
func ( map[string][]*doc.Note) map[string]noteHeader {
	 := map[string]noteHeader{}
	for  := range  {
		[] = noteHeader{
			SafeIdentifier: safehtml.IdentifierFromConstantPrefix("pkg-note", ),
			Label:          strings.Title(strings.ToLower()),
		}
	}
	return 
}
versionedPkgPath transforms package paths to contain the same version as the current module if the package belongs to the module. As a special case, versionedPkgPath will not add versions to standard library packages.
func ( string,  *ModuleInfo) string {
	if  == nil || !.ModulePackages[] {
		return 
We don't need to do anything special here for standard library packages since pkgPath will never contain the "std/" module prefix, and modInfo.ModulePackages contains this prefix for standard library packages.
	 := [len(.ModulePath):]
	return fmt.Sprintf("%s@%s%s", .ModulePath, .ResolvedVersion, )