internal/godoc,fetch: move doc rendering to godoc package

Write a Render method in the godoc package that does all
doc rendering.

Modify the internal/fetch package to use it.

This required moving the dochtml and internal/doc packages under
internal/godoc.

It's redundant to have the set of module package paths in both
godoc.Package and dochtml.ModuleInfo, but we'll leave that
for now to keep the number of changes to a minimum.

The new Render method is effectively tested by TestFetchModule
in internal/fetch.

Change-Id: I655451386c55c883682eae4277d2d5b2d9c4fc3b
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/258423
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/fetch/fetch_test.go b/internal/fetch/fetch_test.go
index afe50be..b7010c2 100644
--- a/internal/fetch/fetch_test.go
+++ b/internal/fetch/fetch_test.go
@@ -17,6 +17,7 @@
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/derrors"
+	"golang.org/x/pkgsite/internal/godoc"
 	"golang.org/x/pkgsite/internal/proxy"
 	"golang.org/x/pkgsite/internal/source"
 	"golang.org/x/pkgsite/internal/stdlib"
@@ -40,8 +41,8 @@
 	}
 	defer func() { httpPost = origPost }()
 
-	defer func(oldmax int) { MaxDocumentationHTML = oldmax }(MaxDocumentationHTML)
-	MaxDocumentationHTML = 1 * megabyte
+	defer func(oldmax int) { godoc.MaxDocumentationHTML = oldmax }(godoc.MaxDocumentationHTML)
+	godoc.MaxDocumentationHTML = 1 * megabyte
 
 	for _, test := range []struct {
 		name         string
diff --git a/internal/fetch/limit.go b/internal/fetch/limit.go
index 436862e..d929449 100644
--- a/internal/fetch/limit.go
+++ b/internal/fetch/limit.go
@@ -15,12 +15,4 @@
 	MaxFileSize = 30 * megabyte
 )
 
-// MaxDocumentationHTML is a limit on the rendered documentation HTML size.
-//
-// The current limit of is based on the largest packages that
-// pkg.go.dev has encountered. See https://golang.org/issue/40576.
-//
-// It is a variable for testing.
-var MaxDocumentationHTML = 20 * megabyte
-
 const megabyte = 1000 * 1000
diff --git a/internal/fetch/load.go b/internal/fetch/load.go
index 287c7fe..9c4f836 100644
--- a/internal/fetch/load.go
+++ b/internal/fetch/load.go
@@ -22,18 +22,13 @@
 	"os"
 	"path"
 	"runtime"
-	"sort"
 	"strings"
 
-	"github.com/google/safehtml"
-	"github.com/google/safehtml/template"
 	"go.opencensus.io/trace"
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/config"
 	"golang.org/x/pkgsite/internal/derrors"
 	"golang.org/x/pkgsite/internal/experiment"
-	"golang.org/x/pkgsite/internal/fetch/dochtml"
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
 	"golang.org/x/pkgsite/internal/godoc"
 	"golang.org/x/pkgsite/internal/log"
 	"golang.org/x/pkgsite/internal/source"
@@ -66,14 +61,14 @@
 // loadPackage returns nil, nil.
 //
 // If the package is fine except that its documentation is too large, loadPackage
-// returns both a package and a non-nil error with dochtml.ErrTooLarge in its chain.
-func loadPackage(ctx context.Context, zipGoFiles []*zip.File, innerPath string, sourceInfo *source.Info, modInfo *dochtml.ModuleInfo) (_ *goPackage, err error) {
+// returns both a package and a non-nil error with godoc.ErrTooLarge in its chain.
+func loadPackage(ctx context.Context, zipGoFiles []*zip.File, innerPath string, sourceInfo *source.Info, modInfo *godoc.ModuleInfo) (_ *goPackage, err error) {
 	defer derrors.Wrap(&err, "loadPackage(ctx, zipGoFiles, %q, sourceInfo, modInfo)", innerPath)
 	ctx, span := trace.StartSpan(ctx, "fetch.loadPackage")
 	defer span.End()
 	for _, env := range goEnvs {
 		pkg, err := loadPackageWithBuildContext(ctx, env.GOOS, env.GOARCH, zipGoFiles, innerPath, sourceInfo, modInfo)
-		if err != nil && !errors.Is(err, dochtml.ErrTooLarge) && !errors.Is(err, derrors.NotFound) {
+		if err != nil && !errors.Is(err, godoc.ErrTooLarge) && !errors.Is(err, derrors.NotFound) {
 			return nil, err
 		}
 		if pkg != nil {
@@ -103,7 +98,7 @@
 // or all .go files have been excluded by constraints.
 // A *BadPackageError error is returned if the directory
 // contains .go files but do not make up a valid package.
-func loadPackageWithBuildContext(ctx context.Context, goos, goarch string, zipGoFiles []*zip.File, innerPath string, sourceInfo *source.Info, modInfo *dochtml.ModuleInfo) (_ *goPackage, err error) {
+func loadPackageWithBuildContext(ctx context.Context, goos, goarch string, zipGoFiles []*zip.File, innerPath string, sourceInfo *source.Info, modInfo *godoc.ModuleInfo) (_ *goPackage, err error) {
 	modulePath := modInfo.ModulePath
 	defer derrors.Wrap(&err, "loadPackageWithBuildContext(%q, %q, zipGoFiles, %q, %q, %+v)",
 		goos, goarch, innerPath, modulePath, sourceInfo)
@@ -113,25 +108,21 @@
 		return nil, err
 	}
 	docPkg := godoc.NewPackage(fset, modInfo.ModulePackages)
-	var allGoFiles []*ast.File
 	for _, pf := range goFiles {
+		var removeNodes bool
 		if experiment.IsActive(ctx, internal.ExperimentRemoveUnusedAST) {
-			removeNodes := true
+			removeNodes = true
 			// Don't strip the seemingly unexported functions from the builtin package;
 			// they are actually Go builtins like make, new, etc.
 			if !(modulePath == stdlib.ModulePath && innerPath == "builtin") {
 				removeNodes = false
 			}
-			docPkg.AddFile(pf, removeNodes)
 		}
-		allGoFiles = append(allGoFiles, pf)
+		docPkg.AddFile(pf, removeNodes)
 	}
-	d, err := loadPackageWithFiles(modulePath, innerPath, packageName, allGoFiles, fset)
-	if err != nil {
-		return nil, err
-	}
-	docHTML, err := renderDocHTML(ctx, innerPath, d, fset, sourceInfo, modInfo)
-	if err != nil && !errors.Is(err, dochtml.ErrTooLarge) {
+
+	synopsis, imports, docHTML, err := docPkg.Render(ctx, innerPath, sourceInfo, modInfo, goos, goarch)
+	if err != nil && !errors.Is(err, godoc.ErrTooLarge) {
 		return nil, err
 	}
 	var src []byte
@@ -149,9 +140,9 @@
 	return &goPackage{
 		path:              importPath,
 		name:              packageName,
-		synopsis:          doc.Synopsis(d.Doc),
+		synopsis:          synopsis,
 		v1path:            v1path,
-		imports:           d.Imports,
+		imports:           imports,
 		documentationHTML: docHTML,
 		goos:              goos,
 		goarch:            goarch,
@@ -169,7 +160,6 @@
 	if err != nil {
 		return "", nil, nil, err
 	}
-
 	// Parse .go files and add them to the goFiles slice.
 	var (
 		fset            = token.NewFileSet()
@@ -214,81 +204,6 @@
 	return packageName, goFiles, fset, nil
 }
 
-func loadPackageWithFiles(modulePath, innerPath, packageName string, allGoFiles []*ast.File, fset *token.FileSet) (_ *doc.Package, err error) {
-	defer derrors.Wrap(&err, "loadPackageWithFiles")
-	// The "builtin" package in the standard library is a special case.
-	// We want to show documentation for all globals (not just exported ones),
-	// and avoid association of consts, vars, and factory functions with types
-	// since it's not helpful (see golang.org/issue/6645).
-	var noFiltering, noTypeAssociation bool
-	if modulePath == stdlib.ModulePath && innerPath == "builtin" {
-		noFiltering = true
-		noTypeAssociation = true
-	}
-
-	// Compute package documentation.
-	importPath := path.Join(modulePath, innerPath)
-	var m doc.Mode
-	if noFiltering {
-		m |= doc.AllDecls
-	}
-	d, err := doc.NewFromFiles(fset, allGoFiles, importPath, m)
-	if err != nil {
-		return nil, fmt.Errorf("doc.NewFromFiles: %v", err)
-	}
-	if d.ImportPath != importPath || d.Name != packageName {
-		panic(fmt.Errorf("internal error: *doc.Package has an unexpected import path (%q != %q) or package name (%q != %q)", d.ImportPath, importPath, d.Name, packageName))
-	}
-	if noTypeAssociation {
-		for _, t := range d.Types {
-			d.Consts, t.Consts = append(d.Consts, t.Consts...), nil
-			d.Vars, t.Vars = append(d.Vars, t.Vars...), nil
-			d.Funcs, t.Funcs = append(d.Funcs, t.Funcs...), nil
-		}
-		sort.Slice(d.Funcs, func(i, j int) bool { return d.Funcs[i].Name < d.Funcs[j].Name })
-	}
-
-	// Process package imports.
-	if len(d.Imports) > maxImportsPerPackage {
-		return nil, fmt.Errorf("%d imports found package %q; exceeds limit %d for maxImportsPerPackage", len(d.Imports), importPath, maxImportsPerPackage)
-	}
-	return d, nil
-}
-
-// renderDocHTML renders documentation HTML for a given package.
-func renderDocHTML(ctx context.Context, innerPath string, d *doc.Package, fset *token.FileSet, sourceInfo *source.Info, modInfo *dochtml.ModuleInfo) (_ safehtml.HTML, err error) {
-	defer derrors.Wrap(&err, "renderDocHTML")
-	sourceLinkFunc := func(n ast.Node) string {
-		if sourceInfo == nil {
-			return ""
-		}
-		p := fset.Position(n.Pos())
-		if p.Line == 0 { // invalid Position
-			return ""
-		}
-		return sourceInfo.LineURL(path.Join(innerPath, p.Filename), p.Line)
-	}
-	fileLinkFunc := func(filename string) string {
-		if sourceInfo == nil {
-			return ""
-		}
-		return sourceInfo.FileURL(path.Join(innerPath, filename))
-	}
-
-	docHTML, err := dochtml.Render(ctx, fset, d, dochtml.RenderOptions{
-		FileLinkFunc:   fileLinkFunc,
-		SourceLinkFunc: sourceLinkFunc,
-		ModInfo:        modInfo,
-		Limit:          int64(MaxDocumentationHTML),
-	})
-	if errors.Is(err, dochtml.ErrTooLarge) {
-		docHTML = template.MustParseAndExecuteToHTML(docTooLargeReplacement)
-	} else if err != nil {
-		return safehtml.HTML{}, err
-	}
-	return docHTML, err
-}
-
 // matchingFiles returns a map from file names to their contents, read from zipGoFiles.
 // It includes only those files that match the build context determined by goos and goarch.
 func matchingFiles(goos, goarch string, zipGoFiles []*zip.File) (files map[string][]byte, err error) {
diff --git a/internal/fetch/package.go b/internal/fetch/package.go
index cefd898..6645c10 100644
--- a/internal/fetch/package.go
+++ b/internal/fetch/package.go
@@ -20,7 +20,7 @@
 	"golang.org/x/mod/module"
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/derrors"
-	"golang.org/x/pkgsite/internal/fetch/dochtml"
+	"golang.org/x/pkgsite/internal/godoc"
 	"golang.org/x/pkgsite/internal/licenses"
 	"golang.org/x/pkgsite/internal/log"
 	"golang.org/x/pkgsite/internal/source"
@@ -94,7 +94,7 @@
 		// modInfo contains all the module information a package in the module
 		// needs to render its documentation, to be populated during phase 1
 		// and used during phase 2.
-		modInfo = &dochtml.ModuleInfo{
+		modInfo = &godoc.ModuleInfo{
 			ModulePath:      modulePath,
 			ResolvedVersion: resolvedVersion,
 			ModulePackages:  make(map[string]bool),
@@ -199,7 +199,7 @@
 			incompleteDirs[innerPath] = true
 			status = derrors.PackageInvalidContents
 			errMsg = err.Error()
-		} else if errors.Is(err, dochtml.ErrTooLarge) {
+		} else if errors.Is(err, godoc.ErrTooLarge) {
 			status = derrors.PackageDocumentationHTMLTooLarge
 			errMsg = err.Error()
 		} else if err != nil {
diff --git a/internal/fetch/dochtml/dochtml.go b/internal/godoc/dochtml/dochtml.go
similarity index 98%
rename from internal/fetch/dochtml/dochtml.go
rename to internal/godoc/dochtml/dochtml.go
index 3239ef6..c268abd 100644
--- a/internal/fetch/dochtml/dochtml.go
+++ b/internal/godoc/dochtml/dochtml.go
@@ -27,8 +27,8 @@
 	"github.com/google/safehtml/template"
 	"github.com/google/safehtml/uncheckedconversions"
 	"golang.org/x/pkgsite/internal/derrors"
-	"golang.org/x/pkgsite/internal/fetch/dochtml/internal/render"
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/dochtml/internal/render"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 )
 
 var (
diff --git a/internal/fetch/dochtml/dochtml_test.go b/internal/godoc/dochtml/dochtml_test.go
similarity index 99%
rename from internal/fetch/dochtml/dochtml_test.go
rename to internal/godoc/dochtml/dochtml_test.go
index 1b9baa5..c465371 100644
--- a/internal/fetch/dochtml/dochtml_test.go
+++ b/internal/godoc/dochtml/dochtml_test.go
@@ -18,7 +18,7 @@
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"golang.org/x/net/html"
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 	"golang.org/x/pkgsite/internal/testing/htmlcheck"
 )
 
diff --git a/internal/fetch/dochtml/internal/render/idents.go b/internal/godoc/dochtml/internal/render/idents.go
similarity index 99%
rename from internal/fetch/dochtml/internal/render/idents.go
rename to internal/godoc/dochtml/internal/render/idents.go
index 63e176d..b7911f1 100644
--- a/internal/fetch/dochtml/internal/render/idents.go
+++ b/internal/godoc/dochtml/internal/render/idents.go
@@ -13,7 +13,7 @@
 
 	"github.com/google/safehtml"
 	"github.com/google/safehtml/template"
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 )
 
 /*
diff --git a/internal/fetch/dochtml/internal/render/idents_test.go b/internal/godoc/dochtml/internal/render/idents_test.go
similarity index 98%
rename from internal/fetch/dochtml/internal/render/idents_test.go
rename to internal/godoc/dochtml/internal/render/idents_test.go
index 926df21..f89d464 100644
--- a/internal/fetch/dochtml/internal/render/idents_test.go
+++ b/internal/godoc/dochtml/internal/render/idents_test.go
@@ -8,7 +8,7 @@
 	"go/ast"
 	"testing"
 
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 )
 
 func TestResolveIdentifier(t *testing.T) {
diff --git a/internal/fetch/dochtml/internal/render/linkify.go b/internal/godoc/dochtml/internal/render/linkify.go
similarity index 99%
rename from internal/fetch/dochtml/internal/render/linkify.go
rename to internal/godoc/dochtml/internal/render/linkify.go
index b404678..34d9604 100644
--- a/internal/fetch/dochtml/internal/render/linkify.go
+++ b/internal/godoc/dochtml/internal/render/linkify.go
@@ -21,7 +21,7 @@
 	"github.com/google/safehtml"
 	"github.com/google/safehtml/legacyconversions"
 	safetemplate "github.com/google/safehtml/template"
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 	"golang.org/x/pkgsite/internal/log"
 )
 
diff --git a/internal/fetch/dochtml/internal/render/linkify_test.go b/internal/godoc/dochtml/internal/render/linkify_test.go
similarity index 99%
rename from internal/fetch/dochtml/internal/render/linkify_test.go
rename to internal/godoc/dochtml/internal/render/linkify_test.go
index 728e662..563743a 100644
--- a/internal/fetch/dochtml/internal/render/linkify_test.go
+++ b/internal/godoc/dochtml/internal/render/linkify_test.go
@@ -15,7 +15,7 @@
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/safehtml"
 	"github.com/google/safehtml/testconversions"
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 )
 
 func TestDocHTML(t *testing.T) {
diff --git a/internal/fetch/dochtml/internal/render/render.go b/internal/godoc/dochtml/internal/render/render.go
similarity index 99%
rename from internal/fetch/dochtml/internal/render/render.go
rename to internal/godoc/dochtml/internal/render/render.go
index 232df05..382cd49 100644
--- a/internal/fetch/dochtml/internal/render/render.go
+++ b/internal/godoc/dochtml/internal/render/render.go
@@ -14,7 +14,7 @@
 	"strings"
 
 	"github.com/google/safehtml"
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 )
 
 var (
diff --git a/internal/fetch/dochtml/internal/render/render_test.go b/internal/godoc/dochtml/internal/render/render_test.go
similarity index 98%
rename from internal/fetch/dochtml/internal/render/render_test.go
rename to internal/godoc/dochtml/internal/render/render_test.go
index 410fbac..b2e1517 100644
--- a/internal/fetch/dochtml/internal/render/render_test.go
+++ b/internal/godoc/dochtml/internal/render/render_test.go
@@ -14,7 +14,7 @@
 	"strings"
 	"testing"
 
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 )
 
 var (
diff --git a/internal/fetch/dochtml/internal/render/short_synopsis.go b/internal/godoc/dochtml/internal/render/short_synopsis.go
similarity index 100%
rename from internal/fetch/dochtml/internal/render/short_synopsis.go
rename to internal/godoc/dochtml/internal/render/short_synopsis.go
diff --git a/internal/fetch/dochtml/internal/render/short_synopsis_test.go b/internal/godoc/dochtml/internal/render/short_synopsis_test.go
similarity index 100%
rename from internal/fetch/dochtml/internal/render/short_synopsis_test.go
rename to internal/godoc/dochtml/internal/render/short_synopsis_test.go
diff --git a/internal/fetch/dochtml/internal/render/synopsis.go b/internal/godoc/dochtml/internal/render/synopsis.go
similarity index 100%
rename from internal/fetch/dochtml/internal/render/synopsis.go
rename to internal/godoc/dochtml/internal/render/synopsis.go
diff --git a/internal/fetch/dochtml/internal/render/synopsis_test.go b/internal/godoc/dochtml/internal/render/synopsis_test.go
similarity index 100%
rename from internal/fetch/dochtml/internal/render/synopsis_test.go
rename to internal/godoc/dochtml/internal/render/synopsis_test.go
diff --git a/internal/fetch/dochtml/internal/render/testdata/io.go b/internal/godoc/dochtml/internal/render/testdata/io.go
similarity index 100%
rename from internal/fetch/dochtml/internal/render/testdata/io.go
rename to internal/godoc/dochtml/internal/render/testdata/io.go
diff --git a/internal/fetch/dochtml/internal/render/testdata/os.go b/internal/godoc/dochtml/internal/render/testdata/os.go
similarity index 100%
rename from internal/fetch/dochtml/internal/render/testdata/os.go
rename to internal/godoc/dochtml/internal/render/testdata/os.go
diff --git a/internal/fetch/dochtml/internal/render/testdata/tar.go b/internal/godoc/dochtml/internal/render/testdata/tar.go
similarity index 100%
rename from internal/fetch/dochtml/internal/render/testdata/tar.go
rename to internal/godoc/dochtml/internal/render/testdata/tar.go
diff --git a/internal/fetch/dochtml/internal/render/testdata/time.go b/internal/godoc/dochtml/internal/render/testdata/time.go
similarity index 100%
rename from internal/fetch/dochtml/internal/render/testdata/time.go
rename to internal/godoc/dochtml/internal/render/testdata/time.go
diff --git a/internal/fetch/dochtml/io.go b/internal/godoc/dochtml/io.go
similarity index 100%
rename from internal/fetch/dochtml/io.go
rename to internal/godoc/dochtml/io.go
diff --git a/internal/fetch/dochtml/template.go b/internal/godoc/dochtml/template.go
similarity index 92%
rename from internal/fetch/dochtml/template.go
rename to internal/godoc/dochtml/template.go
index a6f9dc6..d10974e 100644
--- a/internal/fetch/dochtml/template.go
+++ b/internal/godoc/dochtml/template.go
@@ -8,8 +8,8 @@
 	"reflect"
 
 	"github.com/google/safehtml/template"
-	"golang.org/x/pkgsite/internal/fetch/dochtml/internal/render"
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/dochtml/internal/render"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 )
 
 // htmlPackage is the template used to render documentation HTML.
diff --git a/internal/fetch/dochtml/template_body.go b/internal/godoc/dochtml/template_body.go
similarity index 100%
rename from internal/fetch/dochtml/template_body.go
rename to internal/godoc/dochtml/template_body.go
diff --git a/internal/fetch/dochtml/template_sidenav.go b/internal/godoc/dochtml/template_sidenav.go
similarity index 100%
rename from internal/fetch/dochtml/template_sidenav.go
rename to internal/godoc/dochtml/template_sidenav.go
diff --git a/internal/fetch/dochtml/template_test.go b/internal/godoc/dochtml/template_test.go
similarity index 100%
rename from internal/fetch/dochtml/template_test.go
rename to internal/godoc/dochtml/template_test.go
diff --git a/internal/fetch/dochtml/testdata/everydecl.go b/internal/godoc/dochtml/testdata/everydecl.go
similarity index 100%
rename from internal/fetch/dochtml/testdata/everydecl.go
rename to internal/godoc/dochtml/testdata/everydecl.go
diff --git a/internal/fetch/dochtml/testdata/example_test.go b/internal/godoc/dochtml/testdata/example_test.go
similarity index 100%
rename from internal/fetch/dochtml/testdata/example_test.go
rename to internal/godoc/dochtml/testdata/example_test.go
diff --git a/internal/godoc/encode.go b/internal/godoc/encode.go
index 2bd4315..93f4445 100644
--- a/internal/godoc/encode.go
+++ b/internal/godoc/encode.go
@@ -132,7 +132,7 @@
 	ast.Inspect(f, func(n ast.Node) bool {
 		switch n := n.(type) {
 		case *ast.File:
-			n.Scope = nil // doc doesn't use scopes
+			n.Scope.Objects = nil // doc doesn't use scopes
 		case *ast.Ident:
 			if n.Obj != nil {
 				if _, ok := n.Obj.Decl.(int); !ok {
diff --git a/internal/godoc/godoc.go b/internal/godoc/godoc.go
index 6b62d76..cf6579a 100644
--- a/internal/godoc/godoc.go
+++ b/internal/godoc/godoc.go
@@ -8,8 +8,14 @@
 import (
 	"go/ast"
 	"go/token"
+
+	"golang.org/x/pkgsite/internal/godoc/dochtml"
 )
 
+var ErrTooLarge = dochtml.ErrTooLarge
+
+type ModuleInfo = dochtml.ModuleInfo
+
 // A package contains everything needed to render Go documentation for a package.
 type Package struct {
 	Fset *token.FileSet
diff --git a/internal/fetch/internal/doc/doc.go b/internal/godoc/internal/doc/doc.go
similarity index 100%
rename from internal/fetch/internal/doc/doc.go
rename to internal/godoc/internal/doc/doc.go
diff --git a/internal/fetch/internal/doc/doc_test.go b/internal/godoc/internal/doc/doc_test.go
similarity index 100%
rename from internal/fetch/internal/doc/doc_test.go
rename to internal/godoc/internal/doc/doc_test.go
diff --git a/internal/fetch/internal/doc/example.go b/internal/godoc/internal/doc/example.go
similarity index 100%
rename from internal/fetch/internal/doc/example.go
rename to internal/godoc/internal/doc/example.go
diff --git a/internal/fetch/internal/doc/example_test.go b/internal/godoc/internal/doc/example_test.go
similarity index 99%
rename from internal/fetch/internal/doc/example_test.go
rename to internal/godoc/internal/doc/example_test.go
index 8f3dcef..657e62e 100644
--- a/internal/fetch/internal/doc/example_test.go
+++ b/internal/godoc/internal/doc/example_test.go
@@ -15,7 +15,7 @@
 	"strings"
 	"testing"
 
-	"golang.org/x/pkgsite/internal/fetch/internal/doc"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
 )
 
 const exampleTestFile = `
diff --git a/internal/fetch/internal/doc/exports.go b/internal/godoc/internal/doc/exports.go
similarity index 100%
rename from internal/fetch/internal/doc/exports.go
rename to internal/godoc/internal/doc/exports.go
diff --git a/internal/fetch/internal/doc/filter.go b/internal/godoc/internal/doc/filter.go
similarity index 100%
rename from internal/fetch/internal/doc/filter.go
rename to internal/godoc/internal/doc/filter.go
diff --git a/internal/fetch/internal/doc/reader.go b/internal/godoc/internal/doc/reader.go
similarity index 100%
rename from internal/fetch/internal/doc/reader.go
rename to internal/godoc/internal/doc/reader.go
diff --git a/internal/fetch/internal/doc/synopsis.go b/internal/godoc/internal/doc/synopsis.go
similarity index 100%
rename from internal/fetch/internal/doc/synopsis.go
rename to internal/godoc/internal/doc/synopsis.go
diff --git a/internal/fetch/internal/doc/synopsis_test.go b/internal/godoc/internal/doc/synopsis_test.go
similarity index 100%
rename from internal/fetch/internal/doc/synopsis_test.go
rename to internal/godoc/internal/doc/synopsis_test.go
diff --git a/internal/fetch/internal/doc/testdata/a.0.golden b/internal/godoc/internal/doc/testdata/a.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/a.0.golden
rename to internal/godoc/internal/doc/testdata/a.0.golden
diff --git a/internal/fetch/internal/doc/testdata/a.1.golden b/internal/godoc/internal/doc/testdata/a.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/a.1.golden
rename to internal/godoc/internal/doc/testdata/a.1.golden
diff --git a/internal/fetch/internal/doc/testdata/a.2.golden b/internal/godoc/internal/doc/testdata/a.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/a.2.golden
rename to internal/godoc/internal/doc/testdata/a.2.golden
diff --git a/internal/fetch/internal/doc/testdata/a0.go b/internal/godoc/internal/doc/testdata/a0.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/a0.go
rename to internal/godoc/internal/doc/testdata/a0.go
diff --git a/internal/fetch/internal/doc/testdata/a1.go b/internal/godoc/internal/doc/testdata/a1.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/a1.go
rename to internal/godoc/internal/doc/testdata/a1.go
diff --git a/internal/fetch/internal/doc/testdata/b.0.golden b/internal/godoc/internal/doc/testdata/b.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/b.0.golden
rename to internal/godoc/internal/doc/testdata/b.0.golden
diff --git a/internal/fetch/internal/doc/testdata/b.1.golden b/internal/godoc/internal/doc/testdata/b.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/b.1.golden
rename to internal/godoc/internal/doc/testdata/b.1.golden
diff --git a/internal/fetch/internal/doc/testdata/b.2.golden b/internal/godoc/internal/doc/testdata/b.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/b.2.golden
rename to internal/godoc/internal/doc/testdata/b.2.golden
diff --git a/internal/fetch/internal/doc/testdata/b.go b/internal/godoc/internal/doc/testdata/b.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/b.go
rename to internal/godoc/internal/doc/testdata/b.go
diff --git a/internal/fetch/internal/doc/testdata/benchmark.go b/internal/godoc/internal/doc/testdata/benchmark.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/benchmark.go
rename to internal/godoc/internal/doc/testdata/benchmark.go
diff --git a/internal/fetch/internal/doc/testdata/blank.0.golden b/internal/godoc/internal/doc/testdata/blank.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/blank.0.golden
rename to internal/godoc/internal/doc/testdata/blank.0.golden
diff --git a/internal/fetch/internal/doc/testdata/blank.1.golden b/internal/godoc/internal/doc/testdata/blank.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/blank.1.golden
rename to internal/godoc/internal/doc/testdata/blank.1.golden
diff --git a/internal/fetch/internal/doc/testdata/blank.2.golden b/internal/godoc/internal/doc/testdata/blank.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/blank.2.golden
rename to internal/godoc/internal/doc/testdata/blank.2.golden
diff --git a/internal/fetch/internal/doc/testdata/blank.go b/internal/godoc/internal/doc/testdata/blank.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/blank.go
rename to internal/godoc/internal/doc/testdata/blank.go
diff --git a/internal/fetch/internal/doc/testdata/bugpara.0.golden b/internal/godoc/internal/doc/testdata/bugpara.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/bugpara.0.golden
rename to internal/godoc/internal/doc/testdata/bugpara.0.golden
diff --git a/internal/fetch/internal/doc/testdata/bugpara.1.golden b/internal/godoc/internal/doc/testdata/bugpara.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/bugpara.1.golden
rename to internal/godoc/internal/doc/testdata/bugpara.1.golden
diff --git a/internal/fetch/internal/doc/testdata/bugpara.2.golden b/internal/godoc/internal/doc/testdata/bugpara.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/bugpara.2.golden
rename to internal/godoc/internal/doc/testdata/bugpara.2.golden
diff --git a/internal/fetch/internal/doc/testdata/bugpara.go b/internal/godoc/internal/doc/testdata/bugpara.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/bugpara.go
rename to internal/godoc/internal/doc/testdata/bugpara.go
diff --git a/internal/fetch/internal/doc/testdata/c.0.golden b/internal/godoc/internal/doc/testdata/c.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/c.0.golden
rename to internal/godoc/internal/doc/testdata/c.0.golden
diff --git a/internal/fetch/internal/doc/testdata/c.1.golden b/internal/godoc/internal/doc/testdata/c.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/c.1.golden
rename to internal/godoc/internal/doc/testdata/c.1.golden
diff --git a/internal/fetch/internal/doc/testdata/c.2.golden b/internal/godoc/internal/doc/testdata/c.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/c.2.golden
rename to internal/godoc/internal/doc/testdata/c.2.golden
diff --git a/internal/fetch/internal/doc/testdata/c.go b/internal/godoc/internal/doc/testdata/c.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/c.go
rename to internal/godoc/internal/doc/testdata/c.go
diff --git a/internal/fetch/internal/doc/testdata/d.0.golden b/internal/godoc/internal/doc/testdata/d.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/d.0.golden
rename to internal/godoc/internal/doc/testdata/d.0.golden
diff --git a/internal/fetch/internal/doc/testdata/d.1.golden b/internal/godoc/internal/doc/testdata/d.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/d.1.golden
rename to internal/godoc/internal/doc/testdata/d.1.golden
diff --git a/internal/fetch/internal/doc/testdata/d.2.golden b/internal/godoc/internal/doc/testdata/d.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/d.2.golden
rename to internal/godoc/internal/doc/testdata/d.2.golden
diff --git a/internal/fetch/internal/doc/testdata/d1.go b/internal/godoc/internal/doc/testdata/d1.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/d1.go
rename to internal/godoc/internal/doc/testdata/d1.go
diff --git a/internal/fetch/internal/doc/testdata/d2.go b/internal/godoc/internal/doc/testdata/d2.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/d2.go
rename to internal/godoc/internal/doc/testdata/d2.go
diff --git a/internal/fetch/internal/doc/testdata/e.0.golden b/internal/godoc/internal/doc/testdata/e.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/e.0.golden
rename to internal/godoc/internal/doc/testdata/e.0.golden
diff --git a/internal/fetch/internal/doc/testdata/e.1.golden b/internal/godoc/internal/doc/testdata/e.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/e.1.golden
rename to internal/godoc/internal/doc/testdata/e.1.golden
diff --git a/internal/fetch/internal/doc/testdata/e.2.golden b/internal/godoc/internal/doc/testdata/e.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/e.2.golden
rename to internal/godoc/internal/doc/testdata/e.2.golden
diff --git a/internal/fetch/internal/doc/testdata/e.go b/internal/godoc/internal/doc/testdata/e.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/e.go
rename to internal/godoc/internal/doc/testdata/e.go
diff --git a/internal/fetch/internal/doc/testdata/error1.0.golden b/internal/godoc/internal/doc/testdata/error1.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/error1.0.golden
rename to internal/godoc/internal/doc/testdata/error1.0.golden
diff --git a/internal/fetch/internal/doc/testdata/error1.1.golden b/internal/godoc/internal/doc/testdata/error1.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/error1.1.golden
rename to internal/godoc/internal/doc/testdata/error1.1.golden
diff --git a/internal/fetch/internal/doc/testdata/error1.2.golden b/internal/godoc/internal/doc/testdata/error1.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/error1.2.golden
rename to internal/godoc/internal/doc/testdata/error1.2.golden
diff --git a/internal/fetch/internal/doc/testdata/error1.go b/internal/godoc/internal/doc/testdata/error1.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/error1.go
rename to internal/godoc/internal/doc/testdata/error1.go
diff --git a/internal/fetch/internal/doc/testdata/error2.0.golden b/internal/godoc/internal/doc/testdata/error2.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/error2.0.golden
rename to internal/godoc/internal/doc/testdata/error2.0.golden
diff --git a/internal/fetch/internal/doc/testdata/error2.1.golden b/internal/godoc/internal/doc/testdata/error2.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/error2.1.golden
rename to internal/godoc/internal/doc/testdata/error2.1.golden
diff --git a/internal/fetch/internal/doc/testdata/error2.2.golden b/internal/godoc/internal/doc/testdata/error2.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/error2.2.golden
rename to internal/godoc/internal/doc/testdata/error2.2.golden
diff --git a/internal/fetch/internal/doc/testdata/error2.go b/internal/godoc/internal/doc/testdata/error2.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/error2.go
rename to internal/godoc/internal/doc/testdata/error2.go
diff --git a/internal/fetch/internal/doc/testdata/example.go b/internal/godoc/internal/doc/testdata/example.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/example.go
rename to internal/godoc/internal/doc/testdata/example.go
diff --git a/internal/fetch/internal/doc/testdata/f.0.golden b/internal/godoc/internal/doc/testdata/f.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/f.0.golden
rename to internal/godoc/internal/doc/testdata/f.0.golden
diff --git a/internal/fetch/internal/doc/testdata/f.1.golden b/internal/godoc/internal/doc/testdata/f.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/f.1.golden
rename to internal/godoc/internal/doc/testdata/f.1.golden
diff --git a/internal/fetch/internal/doc/testdata/f.2.golden b/internal/godoc/internal/doc/testdata/f.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/f.2.golden
rename to internal/godoc/internal/doc/testdata/f.2.golden
diff --git a/internal/fetch/internal/doc/testdata/f.go b/internal/godoc/internal/doc/testdata/f.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/f.go
rename to internal/godoc/internal/doc/testdata/f.go
diff --git a/internal/fetch/internal/doc/testdata/g.0.golden b/internal/godoc/internal/doc/testdata/g.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/g.0.golden
rename to internal/godoc/internal/doc/testdata/g.0.golden
diff --git a/internal/fetch/internal/doc/testdata/g.1.golden b/internal/godoc/internal/doc/testdata/g.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/g.1.golden
rename to internal/godoc/internal/doc/testdata/g.1.golden
diff --git a/internal/fetch/internal/doc/testdata/g.2.golden b/internal/godoc/internal/doc/testdata/g.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/g.2.golden
rename to internal/godoc/internal/doc/testdata/g.2.golden
diff --git a/internal/fetch/internal/doc/testdata/g.go b/internal/godoc/internal/doc/testdata/g.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/g.go
rename to internal/godoc/internal/doc/testdata/g.go
diff --git a/internal/fetch/internal/doc/testdata/issue12839.0.golden b/internal/godoc/internal/doc/testdata/issue12839.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue12839.0.golden
rename to internal/godoc/internal/doc/testdata/issue12839.0.golden
diff --git a/internal/fetch/internal/doc/testdata/issue12839.1.golden b/internal/godoc/internal/doc/testdata/issue12839.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue12839.1.golden
rename to internal/godoc/internal/doc/testdata/issue12839.1.golden
diff --git a/internal/fetch/internal/doc/testdata/issue12839.2.golden b/internal/godoc/internal/doc/testdata/issue12839.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue12839.2.golden
rename to internal/godoc/internal/doc/testdata/issue12839.2.golden
diff --git a/internal/fetch/internal/doc/testdata/issue12839.go b/internal/godoc/internal/doc/testdata/issue12839.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue12839.go
rename to internal/godoc/internal/doc/testdata/issue12839.go
diff --git a/internal/fetch/internal/doc/testdata/issue13742.0.golden b/internal/godoc/internal/doc/testdata/issue13742.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue13742.0.golden
rename to internal/godoc/internal/doc/testdata/issue13742.0.golden
diff --git a/internal/fetch/internal/doc/testdata/issue13742.1.golden b/internal/godoc/internal/doc/testdata/issue13742.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue13742.1.golden
rename to internal/godoc/internal/doc/testdata/issue13742.1.golden
diff --git a/internal/fetch/internal/doc/testdata/issue13742.2.golden b/internal/godoc/internal/doc/testdata/issue13742.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue13742.2.golden
rename to internal/godoc/internal/doc/testdata/issue13742.2.golden
diff --git a/internal/fetch/internal/doc/testdata/issue13742.go b/internal/godoc/internal/doc/testdata/issue13742.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue13742.go
rename to internal/godoc/internal/doc/testdata/issue13742.go
diff --git a/internal/fetch/internal/doc/testdata/issue16153.0.golden b/internal/godoc/internal/doc/testdata/issue16153.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue16153.0.golden
rename to internal/godoc/internal/doc/testdata/issue16153.0.golden
diff --git a/internal/fetch/internal/doc/testdata/issue16153.1.golden b/internal/godoc/internal/doc/testdata/issue16153.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue16153.1.golden
rename to internal/godoc/internal/doc/testdata/issue16153.1.golden
diff --git a/internal/fetch/internal/doc/testdata/issue16153.2.golden b/internal/godoc/internal/doc/testdata/issue16153.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue16153.2.golden
rename to internal/godoc/internal/doc/testdata/issue16153.2.golden
diff --git a/internal/fetch/internal/doc/testdata/issue16153.go b/internal/godoc/internal/doc/testdata/issue16153.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue16153.go
rename to internal/godoc/internal/doc/testdata/issue16153.go
diff --git a/internal/fetch/internal/doc/testdata/issue17788.0.golden b/internal/godoc/internal/doc/testdata/issue17788.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue17788.0.golden
rename to internal/godoc/internal/doc/testdata/issue17788.0.golden
diff --git a/internal/fetch/internal/doc/testdata/issue17788.1.golden b/internal/godoc/internal/doc/testdata/issue17788.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue17788.1.golden
rename to internal/godoc/internal/doc/testdata/issue17788.1.golden
diff --git a/internal/fetch/internal/doc/testdata/issue17788.2.golden b/internal/godoc/internal/doc/testdata/issue17788.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue17788.2.golden
rename to internal/godoc/internal/doc/testdata/issue17788.2.golden
diff --git a/internal/fetch/internal/doc/testdata/issue17788.go b/internal/godoc/internal/doc/testdata/issue17788.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue17788.go
rename to internal/godoc/internal/doc/testdata/issue17788.go
diff --git a/internal/fetch/internal/doc/testdata/issue22856.0.golden b/internal/godoc/internal/doc/testdata/issue22856.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue22856.0.golden
rename to internal/godoc/internal/doc/testdata/issue22856.0.golden
diff --git a/internal/fetch/internal/doc/testdata/issue22856.1.golden b/internal/godoc/internal/doc/testdata/issue22856.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue22856.1.golden
rename to internal/godoc/internal/doc/testdata/issue22856.1.golden
diff --git a/internal/fetch/internal/doc/testdata/issue22856.2.golden b/internal/godoc/internal/doc/testdata/issue22856.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue22856.2.golden
rename to internal/godoc/internal/doc/testdata/issue22856.2.golden
diff --git a/internal/fetch/internal/doc/testdata/issue22856.go b/internal/godoc/internal/doc/testdata/issue22856.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/issue22856.go
rename to internal/godoc/internal/doc/testdata/issue22856.go
diff --git a/internal/fetch/internal/doc/testdata/predeclared.0.golden b/internal/godoc/internal/doc/testdata/predeclared.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/predeclared.0.golden
rename to internal/godoc/internal/doc/testdata/predeclared.0.golden
diff --git a/internal/fetch/internal/doc/testdata/predeclared.1.golden b/internal/godoc/internal/doc/testdata/predeclared.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/predeclared.1.golden
rename to internal/godoc/internal/doc/testdata/predeclared.1.golden
diff --git a/internal/fetch/internal/doc/testdata/predeclared.2.golden b/internal/godoc/internal/doc/testdata/predeclared.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/predeclared.2.golden
rename to internal/godoc/internal/doc/testdata/predeclared.2.golden
diff --git a/internal/fetch/internal/doc/testdata/predeclared.go b/internal/godoc/internal/doc/testdata/predeclared.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/predeclared.go
rename to internal/godoc/internal/doc/testdata/predeclared.go
diff --git a/internal/fetch/internal/doc/testdata/template.txt b/internal/godoc/internal/doc/testdata/template.txt
similarity index 100%
rename from internal/fetch/internal/doc/testdata/template.txt
rename to internal/godoc/internal/doc/testdata/template.txt
diff --git a/internal/fetch/internal/doc/testdata/testing.0.golden b/internal/godoc/internal/doc/testdata/testing.0.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/testing.0.golden
rename to internal/godoc/internal/doc/testdata/testing.0.golden
diff --git a/internal/fetch/internal/doc/testdata/testing.1.golden b/internal/godoc/internal/doc/testdata/testing.1.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/testing.1.golden
rename to internal/godoc/internal/doc/testdata/testing.1.golden
diff --git a/internal/fetch/internal/doc/testdata/testing.2.golden b/internal/godoc/internal/doc/testdata/testing.2.golden
similarity index 100%
rename from internal/fetch/internal/doc/testdata/testing.2.golden
rename to internal/godoc/internal/doc/testdata/testing.2.golden
diff --git a/internal/fetch/internal/doc/testdata/testing.go b/internal/godoc/internal/doc/testdata/testing.go
similarity index 100%
rename from internal/fetch/internal/doc/testdata/testing.go
rename to internal/godoc/internal/doc/testdata/testing.go
diff --git a/internal/godoc/parse.go b/internal/godoc/parse.go
index 4e64b02..12d441f 100644
--- a/internal/godoc/parse.go
+++ b/internal/godoc/parse.go
@@ -15,7 +15,7 @@
 	"github.com/google/safehtml"
 	"github.com/google/safehtml/uncheckedconversions"
 	"golang.org/x/pkgsite/internal/derrors"
-	"golang.org/x/pkgsite/internal/fetch/dochtml"
+	"golang.org/x/pkgsite/internal/godoc/dochtml"
 )
 
 // SectionType is a section of the docHTML.
diff --git a/internal/godoc/render.go b/internal/godoc/render.go
new file mode 100644
index 0000000..dc48c56
--- /dev/null
+++ b/internal/godoc/render.go
@@ -0,0 +1,117 @@
+// Copyright 2020 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
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"go/ast"
+	"path"
+	"sort"
+
+	"github.com/google/safehtml"
+	"github.com/google/safehtml/template"
+	"golang.org/x/pkgsite/internal/derrors"
+	"golang.org/x/pkgsite/internal/godoc/dochtml"
+	"golang.org/x/pkgsite/internal/godoc/internal/doc"
+	"golang.org/x/pkgsite/internal/source"
+	"golang.org/x/pkgsite/internal/stdlib"
+)
+
+const (
+	megabyte               = 1000 * 1000
+	maxImportsPerPackage   = 1000
+	docTooLargeReplacement = `<p>Documentation is too large to display.</p>`
+)
+
+// MaxDocumentationHTML is a limit on the rendered documentation HTML size.
+//
+// The current limit of is based on the largest packages that
+// pkg.go.dev has encountered. See https://golang.org/issue/40576.
+//
+// It is a variable for testing.
+var MaxDocumentationHTML = 20 * megabyte
+
+// Render renders the documentation for the package.
+// This is mostly copied from internal/fetch/fetch.go.
+func (p *Package) Render(ctx context.Context, innerPath string, sourceInfo *source.Info, modInfo *ModuleInfo, goos, goarch string) (synopsis string, imports []string, html safehtml.HTML, err error) {
+	defer derrors.Wrap(&err, "godoc.Package.Render(%q, %q, %q, %q, %q)", modInfo.ModulePath, modInfo.ResolvedVersion, innerPath, goos, goarch)
+
+	importPath := path.Join(modInfo.ModulePath, innerPath)
+	if modInfo.ModulePath == stdlib.ModulePath {
+		importPath = innerPath
+	}
+	// The "builtin" package in the standard library is a special case.
+	// We want to show documentation for all globals (not just exported ones),
+	// and avoid association of consts, vars, and factory functions with types
+	// since it's not helpful (see golang.org/issue/6645).
+	var noFiltering, noTypeAssociation bool
+	if modInfo.ModulePath == stdlib.ModulePath && importPath == "builtin" {
+		noFiltering = true
+		noTypeAssociation = true
+	}
+
+	// Compute package documentation.
+	var m doc.Mode
+	if noFiltering {
+		m |= doc.AllDecls
+	}
+	var allGoFiles []*ast.File
+	for _, f := range p.Files {
+		allGoFiles = append(allGoFiles, f.AST)
+	}
+	d, err := doc.NewFromFiles(p.Fset, allGoFiles, importPath, m)
+	if err != nil {
+		return "", nil, safehtml.HTML{}, fmt.Errorf("doc.NewFromFiles: %v", err)
+	}
+	if d.ImportPath != importPath {
+		panic(fmt.Errorf("internal error: *doc.Package has an unexpected import path (%q != %q)", d.ImportPath, importPath))
+	}
+	if noTypeAssociation {
+		for _, t := range d.Types {
+			d.Consts, t.Consts = append(d.Consts, t.Consts...), nil
+			d.Vars, t.Vars = append(d.Vars, t.Vars...), nil
+			d.Funcs, t.Funcs = append(d.Funcs, t.Funcs...), nil
+		}
+		sort.Slice(d.Funcs, func(i, j int) bool { return d.Funcs[i].Name < d.Funcs[j].Name })
+	}
+
+	// Process package imports.
+	if len(d.Imports) > maxImportsPerPackage {
+		return "", nil, safehtml.HTML{}, fmt.Errorf("%d imports found package %q; exceeds limit %d for maxImportsPerPackage", len(d.Imports), importPath, maxImportsPerPackage)
+	}
+
+	// Render documentation HTML.
+	sourceLinkFunc := func(n ast.Node) string {
+		if sourceInfo == nil {
+			return ""
+		}
+		p := p.Fset.Position(n.Pos())
+		if p.Line == 0 { // invalid Position
+			return ""
+		}
+		return sourceInfo.LineURL(path.Join(innerPath, p.Filename), p.Line)
+	}
+	fileLinkFunc := func(filename string) string {
+		if sourceInfo == nil {
+			return ""
+		}
+		return sourceInfo.FileURL(path.Join(innerPath, filename))
+	}
+
+	docHTML, err := dochtml.Render(ctx, p.Fset, d, dochtml.RenderOptions{
+		FileLinkFunc:   fileLinkFunc,
+		SourceLinkFunc: sourceLinkFunc,
+		ModInfo:        modInfo,
+		Limit:          int64(MaxDocumentationHTML),
+	})
+	if errors.Is(err, ErrTooLarge) {
+		docHTML = template.MustParseAndExecuteToHTML(docTooLargeReplacement)
+	} else if err != nil {
+		return "", nil, safehtml.HTML{}, fmt.Errorf("dochtml.Render: %v", err)
+	}
+	return doc.Synopsis(d.Doc), d.Imports, docHTML, err
+}
diff --git a/internal/worker/fetcherror_test.go b/internal/worker/fetcherror_test.go
index be301d7..05c1636 100644
--- a/internal/worker/fetcherror_test.go
+++ b/internal/worker/fetcherror_test.go
@@ -19,6 +19,7 @@
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/derrors"
 	"golang.org/x/pkgsite/internal/fetch"
+	"golang.org/x/pkgsite/internal/godoc"
 	"golang.org/x/pkgsite/internal/postgres"
 	"golang.org/x/pkgsite/internal/proxy"
 	"golang.org/x/pkgsite/internal/source"
@@ -299,7 +300,7 @@
 		var b strings.Builder
 		b.WriteString("package bar\n\n")
 		b.WriteString("const Bar = `\n")
-		for b.Len() <= fetch.MaxDocumentationHTML {
+		for b.Len() <= godoc.MaxDocumentationHTML {
 			b.WriteString("All work and no play makes Jack a dull boy.\n")
 		}
 		b.WriteString("`\n")
@@ -310,7 +311,7 @@
 		var b strings.Builder
 		b.WriteString("package baz\n\n")
 		b.WriteString("var Baz = []string{\n")
-		for b.Len() <= fetch.MaxDocumentationHTML {
+		for b.Len() <= godoc.MaxDocumentationHTML {
 			b.WriteString("`All work and no play makes Jack a dull boy.`,\n")
 		}
 		b.WriteString("}\n")