Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / godoc / godoc.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/godoc/godoc.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/godoc/godoc.go
new file mode 100644 (file)
index 0000000..6d4d115
--- /dev/null
@@ -0,0 +1,939 @@
+// Copyright 2013 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 godoc is a work-in-progress (2013-07-17) package to
+// begin splitting up the godoc binary into multiple pieces.
+//
+// This package comment will evolve over time as this package splits
+// into smaller pieces.
+package godoc // import "golang.org/x/tools/godoc"
+
+import (
+       "bufio"
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/doc"
+       "go/format"
+       "go/printer"
+       "go/token"
+       htmltemplate "html/template"
+       "io"
+       "log"
+       "os"
+       pathpkg "path"
+       "regexp"
+       "strconv"
+       "strings"
+       "text/template"
+       "time"
+       "unicode"
+       "unicode/utf8"
+)
+
+// Fake relative package path for built-ins. Documentation for all globals
+// (not just exported ones) will be shown for packages in this directory,
+// and there will be no association of consts, vars, and factory functions
+// with types (see issue 6645).
+const builtinPkgPath = "builtin"
+
+// FuncMap defines template functions used in godoc templates.
+//
+// Convention: template function names ending in "_html" or "_url" produce
+//             HTML- or URL-escaped strings; all other function results may
+//             require explicit escaping in the template.
+func (p *Presentation) FuncMap() template.FuncMap {
+       p.initFuncMapOnce.Do(p.initFuncMap)
+       return p.funcMap
+}
+
+func (p *Presentation) TemplateFuncs() template.FuncMap {
+       p.initFuncMapOnce.Do(p.initFuncMap)
+       return p.templateFuncs
+}
+
+func (p *Presentation) initFuncMap() {
+       if p.Corpus == nil {
+               panic("nil Presentation.Corpus")
+       }
+       p.templateFuncs = template.FuncMap{
+               "code": p.code,
+       }
+       p.funcMap = template.FuncMap{
+               // various helpers
+               "filename": filenameFunc,
+               "repeat":   strings.Repeat,
+               "since":    p.Corpus.pkgAPIInfo.sinceVersionFunc,
+
+               // access to FileInfos (directory listings)
+               "fileInfoName": fileInfoNameFunc,
+               "fileInfoTime": fileInfoTimeFunc,
+
+               // access to search result information
+               "infoKind_html":    infoKind_htmlFunc,
+               "infoLine":         p.infoLineFunc,
+               "infoSnippet_html": p.infoSnippet_htmlFunc,
+
+               // formatting of AST nodes
+               "node":         p.nodeFunc,
+               "node_html":    p.node_htmlFunc,
+               "comment_html": comment_htmlFunc,
+               "sanitize":     sanitizeFunc,
+
+               // support for URL attributes
+               "pkgLink":       pkgLinkFunc,
+               "srcLink":       srcLinkFunc,
+               "posLink_url":   newPosLink_urlFunc(srcPosLinkFunc),
+               "docLink":       docLinkFunc,
+               "queryLink":     queryLinkFunc,
+               "srcBreadcrumb": srcBreadcrumbFunc,
+               "srcToPkgLink":  srcToPkgLinkFunc,
+
+               // formatting of Examples
+               "example_html":   p.example_htmlFunc,
+               "example_name":   p.example_nameFunc,
+               "example_suffix": p.example_suffixFunc,
+
+               // formatting of analysis information
+               "callgraph_html":  p.callgraph_htmlFunc,
+               "implements_html": p.implements_htmlFunc,
+               "methodset_html":  p.methodset_htmlFunc,
+
+               // formatting of Notes
+               "noteTitle": noteTitle,
+
+               // Number operation
+               "multiply": multiply,
+
+               // formatting of PageInfoMode query string
+               "modeQueryString": modeQueryString,
+
+               // check whether to display third party section or not
+               "hasThirdParty": hasThirdParty,
+
+               // get the no. of columns to split the toc in search page
+               "tocColCount": tocColCount,
+       }
+       if p.URLForSrc != nil {
+               p.funcMap["srcLink"] = p.URLForSrc
+       }
+       if p.URLForSrcPos != nil {
+               p.funcMap["posLink_url"] = newPosLink_urlFunc(p.URLForSrcPos)
+       }
+       if p.URLForSrcQuery != nil {
+               p.funcMap["queryLink"] = p.URLForSrcQuery
+       }
+}
+
+func multiply(a, b int) int { return a * b }
+
+func filenameFunc(path string) string {
+       _, localname := pathpkg.Split(path)
+       return localname
+}
+
+func fileInfoNameFunc(fi os.FileInfo) string {
+       name := fi.Name()
+       if fi.IsDir() {
+               name += "/"
+       }
+       return name
+}
+
+func fileInfoTimeFunc(fi os.FileInfo) string {
+       if t := fi.ModTime(); t.Unix() != 0 {
+               return t.Local().String()
+       }
+       return "" // don't return epoch if time is obviously not set
+}
+
+// The strings in infoKinds must be properly html-escaped.
+var infoKinds = [nKinds]string{
+       PackageClause: "package clause",
+       ImportDecl:    "import decl",
+       ConstDecl:     "const decl",
+       TypeDecl:      "type decl",
+       VarDecl:       "var decl",
+       FuncDecl:      "func decl",
+       MethodDecl:    "method decl",
+       Use:           "use",
+}
+
+func infoKind_htmlFunc(info SpotInfo) string {
+       return infoKinds[info.Kind()] // infoKind entries are html-escaped
+}
+
+func (p *Presentation) infoLineFunc(info SpotInfo) int {
+       line := info.Lori()
+       if info.IsIndex() {
+               index, _ := p.Corpus.searchIndex.Get()
+               if index != nil {
+                       line = index.(*Index).Snippet(line).Line
+               } else {
+                       // no line information available because
+                       // we don't have an index - this should
+                       // never happen; be conservative and don't
+                       // crash
+                       line = 0
+               }
+       }
+       return line
+}
+
+func (p *Presentation) infoSnippet_htmlFunc(info SpotInfo) string {
+       if info.IsIndex() {
+               index, _ := p.Corpus.searchIndex.Get()
+               // Snippet.Text was HTML-escaped when it was generated
+               return index.(*Index).Snippet(info.Lori()).Text
+       }
+       return `<span class="alert">no snippet text available</span>`
+}
+
+func (p *Presentation) nodeFunc(info *PageInfo, node interface{}) string {
+       var buf bytes.Buffer
+       p.writeNode(&buf, info, info.FSet, node)
+       return buf.String()
+}
+
+func (p *Presentation) node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string {
+       var buf1 bytes.Buffer
+       p.writeNode(&buf1, info, info.FSet, node)
+
+       var buf2 bytes.Buffer
+       if n, _ := node.(ast.Node); n != nil && linkify && p.DeclLinks {
+               LinkifyText(&buf2, buf1.Bytes(), n)
+               if st, name := isStructTypeDecl(n); st != nil {
+                       addStructFieldIDAttributes(&buf2, name, st)
+               }
+       } else {
+               FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
+       }
+
+       return buf2.String()
+}
+
+// isStructTypeDecl checks whether n is a struct declaration.
+// It either returns a non-nil StructType and its name, or zero values.
+func isStructTypeDecl(n ast.Node) (st *ast.StructType, name string) {
+       gd, ok := n.(*ast.GenDecl)
+       if !ok || gd.Tok != token.TYPE {
+               return nil, ""
+       }
+       if gd.Lparen > 0 {
+               // Parenthesized type. Who does that, anyway?
+               // TODO: Reportedly gri does. Fix this to handle that too.
+               return nil, ""
+       }
+       if len(gd.Specs) != 1 {
+               return nil, ""
+       }
+       ts, ok := gd.Specs[0].(*ast.TypeSpec)
+       if !ok {
+               return nil, ""
+       }
+       st, ok = ts.Type.(*ast.StructType)
+       if !ok {
+               return nil, ""
+       }
+       return st, ts.Name.Name
+}
+
+// addStructFieldIDAttributes modifies the contents of buf such that
+// all struct fields of the named struct have <span id='name.Field'>
+// in them, so people can link to /#Struct.Field.
+func addStructFieldIDAttributes(buf *bytes.Buffer, name string, st *ast.StructType) {
+       if st.Fields == nil {
+               return
+       }
+       // needsLink is a set of identifiers that still need to be
+       // linked, where value == key, to avoid an allocation in func
+       // linkedField.
+       needsLink := make(map[string]string)
+
+       for _, f := range st.Fields.List {
+               if len(f.Names) == 0 {
+                       continue
+               }
+               fieldName := f.Names[0].Name
+               needsLink[fieldName] = fieldName
+       }
+       var newBuf bytes.Buffer
+       foreachLine(buf.Bytes(), func(line []byte) {
+               if fieldName := linkedField(line, needsLink); fieldName != "" {
+                       fmt.Fprintf(&newBuf, `<span id="%s.%s"></span>`, name, fieldName)
+                       delete(needsLink, fieldName)
+               }
+               newBuf.Write(line)
+       })
+       buf.Reset()
+       buf.Write(newBuf.Bytes())
+}
+
+// foreachLine calls fn for each line of in, where a line includes
+// the trailing "\n", except on the last line, if it doesn't exist.
+func foreachLine(in []byte, fn func(line []byte)) {
+       for len(in) > 0 {
+               nl := bytes.IndexByte(in, '\n')
+               if nl == -1 {
+                       fn(in)
+                       return
+               }
+               fn(in[:nl+1])
+               in = in[nl+1:]
+       }
+}
+
+// commentPrefix is the line prefix for comments after they've been HTMLified.
+var commentPrefix = []byte(`<span class="comment">// `)
+
+// linkedField determines whether the given line starts with an
+// identifier in the provided ids map (mapping from identifier to the
+// same identifier). The line can start with either an identifier or
+// an identifier in a comment. If one matches, it returns the
+// identifier that matched. Otherwise it returns the empty string.
+func linkedField(line []byte, ids map[string]string) string {
+       line = bytes.TrimSpace(line)
+
+       // For fields with a doc string of the
+       // conventional form, we put the new span into
+       // the comment instead of the field.
+       // The "conventional" form is a complete sentence
+       // per https://golang.org/s/style#comment-sentences like:
+       //
+       //    // Foo is an optional Fooer to foo the foos.
+       //    Foo Fooer
+       //
+       // In this case, we want the #StructName.Foo
+       // link to make the browser go to the comment
+       // line "Foo is an optional Fooer" instead of
+       // the "Foo Fooer" line, which could otherwise
+       // obscure the docs above the browser's "fold".
+       //
+       // TODO: do this better, so it works for all
+       // comments, including unconventional ones.
+       line = bytes.TrimPrefix(line, commentPrefix)
+       id := scanIdentifier(line)
+       if len(id) == 0 {
+               // No leading identifier. Avoid map lookup for
+               // somewhat common case.
+               return ""
+       }
+       return ids[string(id)]
+}
+
+// scanIdentifier scans a valid Go identifier off the front of v and
+// either returns a subslice of v if there's a valid identifier, or
+// returns a zero-length slice.
+func scanIdentifier(v []byte) []byte {
+       var n int // number of leading bytes of v belonging to an identifier
+       for {
+               r, width := utf8.DecodeRune(v[n:])
+               if !(isLetter(r) || n > 0 && isDigit(r)) {
+                       break
+               }
+               n += width
+       }
+       return v[:n]
+}
+
+func isLetter(ch rune) bool {
+       return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
+}
+
+func isDigit(ch rune) bool {
+       return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
+}
+
+func comment_htmlFunc(comment string) string {
+       var buf bytes.Buffer
+       // TODO(gri) Provide list of words (e.g. function parameters)
+       //           to be emphasized by ToHTML.
+       doc.ToHTML(&buf, comment, nil) // does html-escaping
+       return buf.String()
+}
+
+// sanitizeFunc sanitizes the argument src by replacing newlines with
+// blanks, removing extra blanks, and by removing trailing whitespace
+// and commas before closing parentheses.
+func sanitizeFunc(src string) string {
+       buf := make([]byte, len(src))
+       j := 0      // buf index
+       comma := -1 // comma index if >= 0
+       for i := 0; i < len(src); i++ {
+               ch := src[i]
+               switch ch {
+               case '\t', '\n', ' ':
+                       // ignore whitespace at the beginning, after a blank, or after opening parentheses
+                       if j == 0 {
+                               continue
+                       }
+                       if p := buf[j-1]; p == ' ' || p == '(' || p == '{' || p == '[' {
+                               continue
+                       }
+                       // replace all whitespace with blanks
+                       ch = ' '
+               case ',':
+                       comma = j
+               case ')', '}', ']':
+                       // remove any trailing comma
+                       if comma >= 0 {
+                               j = comma
+                       }
+                       // remove any trailing whitespace
+                       if j > 0 && buf[j-1] == ' ' {
+                               j--
+                       }
+               default:
+                       comma = -1
+               }
+               buf[j] = ch
+               j++
+       }
+       // remove trailing blank, if any
+       if j > 0 && buf[j-1] == ' ' {
+               j--
+       }
+       return string(buf[:j])
+}
+
+type PageInfo struct {
+       Dirname  string // directory containing the package
+       Err      error  // error or nil
+       GoogleCN bool   // page is being served from golang.google.cn
+
+       Mode PageInfoMode // display metadata from query string
+
+       // package info
+       FSet       *token.FileSet         // nil if no package documentation
+       PDoc       *doc.Package           // nil if no package documentation
+       Examples   []*doc.Example         // nil if no example code
+       Notes      map[string][]*doc.Note // nil if no package Notes
+       PAst       map[string]*ast.File   // nil if no AST with package exports
+       IsMain     bool                   // true for package main
+       IsFiltered bool                   // true if results were filtered
+
+       // analysis info
+       TypeInfoIndex  map[string]int  // index of JSON datum for type T (if -analysis=type)
+       AnalysisData   htmltemplate.JS // array of TypeInfoJSON values
+       CallGraph      htmltemplate.JS // array of PCGNodeJSON values    (if -analysis=pointer)
+       CallGraphIndex map[string]int  // maps func name to index in CallGraph
+
+       // directory info
+       Dirs    *DirList  // nil if no directory information
+       DirTime time.Time // directory time stamp
+       DirFlat bool      // if set, show directory in a flat (non-indented) manner
+}
+
+func (info *PageInfo) IsEmpty() bool {
+       return info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil
+}
+
+func pkgLinkFunc(path string) string {
+       // because of the irregular mapping under goroot
+       // we need to correct certain relative paths
+       path = strings.TrimPrefix(path, "/")
+       path = strings.TrimPrefix(path, "src/")
+       path = strings.TrimPrefix(path, "pkg/")
+       return "pkg/" + path
+}
+
+// srcToPkgLinkFunc builds an <a> tag linking to the package
+// documentation of relpath.
+func srcToPkgLinkFunc(relpath string) string {
+       relpath = pkgLinkFunc(relpath)
+       relpath = pathpkg.Dir(relpath)
+       if relpath == "pkg" {
+               return `<a href="/pkg">Index</a>`
+       }
+       return fmt.Sprintf(`<a href="/%s">%s</a>`, relpath, relpath[len("pkg/"):])
+}
+
+// srcBreadcrumbFun converts each segment of relpath to a HTML <a>.
+// Each segment links to its corresponding src directories.
+func srcBreadcrumbFunc(relpath string) string {
+       segments := strings.Split(relpath, "/")
+       var buf bytes.Buffer
+       var selectedSegment string
+       var selectedIndex int
+
+       if strings.HasSuffix(relpath, "/") {
+               // relpath is a directory ending with a "/".
+               // Selected segment is the segment before the last slash.
+               selectedIndex = len(segments) - 2
+               selectedSegment = segments[selectedIndex] + "/"
+       } else {
+               selectedIndex = len(segments) - 1
+               selectedSegment = segments[selectedIndex]
+       }
+
+       for i := range segments[:selectedIndex] {
+               buf.WriteString(fmt.Sprintf(`<a href="/%s">%s</a>/`,
+                       strings.Join(segments[:i+1], "/"),
+                       segments[i],
+               ))
+       }
+
+       buf.WriteString(`<span class="text-muted">`)
+       buf.WriteString(selectedSegment)
+       buf.WriteString(`</span>`)
+       return buf.String()
+}
+
+func newPosLink_urlFunc(srcPosLinkFunc func(s string, line, low, high int) string) func(info *PageInfo, n interface{}) string {
+       // n must be an ast.Node or a *doc.Note
+       return func(info *PageInfo, n interface{}) string {
+               var pos, end token.Pos
+
+               switch n := n.(type) {
+               case ast.Node:
+                       pos = n.Pos()
+                       end = n.End()
+               case *doc.Note:
+                       pos = n.Pos
+                       end = n.End
+               default:
+                       panic(fmt.Sprintf("wrong type for posLink_url template formatter: %T", n))
+               }
+
+               var relpath string
+               var line int
+               var low, high int // selection offset range
+
+               if pos.IsValid() {
+                       p := info.FSet.Position(pos)
+                       relpath = p.Filename
+                       line = p.Line
+                       low = p.Offset
+               }
+               if end.IsValid() {
+                       high = info.FSet.Position(end).Offset
+               }
+
+               return srcPosLinkFunc(relpath, line, low, high)
+       }
+}
+
+func srcPosLinkFunc(s string, line, low, high int) string {
+       s = srcLinkFunc(s)
+       var buf bytes.Buffer
+       template.HTMLEscape(&buf, []byte(s))
+       // selection ranges are of form "s=low:high"
+       if low < high {
+               fmt.Fprintf(&buf, "?s=%d:%d", low, high) // no need for URL escaping
+               // if we have a selection, position the page
+               // such that the selection is a bit below the top
+               line -= 10
+               if line < 1 {
+                       line = 1
+               }
+       }
+       // line id's in html-printed source are of the
+       // form "L%d" where %d stands for the line number
+       if line > 0 {
+               fmt.Fprintf(&buf, "#L%d", line) // no need for URL escaping
+       }
+       return buf.String()
+}
+
+func srcLinkFunc(s string) string {
+       s = pathpkg.Clean("/" + s)
+       if !strings.HasPrefix(s, "/src/") {
+               s = "/src" + s
+       }
+       return s
+}
+
+// queryLinkFunc returns a URL for a line in a source file with a highlighted
+// query term.
+// s is expected to be a path to a source file.
+// query is expected to be a string that has already been appropriately escaped
+// for use in a URL query.
+func queryLinkFunc(s, query string, line int) string {
+       url := pathpkg.Clean("/"+s) + "?h=" + query
+       if line > 0 {
+               url += "#L" + strconv.Itoa(line)
+       }
+       return url
+}
+
+func docLinkFunc(s string, ident string) string {
+       return pathpkg.Clean("/pkg/"+s) + "/#" + ident
+}
+
+func (p *Presentation) example_htmlFunc(info *PageInfo, funcName string) string {
+       var buf bytes.Buffer
+       for _, eg := range info.Examples {
+               name := stripExampleSuffix(eg.Name)
+
+               if name != funcName {
+                       continue
+               }
+
+               // print code
+               cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
+               code := p.node_htmlFunc(info, cnode, true)
+               out := eg.Output
+               wholeFile := true
+
+               // Additional formatting if this is a function body.
+               if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
+                       wholeFile = false
+                       // remove surrounding braces
+                       code = code[1 : n-1]
+                       // unindent
+                       code = replaceLeadingIndentation(code, strings.Repeat(" ", p.TabWidth), "")
+                       // remove output comment
+                       if loc := exampleOutputRx.FindStringIndex(code); loc != nil {
+                               code = strings.TrimSpace(code[:loc[0]])
+                       }
+               }
+
+               // Write out the playground code in standard Go style
+               // (use tabs, no comment highlight, etc).
+               play := ""
+               if eg.Play != nil && p.ShowPlayground {
+                       var buf bytes.Buffer
+                       eg.Play.Comments = filterOutBuildAnnotations(eg.Play.Comments)
+                       if err := format.Node(&buf, info.FSet, eg.Play); err != nil {
+                               log.Print(err)
+                       } else {
+                               play = buf.String()
+                       }
+               }
+
+               // Drop output, as the output comment will appear in the code.
+               if wholeFile && play == "" {
+                       out = ""
+               }
+
+               if p.ExampleHTML == nil {
+                       out = ""
+                       return ""
+               }
+
+               err := p.ExampleHTML.Execute(&buf, struct {
+                       Name, Doc, Code, Play, Output string
+                       GoogleCN                      bool
+               }{eg.Name, eg.Doc, code, play, out, info.GoogleCN})
+               if err != nil {
+                       log.Print(err)
+               }
+       }
+       return buf.String()
+}
+
+func filterOutBuildAnnotations(cg []*ast.CommentGroup) []*ast.CommentGroup {
+       if len(cg) == 0 {
+               return cg
+       }
+
+       for i := range cg {
+               if !strings.HasPrefix(cg[i].Text(), "+build ") {
+                       // Found the first non-build tag, return from here until the end
+                       // of the slice.
+                       return cg[i:]
+               }
+       }
+
+       // There weren't any non-build tags, return an empty slice.
+       return []*ast.CommentGroup{}
+}
+
+// example_nameFunc takes an example function name and returns its display
+// name. For example, "Foo_Bar_quux" becomes "Foo.Bar (Quux)".
+func (p *Presentation) example_nameFunc(s string) string {
+       name, suffix := splitExampleName(s)
+       // replace _ with . for method names
+       name = strings.Replace(name, "_", ".", 1)
+       // use "Package" if no name provided
+       if name == "" {
+               name = "Package"
+       }
+       return name + suffix
+}
+
+// example_suffixFunc takes an example function name and returns its suffix in
+// parenthesized form. For example, "Foo_Bar_quux" becomes " (Quux)".
+func (p *Presentation) example_suffixFunc(name string) string {
+       _, suffix := splitExampleName(name)
+       return suffix
+}
+
+// implements_html returns the "> Implements" toggle for a package-level named type.
+// Its contents are populated from JSON data by client-side JS at load time.
+func (p *Presentation) implements_htmlFunc(info *PageInfo, typeName string) string {
+       if p.ImplementsHTML == nil {
+               return ""
+       }
+       index, ok := info.TypeInfoIndex[typeName]
+       if !ok {
+               return ""
+       }
+       var buf bytes.Buffer
+       err := p.ImplementsHTML.Execute(&buf, struct{ Index int }{index})
+       if err != nil {
+               log.Print(err)
+       }
+       return buf.String()
+}
+
+// methodset_html returns the "> Method set" toggle for a package-level named type.
+// Its contents are populated from JSON data by client-side JS at load time.
+func (p *Presentation) methodset_htmlFunc(info *PageInfo, typeName string) string {
+       if p.MethodSetHTML == nil {
+               return ""
+       }
+       index, ok := info.TypeInfoIndex[typeName]
+       if !ok {
+               return ""
+       }
+       var buf bytes.Buffer
+       err := p.MethodSetHTML.Execute(&buf, struct{ Index int }{index})
+       if err != nil {
+               log.Print(err)
+       }
+       return buf.String()
+}
+
+// callgraph_html returns the "> Call graph" toggle for a package-level func.
+// Its contents are populated from JSON data by client-side JS at load time.
+func (p *Presentation) callgraph_htmlFunc(info *PageInfo, recv, name string) string {
+       if p.CallGraphHTML == nil {
+               return ""
+       }
+       if recv != "" {
+               // Format must match (*ssa.Function).RelString().
+               name = fmt.Sprintf("(%s).%s", recv, name)
+       }
+       index, ok := info.CallGraphIndex[name]
+       if !ok {
+               return ""
+       }
+       var buf bytes.Buffer
+       err := p.CallGraphHTML.Execute(&buf, struct{ Index int }{index})
+       if err != nil {
+               log.Print(err)
+       }
+       return buf.String()
+}
+
+func noteTitle(note string) string {
+       return strings.Title(strings.ToLower(note))
+}
+
+func startsWithUppercase(s string) bool {
+       r, _ := utf8.DecodeRuneInString(s)
+       return unicode.IsUpper(r)
+}
+
+var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*(unordered )?output:`)
+
+// stripExampleSuffix strips lowercase braz in Foo_braz or Foo_Bar_braz from name
+// while keeping uppercase Braz in Foo_Braz.
+func stripExampleSuffix(name string) string {
+       if i := strings.LastIndex(name, "_"); i != -1 {
+               if i < len(name)-1 && !startsWithUppercase(name[i+1:]) {
+                       name = name[:i]
+               }
+       }
+       return name
+}
+
+func splitExampleName(s string) (name, suffix string) {
+       i := strings.LastIndex(s, "_")
+       if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) {
+               name = s[:i]
+               suffix = " (" + strings.Title(s[i+1:]) + ")"
+               return
+       }
+       name = s
+       return
+}
+
+// replaceLeadingIndentation replaces oldIndent at the beginning of each line
+// with newIndent. This is used for formatting examples. Raw strings that
+// span multiple lines are handled specially: oldIndent is not removed (since
+// go/printer will not add any indentation there), but newIndent is added
+// (since we may still want leading indentation).
+func replaceLeadingIndentation(body, oldIndent, newIndent string) string {
+       // Handle indent at the beginning of the first line. After this, we handle
+       // indentation only after a newline.
+       var buf bytes.Buffer
+       if strings.HasPrefix(body, oldIndent) {
+               buf.WriteString(newIndent)
+               body = body[len(oldIndent):]
+       }
+
+       // Use a state machine to keep track of whether we're in a string or
+       // rune literal while we process the rest of the code.
+       const (
+               codeState = iota
+               runeState
+               interpretedStringState
+               rawStringState
+       )
+       searchChars := []string{
+               "'\"`\n", // codeState
+               `\'`,     // runeState
+               `\"`,     // interpretedStringState
+               "`\n",    // rawStringState
+               // newlineState does not need to search
+       }
+       state := codeState
+       for {
+               i := strings.IndexAny(body, searchChars[state])
+               if i < 0 {
+                       buf.WriteString(body)
+                       break
+               }
+               c := body[i]
+               buf.WriteString(body[:i+1])
+               body = body[i+1:]
+               switch state {
+               case codeState:
+                       switch c {
+                       case '\'':
+                               state = runeState
+                       case '"':
+                               state = interpretedStringState
+                       case '`':
+                               state = rawStringState
+                       case '\n':
+                               if strings.HasPrefix(body, oldIndent) {
+                                       buf.WriteString(newIndent)
+                                       body = body[len(oldIndent):]
+                               }
+                       }
+
+               case runeState:
+                       switch c {
+                       case '\\':
+                               r, size := utf8.DecodeRuneInString(body)
+                               buf.WriteRune(r)
+                               body = body[size:]
+                       case '\'':
+                               state = codeState
+                       }
+
+               case interpretedStringState:
+                       switch c {
+                       case '\\':
+                               r, size := utf8.DecodeRuneInString(body)
+                               buf.WriteRune(r)
+                               body = body[size:]
+                       case '"':
+                               state = codeState
+                       }
+
+               case rawStringState:
+                       switch c {
+                       case '`':
+                               state = codeState
+                       case '\n':
+                               buf.WriteString(newIndent)
+                       }
+               }
+       }
+       return buf.String()
+}
+
+// writeNode writes the AST node x to w.
+//
+// The provided fset must be non-nil. The pageInfo is optional. If
+// present, the pageInfo is used to add comments to struct fields to
+// say which version of Go introduced them.
+func (p *Presentation) writeNode(w io.Writer, pageInfo *PageInfo, fset *token.FileSet, x interface{}) {
+       // convert trailing tabs into spaces using a tconv filter
+       // to ensure a good outcome in most browsers (there may still
+       // be tabs in comments and strings, but converting those into
+       // the right number of spaces is much harder)
+       //
+       // TODO(gri) rethink printer flags - perhaps tconv can be eliminated
+       //           with an another printer mode (which is more efficiently
+       //           implemented in the printer than here with another layer)
+
+       var pkgName, structName string
+       var apiInfo pkgAPIVersions
+       if gd, ok := x.(*ast.GenDecl); ok && pageInfo != nil && pageInfo.PDoc != nil &&
+               p.Corpus != nil &&
+               gd.Tok == token.TYPE && len(gd.Specs) != 0 {
+               pkgName = pageInfo.PDoc.ImportPath
+               if ts, ok := gd.Specs[0].(*ast.TypeSpec); ok {
+                       if _, ok := ts.Type.(*ast.StructType); ok {
+                               structName = ts.Name.Name
+                       }
+               }
+               apiInfo = p.Corpus.pkgAPIInfo[pkgName]
+       }
+
+       var out = w
+       var buf bytes.Buffer
+       if structName != "" {
+               out = &buf
+       }
+
+       mode := printer.TabIndent | printer.UseSpaces
+       err := (&printer.Config{Mode: mode, Tabwidth: p.TabWidth}).Fprint(&tconv{p: p, output: out}, fset, x)
+       if err != nil {
+               log.Print(err)
+       }
+
+       // Add comments to struct fields saying which Go version introduced them.
+       if structName != "" {
+               fieldSince := apiInfo.fieldSince[structName]
+               typeSince := apiInfo.typeSince[structName]
+               // Add/rewrite comments on struct fields to note which Go version added them.
+               var buf2 bytes.Buffer
+               buf2.Grow(buf.Len() + len(" // Added in Go 1.n")*10)
+               bs := bufio.NewScanner(&buf)
+               for bs.Scan() {
+                       line := bs.Bytes()
+                       field := firstIdent(line)
+                       var since string
+                       if field != "" {
+                               since = fieldSince[field]
+                               if since != "" && since == typeSince {
+                                       // Don't highlight field versions if they were the
+                                       // same as the struct itself.
+                                       since = ""
+                               }
+                       }
+                       if since == "" {
+                               buf2.Write(line)
+                       } else {
+                               if bytes.Contains(line, slashSlash) {
+                                       line = bytes.TrimRight(line, " \t.")
+                                       buf2.Write(line)
+                                       buf2.WriteString("; added in Go ")
+                               } else {
+                                       buf2.Write(line)
+                                       buf2.WriteString(" // Go ")
+                               }
+                               buf2.WriteString(since)
+                       }
+                       buf2.WriteByte('\n')
+               }
+               w.Write(buf2.Bytes())
+       }
+}
+
+var slashSlash = []byte("//")
+
+// WriteNode writes x to w.
+// TODO(bgarcia) Is this method needed? It's just a wrapper for p.writeNode.
+func (p *Presentation) WriteNode(w io.Writer, fset *token.FileSet, x interface{}) {
+       p.writeNode(w, nil, fset, x)
+}
+
+// firstIdent returns the first identifier in x.
+// This actually parses "identifiers" that begin with numbers too, but we
+// never feed it such input, so it's fine.
+func firstIdent(x []byte) string {
+       x = bytes.TrimSpace(x)
+       i := bytes.IndexFunc(x, func(r rune) bool { return !unicode.IsLetter(r) && !unicode.IsNumber(r) })
+       if i == -1 {
+               return string(x)
+       }
+       return string(x[:i])
+}