cmd/golangorg, internal/godoc: switch from vfs to io/fs
Change-Id: Idf55bca5c9ee20d6612e463ad7fc59ef212171e6
Reviewed-on: https://go-review.googlesource.com/c/website/+/293489
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/golangorg/codewalk.go b/cmd/golangorg/codewalk.go
index a7fdafa..b571343 100644
--- a/cmd/golangorg/codewalk.go
+++ b/cmd/golangorg/codewalk.go
@@ -21,6 +21,7 @@
"errors"
"fmt"
"io"
+ "io/fs"
"log"
"net/http"
"os"
@@ -33,7 +34,6 @@
"unicode/utf8"
"golang.org/x/website/internal/godoc"
- "golang.org/x/website/internal/godoc/vfs"
)
var codewalkHTML, codewalkdirHTML *template.Template
@@ -50,7 +50,7 @@
}
// If directory exists, serve list of code walks.
- dir, err := fs.Lstat(abspath)
+ dir, err := fs.Stat(fsys, toFS(abspath))
if err == nil && dir.IsDir() {
codewalkDir(w, r, relpath, abspath)
return
@@ -146,7 +146,7 @@
// loadCodewalk reads a codewalk from the named XML file.
func loadCodewalk(filename string) (*Codewalk, error) {
- f, err := fs.Open(filename)
+ f, err := fsys.Open(toFS(filename))
if err != nil {
return nil, err
}
@@ -167,7 +167,7 @@
i = len(st.Src)
}
filename := st.Src[0:i]
- data, err := vfs.ReadFile(fs, filename)
+ data, err := fs.ReadFile(fsys, toFS(filename))
if err != nil {
st.Err = err
continue
@@ -214,7 +214,7 @@
Title string
}
- dir, err := fs.ReadDir(abspath)
+ dir, err := fs.ReadDir(fsys, toFS(abspath))
if err != nil {
log.Print(err)
pres.ServeError(w, r, relpath, err)
@@ -248,7 +248,7 @@
// the usual godoc HTML wrapper.
func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) {
abspath := f
- data, err := vfs.ReadFile(fs, abspath)
+ data, err := fs.ReadFile(fsys, toFS(abspath))
if err != nil {
log.Print(err)
pres.ServeError(w, r, f, err)
diff --git a/cmd/golangorg/handlers.go b/cmd/golangorg/handlers.go
index c1919c9..c7125c0 100644
--- a/cmd/golangorg/handlers.go
+++ b/cmd/golangorg/handlers.go
@@ -10,23 +10,32 @@
import (
"encoding/json"
"go/format"
+ "io/fs"
"log"
"net/http"
+ pathpkg "path"
"strings"
"text/template"
"golang.org/x/website/internal/env"
"golang.org/x/website/internal/godoc"
- "golang.org/x/website/internal/godoc/vfs"
"golang.org/x/website/internal/history"
"golang.org/x/website/internal/redirect"
)
var (
pres *godoc.Presentation
- fs = vfs.NameSpace{}
+ fsys fs.FS
)
+// toFS returns the io/fs name for path (no leading slash).
+func toFS(path string) string {
+ if path == "/" {
+ return "."
+ }
+ return pathpkg.Clean(strings.TrimPrefix(path, "/"))
+}
+
// hostEnforcerHandler redirects requests to "http://foo.golang.org/bar"
// to "https://golang.org/bar".
// It permits requests to the host "godoc-test.golang.org" for testing and
@@ -100,7 +109,7 @@
// use underlying file system fs to read the template file
// (cannot use template ParseFile functions directly)
- data, err := vfs.ReadFile(fs, path)
+ data, err := fs.ReadFile(fsys, toFS(path))
if err != nil {
log.Fatal("readTemplate: ", err)
}
diff --git a/cmd/golangorg/main.go b/cmd/golangorg/main.go
index 60f0294..14abaf6 100644
--- a/cmd/golangorg/main.go
+++ b/cmd/golangorg/main.go
@@ -23,6 +23,7 @@
import (
"flag"
"fmt"
+ "io/fs"
"log"
"net/http"
"os"
@@ -31,8 +32,6 @@
"golang.org/x/website"
"golang.org/x/website/internal/godoc"
- "golang.org/x/website/internal/godoc/vfs"
- "golang.org/x/website/internal/godoc/vfs/gatefs"
)
var (
@@ -75,23 +74,16 @@
usage()
}
- fsGate := make(chan bool, 20)
-
- // Determine file system to use.
- rootfs := gatefs.New(vfs.OS(*goroot), fsGate)
- fs.Bind("/", rootfs, "/", vfs.BindReplace)
-
- // Try serving files from _content before trying the main
- // go repository. This lets us update some documentation outside the
- // Go release cycle. This includes root.html, which redirects to "/".
- // See golang.org/issue/29206.
+ // Serve files from _content, falling back to GOROOT.
+ var content fs.FS
if *templateDir != "" {
- fs.Bind("/", vfs.OS(*templateDir), "/", vfs.BindBefore)
+ content = os.DirFS(*templateDir)
} else {
- fs.Bind("/", vfs.FromFS(website.Content), "/", vfs.BindBefore)
+ content = website.Content
}
+ fsys = unionFS{content, os.DirFS(*goroot)}
- corpus := godoc.NewCorpus(fs)
+ corpus := godoc.NewCorpus(fsys)
corpus.Verbose = *verbose
if err := corpus.Init(); err != nil {
log.Fatal(err)
@@ -118,7 +110,6 @@
log.Printf("\tversion = %s", runtime.Version())
log.Printf("\taddress = %s", *httpAddr)
log.Printf("\tgoroot = %s", *goroot)
- fs.Fprint(os.Stderr)
handler = loggingHandler(handler)
}
@@ -128,3 +119,65 @@
log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
}
}
+
+var _ fs.ReadDirFS = unionFS{}
+
+// A unionFS is an FS presenting the union of the file systems in the slice.
+// If multiple file systems provide a particular file, Open uses the FS listed earlier in the slice.
+// If multiple file systems provide a particular directory, ReadDir presents the
+// concatenation of all the directories listed in the slice (with duplicates removed).
+type unionFS []fs.FS
+
+func (fsys unionFS) Open(name string) (fs.File, error) {
+ var errOut error
+ for _, sub := range fsys {
+ f, err := sub.Open(name)
+ if err == nil {
+ // Note: Should technically check for directory
+ // and return a synthetic directory that merges
+ // reads from all the matching directories,
+ // but all the directory reads in internal/godoc
+ // come from fsys.ReadDir, which does that for us.
+ // So we can ignore direct f.ReadDir calls.
+ return f, nil
+ }
+ if errOut == nil {
+ errOut = err
+ }
+ }
+ return nil, errOut
+}
+
+func (fsys unionFS) ReadDir(name string) ([]fs.DirEntry, error) {
+ var all []fs.DirEntry
+ var seen map[string]bool // seen[name] is true if name is listed in all; lazily initialized
+ var errOut error
+ for _, sub := range fsys {
+ list, err := fs.ReadDir(sub, toFS(name))
+ if err != nil {
+ errOut = err
+ }
+ if len(all) == 0 {
+ all = append(all, list...)
+ } else {
+ if seen == nil {
+ // Initialize seen only after we get two different directory listings.
+ seen = make(map[string]bool)
+ for _, d := range all {
+ seen[d.Name()] = true
+ }
+ }
+ for _, d := range list {
+ name := d.Name()
+ if !seen[name] {
+ seen[name] = true
+ all = append(all, d)
+ }
+ }
+ }
+ }
+ if len(all) > 0 {
+ return all, nil
+ }
+ return nil, errOut
+}
diff --git a/cmd/golangorg/project.go b/cmd/golangorg/project.go
index 06f605e..3b6d121 100644
--- a/cmd/golangorg/project.go
+++ b/cmd/golangorg/project.go
@@ -11,12 +11,12 @@
"bytes"
"fmt"
"html/template"
+ "io/fs"
"log"
"net/http"
"sort"
"golang.org/x/website/internal/godoc"
- "golang.org/x/website/internal/godoc/vfs"
"golang.org/x/website/internal/history"
)
@@ -33,7 +33,7 @@
const relPath = "doc/contrib.html"
- src, err := vfs.ReadFile(fs, relPath)
+ src, err := fs.ReadFile(fsys, toFS(relPath))
if err != nil {
log.Printf("reading template %s: %v", relPath, err)
pres.ServeError(w, req, relPath, err)
diff --git a/cmd/golangorg/release.go b/cmd/golangorg/release.go
index 198b786..2438178 100644
--- a/cmd/golangorg/release.go
+++ b/cmd/golangorg/release.go
@@ -12,13 +12,13 @@
"fmt"
"html"
"html/template"
+ "io/fs"
"log"
"net/http"
"sort"
"strings"
"golang.org/x/website/internal/godoc"
- "golang.org/x/website/internal/godoc/vfs"
"golang.org/x/website/internal/history"
)
@@ -30,7 +30,7 @@
func (h releaseHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
const relPath = "doc/devel/release.html"
- src, err := vfs.ReadFile(fs, relPath)
+ src, err := fs.ReadFile(fsys, toFS(relPath))
if err != nil {
log.Printf("reading template %s: %v", relPath, err)
pres.ServeError(w, req, relPath, err)
diff --git a/cmd/golangorg/release_test.go b/cmd/golangorg/release_test.go
index 80e543d..5b2270b 100644
--- a/cmd/golangorg/release_test.go
+++ b/cmd/golangorg/release_test.go
@@ -15,7 +15,6 @@
"golang.org/x/website"
"golang.org/x/website/internal/godoc"
- "golang.org/x/website/internal/godoc/vfs"
)
// Test that the release history page includes expected entries.
@@ -25,11 +24,10 @@
// It can be relaxed whenever the presentation of the release history
// page needs to be changed.
func TestReleaseHistory(t *testing.T) {
- origFS, origPres := fs, pres
- defer func() { fs, pres = origFS, origPres }()
- fs = vfs.NameSpace{}
- fs.Bind("/", vfs.FromFS(website.Content), "/", vfs.BindReplace)
- pres = godoc.NewPresentation(godoc.NewCorpus(fs))
+ origFS, origPres := fsys, pres
+ defer func() { fsys, pres = origFS, origPres }()
+ fsys = website.Content
+ pres = godoc.NewPresentation(godoc.NewCorpus(fsys))
readTemplates(pres)
mux := registerHandlers(pres)
diff --git a/internal/godoc/corpus.go b/internal/godoc/corpus.go
index 74a2488..ddfd2e9 100644
--- a/internal/godoc/corpus.go
+++ b/internal/godoc/corpus.go
@@ -2,15 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
"errors"
+ "io/fs"
"sync"
"time"
"golang.org/x/website/internal/godoc/util"
- "golang.org/x/website/internal/godoc/vfs"
)
// A Corpus holds all the state related to serving and indexing a
@@ -19,7 +22,7 @@
// Construct a new Corpus with NewCorpus, then modify options,
// then call its Init method.
type Corpus struct {
- fs vfs.FileSystem
+ fs fs.FS
// Verbose logging.
Verbose bool
@@ -58,9 +61,9 @@
// NewCorpus returns a new Corpus from a filesystem.
// The returned corpus has all indexing enabled and MaxResults set to 1000.
// Change or set any options on Corpus before calling the Corpus.Init method.
-func NewCorpus(fs vfs.FileSystem) *Corpus {
+func NewCorpus(fsys fs.FS) *Corpus {
c := &Corpus{
- fs: fs,
+ fs: fsys,
refreshMetadataSignal: make(chan bool, 1),
}
return c
diff --git a/internal/godoc/dirtrees.go b/internal/godoc/dirtrees.go
index 387d09c..5c0897c 100644
--- a/internal/godoc/dirtrees.go
+++ b/internal/godoc/dirtrees.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// This file contains the code dealing with package directory trees.
package godoc
@@ -10,14 +13,12 @@
"go/doc"
"go/parser"
"go/token"
+ "io/fs"
"log"
- "os"
pathpkg "path"
"runtime"
"sort"
"strings"
-
- "golang.org/x/website/internal/godoc/vfs"
)
// Conventional name for directories containing test data.
@@ -35,19 +36,24 @@
Dirs []*Directory // subdirectories
}
-func isGoFile(fi os.FileInfo) bool {
+type dirEntryOrFileInfo interface {
+ Name() string
+ IsDir() bool
+}
+
+func isGoFile(fi dirEntryOrFileInfo) bool {
name := fi.Name()
return !fi.IsDir() &&
len(name) > 0 && name[0] != '.' && // ignore .files
pathpkg.Ext(name) == ".go"
}
-func isPkgFile(fi os.FileInfo) bool {
+func isPkgFile(fi dirEntryOrFileInfo) bool {
return isGoFile(fi) &&
!strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
}
-func isPkgDir(fi os.FileInfo) bool {
+func isPkgDir(fi dirEntryOrFileInfo) bool {
name := fi.Name()
return fi.IsDir() && len(name) > 0 &&
name[0] != '_' && name[0] != '.' // ignore _files and .files
@@ -100,7 +106,7 @@
}
ioGate <- struct{}{}
- list, err := b.c.fs.ReadDir(path)
+ list, err := fs.ReadDir(b.c.fs, toFS(path))
<-ioGate
if err != nil {
// TODO: propagate more. See golang.org/issue/14252.
@@ -208,11 +214,19 @@
}
}
-func isGOROOT(fs vfs.FileSystem) bool {
- _, err := fs.Lstat("src/math/abs.go")
+func isGOROOT(fsys fs.FS) bool {
+ _, err := fs.Stat(fsys, "src/math/abs.go")
return err == nil
}
+// toFS returns the io/fs name for path (no leading slash).
+func toFS(path string) string {
+ if path == "/" {
+ return "."
+ }
+ return pathpkg.Clean(strings.TrimPrefix(path, "/"))
+}
+
// newDirectory creates a new package directory tree with at most maxDepth
// levels, anchored at root. The result tree is pruned such that it only
// contains directories that contain package files or that contain
@@ -225,7 +239,7 @@
//
func (c *Corpus) newDirectory(root string, maxDepth int) *Directory {
// The root could be a symbolic link so use Stat not Lstat.
- d, err := c.fs.Stat(root)
+ d, err := fs.Stat(c.fs, toFS(root))
// If we fail here, report detailed error messages; otherwise
// is is hard to see why a directory tree was not built.
switch {
diff --git a/internal/godoc/dirtrees_test.go b/internal/godoc/dirtrees_test.go
index 552fe86..766bc55 100644
--- a/internal/godoc/dirtrees_test.go
+++ b/internal/godoc/dirtrees_test.go
@@ -2,26 +2,20 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
- "go/build"
- "path/filepath"
+ "os"
"runtime"
"sort"
"testing"
-
- "golang.org/x/website/internal/godoc/vfs"
- "golang.org/x/website/internal/godoc/vfs/gatefs"
)
func TestNewDirTree(t *testing.T) {
- fsGate := make(chan bool, 20)
- rootfs := gatefs.New(vfs.OS(runtime.GOROOT()), fsGate)
- fs := vfs.NameSpace{}
- fs.Bind("/", rootfs, "/", vfs.BindReplace)
-
- c := NewCorpus(fs)
+ c := NewCorpus(os.DirFS(runtime.GOROOT()))
// 3 levels deep is enough for testing
dir := c.newDirectory("/", 3)
@@ -46,15 +40,8 @@
b.Skip("not running tests requiring large file scan in short mode")
}
- fsGate := make(chan bool, 20)
+ fs := os.DirFS(runtime.GOROOT())
- goroot := runtime.GOROOT()
- rootfs := gatefs.New(vfs.OS(goroot), fsGate)
- fs := vfs.NameSpace{}
- fs.Bind("/", rootfs, "/", vfs.BindReplace)
- for _, p := range filepath.SplitList(build.Default.GOPATH) {
- fs.Bind("/src/golang.org", gatefs.New(vfs.OS(p), fsGate), "/src/golang.org", vfs.BindAfter)
- }
b.ResetTimer()
b.ReportAllocs()
for tries := 0; tries < b.N; tries++ {
@@ -73,7 +60,7 @@
}
for _, item := range tests {
- fs := vfs.OS(item.path)
+ fs := os.DirFS(item.path)
if isGOROOT(fs) != item.isGOROOT {
t.Errorf("%s: isGOROOT = %v, want %v", item.path, !item.isGOROOT, item.isGOROOT)
}
diff --git a/internal/godoc/format.go b/internal/godoc/format.go
index 3e8c867..e005dcb 100644
--- a/internal/godoc/format.go
+++ b/internal/godoc/format.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// This file implements FormatSelections and FormatText.
// FormatText is used to HTML-format Go and non-Go source
// text with line numbers and highlighted sections. It is
diff --git a/internal/godoc/godoc.go b/internal/godoc/godoc.go
index faec584..c23d549 100644
--- a/internal/godoc/godoc.go
+++ b/internal/godoc/godoc.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// Package godoc is a work-in-progress (2013-07-17) package to
// begin splitting up the godoc binary into multiple pieces.
//
diff --git a/internal/godoc/godoc_test.go b/internal/godoc/godoc_test.go
index 2719ccc..fd65c7e 100644
--- a/internal/godoc/godoc_test.go
+++ b/internal/godoc/godoc_test.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
diff --git a/internal/godoc/linkify.go b/internal/godoc/linkify.go
index e4add22..3f44ee6 100644
--- a/internal/godoc/linkify.go
+++ b/internal/godoc/linkify.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// This file implements LinkifyText which introduces
// links for identifiers pointing to their declarations.
// The approach does not cover all cases because godoc
diff --git a/internal/godoc/markdown.go b/internal/godoc/markdown.go
index fd61aa5..9149bca 100644
--- a/internal/godoc/markdown.go
+++ b/internal/godoc/markdown.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
diff --git a/internal/godoc/meta.go b/internal/godoc/meta.go
index c180254..da5d633 100644
--- a/internal/godoc/meta.go
+++ b/internal/godoc/meta.go
@@ -2,19 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
"bytes"
"encoding/json"
"errors"
+ "io/fs"
"log"
"os"
pathpkg "path"
"strings"
"time"
-
- "golang.org/x/website/internal/godoc/vfs"
)
var (
@@ -67,7 +69,7 @@
metadata := make(map[string]*Metadata)
var scan func(string) // scan is recursive
scan = func(dir string) {
- fis, err := c.fs.ReadDir(dir)
+ fis, err := fs.ReadDir(c.fs, toFS(dir))
if err != nil {
if dir == "/doc" && errors.Is(err, os.ErrNotExist) {
// Be quiet during tests that don't have a /doc tree.
@@ -86,7 +88,7 @@
continue
}
// Extract metadata from the file.
- b, err := vfs.ReadFile(c.fs, name)
+ b, err := fs.ReadFile(c.fs, toFS(name))
if err != nil {
log.Printf("updateMetadata %s: %v", name, err)
continue
diff --git a/internal/godoc/page.go b/internal/godoc/page.go
index 279d803..d6cc640 100644
--- a/internal/godoc/page.go
+++ b/internal/godoc/page.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
diff --git a/internal/godoc/parser.go b/internal/godoc/parser.go
index 8712918..f742596 100644
--- a/internal/godoc/parser.go
+++ b/internal/godoc/parser.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// This file contains support functions for parsing .go files
// accessed via godoc's file system fs.
@@ -12,9 +15,8 @@
"go/ast"
"go/parser"
"go/token"
+ "io/fs"
pathpkg "path"
-
- "golang.org/x/website/internal/godoc/vfs"
)
var linePrefix = []byte("//line ")
@@ -47,7 +49,7 @@
}
func (c *Corpus) parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) {
- src, err := vfs.ReadFile(c.fs, filename)
+ src, err := fs.ReadFile(c.fs, toFS(filename))
if err != nil {
return nil, err
}
diff --git a/internal/godoc/pres.go b/internal/godoc/pres.go
index cc01204..e4734bf 100644
--- a/internal/godoc/pres.go
+++ b/internal/godoc/pres.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
@@ -9,8 +12,6 @@
"regexp"
"sync"
"text/template"
-
- "golang.org/x/website/internal/godoc/vfs/httpfs"
)
// Presentation generates output from a corpus.
@@ -84,7 +85,7 @@
p := &Presentation{
Corpus: c,
mux: http.NewServeMux(),
- fileServer: http.FileServer(httpfs.New(c.fs)),
+ fileServer: http.FileServer(http.FS(c.fs)),
TabWidth: 4,
DeclLinks: true,
diff --git a/internal/godoc/server.go b/internal/godoc/server.go
index 52178ed..3360048 100644
--- a/internal/godoc/server.go
+++ b/internal/godoc/server.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
@@ -15,6 +18,7 @@
"go/token"
htmlpkg "html"
"io"
+ "io/fs"
"io/ioutil"
"log"
"net/http"
@@ -27,7 +31,6 @@
"time"
"golang.org/x/website/internal/godoc/util"
- "golang.org/x/website/internal/godoc/vfs"
)
// handlerServer is a migration from an old godoc http Handler type.
@@ -65,21 +68,24 @@
ctxt := build.Default
ctxt.IsAbsPath = pathpkg.IsAbs
ctxt.IsDir = func(path string) bool {
- fi, err := h.c.fs.Stat(filepath.ToSlash(path))
+ fi, err := fs.Stat(h.c.fs, toFS(filepath.ToSlash(path)))
return err == nil && fi.IsDir()
}
ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
- f, err := h.c.fs.ReadDir(filepath.ToSlash(dir))
+ f, err := fs.ReadDir(h.c.fs, toFS(filepath.ToSlash(dir)))
filtered := make([]os.FileInfo, 0, len(f))
for _, i := range f {
if mode&NoFiltering != 0 || i.Name() != "internal" {
- filtered = append(filtered, i)
+ info, err := i.Info()
+ if err == nil {
+ filtered = append(filtered, info)
+ }
}
}
return filtered, err
}
ctxt.OpenFile = func(name string) (r io.ReadCloser, err error) {
- data, err := vfs.ReadFile(h.c.fs, filepath.ToSlash(name))
+ data, err := fs.ReadFile(h.c.fs, toFS(filepath.ToSlash(name)))
if err != nil {
return nil, err
}
@@ -552,7 +558,7 @@
}
func (p *Presentation) serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
- src, err := vfs.ReadFile(p.Corpus.fs, abspath)
+ src, err := fs.ReadFile(p.Corpus.fs, toFS(abspath))
if err != nil {
log.Printf("ReadFile: %s", err)
p.ServeError(w, r, relpath, err)
@@ -636,17 +642,25 @@
return
}
- list, err := p.Corpus.fs.ReadDir(abspath)
+ list, err := fs.ReadDir(p.Corpus.fs, toFS(abspath))
if err != nil {
p.ServeError(w, r, relpath, err)
return
}
+ var info []fs.FileInfo
+ for _, d := range list {
+ i, err := d.Info()
+ if err == nil {
+ info = append(info, i)
+ }
+ }
+
p.ServePage(w, Page{
Title: "Directory",
SrcPath: relpath,
Tabtitle: relpath,
- Body: applyTemplate(p.DirlistHTML, "dirlistHTML", list),
+ Body: applyTemplate(p.DirlistHTML, "dirlistHTML", info),
GoogleCN: googleCN(r),
})
}
@@ -654,9 +668,9 @@
func (p *Presentation) ServeHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
// get HTML body contents
isMarkdown := false
- src, err := vfs.ReadFile(p.Corpus.fs, abspath)
+ src, err := fs.ReadFile(p.Corpus.fs, toFS(abspath))
if err != nil && strings.HasSuffix(abspath, ".html") {
- if md, errMD := vfs.ReadFile(p.Corpus.fs, strings.TrimSuffix(abspath, ".html")+".md"); errMD == nil {
+ if md, errMD := fs.ReadFile(p.Corpus.fs, toFS(strings.TrimSuffix(abspath, ".html")+".md")); errMD == nil {
src = md
isMarkdown = true
err = nil
@@ -764,19 +778,20 @@
return
}
- dir, err := p.Corpus.fs.Lstat(abspath)
+ dir, err := fs.Stat(p.Corpus.fs, toFS(abspath))
if err != nil {
log.Print(err)
p.ServeError(w, r, relpath, err)
return
}
+ fsPath := toFS(abspath)
if dir != nil && dir.IsDir() {
if redirect(w, r) {
return
}
- index := pathpkg.Join(abspath, "index.html")
- if util.IsTextFile(p.Corpus.fs, index) || util.IsTextFile(p.Corpus.fs, pathpkg.Join(abspath, "index.md")) {
+ index := pathpkg.Join(fsPath, "index.html")
+ if util.IsTextFile(p.Corpus.fs, index) || util.IsTextFile(p.Corpus.fs, pathpkg.Join(fsPath, "index.md")) {
p.ServeHTMLDoc(w, r, index, index)
return
}
@@ -784,7 +799,7 @@
return
}
- if util.IsTextFile(p.Corpus.fs, abspath) {
+ if util.IsTextFile(p.Corpus.fs, fsPath) {
if redirectFile(w, r) {
return
}
diff --git a/internal/godoc/server_test.go b/internal/godoc/server_test.go
index 7a446f9..ea3f8fb 100644
--- a/internal/godoc/server_test.go
+++ b/internal/godoc/server_test.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
@@ -10,9 +13,8 @@
"net/url"
"strings"
"testing"
+ "testing/fstest"
"text/template"
-
- "golang.org/x/website/internal/godoc/vfs/mapfs"
)
// TestIgnoredGoFiles tests the scenario where a folder has no .go or .c files,
@@ -21,11 +23,12 @@
packagePath := "github.com/package"
packageComment := "main is documented in an ignored .go file"
- c := NewCorpus(mapfs.New(map[string]string{
- "src/" + packagePath + "/ignored.go": `// +build ignore
+ c := NewCorpus(fstest.MapFS{
+ "src/" + packagePath + "/ignored.go": {Data: []byte(`// +build ignore
// ` + packageComment + `
-package main`}))
+package main`)},
+ })
srv := &handlerServer{
p: &Presentation{
Corpus: c,
@@ -54,14 +57,15 @@
func TestIssue5247(t *testing.T) {
const packagePath = "example.com/p"
- c := NewCorpus(mapfs.New(map[string]string{
- "src/" + packagePath + "/p.go": `package p
+ c := NewCorpus(fstest.MapFS{
+ "src/" + packagePath + "/p.go": {Data: []byte(`package p
//line notgen.go:3
// F doc //line 1 should appear
// line 2 should appear
func F()
-//line foo.go:100`})) // No newline at end to check corner cases.
+//line foo.go:100`)}, // No newline at end to check corner cases.
+ })
srv := &handlerServer{
p: &Presentation{Corpus: c},
@@ -85,14 +89,15 @@
}
func TestRedirectAndMetadata(t *testing.T) {
- c := NewCorpus(mapfs.New(map[string]string{
- "doc/y/index.html": "Hello, y.",
- "doc/x/index.html": `<!--{
+ c := NewCorpus(fstest.MapFS{
+ "doc/y/index.html": {Data: []byte("Hello, y.")},
+ "doc/x/index.html": {Data: []byte(`<!--{
"Path": "/doc/x/"
}-->
Hello, x.
-`}))
+`)},
+ })
c.updateMetadata()
p := &Presentation{
Corpus: c,
@@ -118,10 +123,10 @@
func TestMarkdown(t *testing.T) {
p := &Presentation{
- Corpus: NewCorpus(mapfs.New(map[string]string{
- "doc/test.md": "**bold**",
- "doc/test2.md": `{{"*template*"}}`,
- })),
+ Corpus: NewCorpus(fstest.MapFS{
+ "doc/test.md": {Data: []byte("**bold**")},
+ "doc/test2.md": {Data: []byte(`{{"*template*"}}`)},
+ }),
GodocHTML: template.Must(template.New("").Parse(`{{printf "%s" .Body}}`)),
}
diff --git a/internal/godoc/spec.go b/internal/godoc/spec.go
index 9ec9427..2c2db4b 100644
--- a/internal/godoc/spec.go
+++ b/internal/godoc/spec.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
// This file contains the mechanism to "linkify" html source
diff --git a/internal/godoc/spec_test.go b/internal/godoc/spec_test.go
index c016516..77373f1 100644
--- a/internal/godoc/spec_test.go
+++ b/internal/godoc/spec_test.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (
diff --git a/internal/godoc/tab.go b/internal/godoc/tab.go
index d314ac7..944074e 100644
--- a/internal/godoc/tab.go
+++ b/internal/godoc/tab.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// TODO(bradfitz,adg): move to util
package godoc
diff --git a/internal/godoc/template.go b/internal/godoc/template.go
index 9b99e7e..3e52453 100644
--- a/internal/godoc/template.go
+++ b/internal/godoc/template.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// Template support for writing HTML documents.
// Documents that include Template: true in their
// metadata are executed as input to text/template.
@@ -34,11 +37,10 @@
import (
"bytes"
"fmt"
+ "io/fs"
"log"
"regexp"
"strings"
-
- "golang.org/x/website/internal/godoc/vfs"
)
// Functions in this file panic on error, but the panic is recovered
@@ -47,7 +49,7 @@
// contents reads and returns the content of the named file
// (from the virtual file system, so for example /doc refers to $GOROOT/doc).
func (c *Corpus) contents(name string) string {
- file, err := vfs.ReadFile(c.fs, name)
+ file, err := fs.ReadFile(c.fs, toFS(name))
if err != nil {
log.Panic(err)
}
diff --git a/internal/godoc/util/util.go b/internal/godoc/util/util.go
index b15fd5c..2397241 100644
--- a/internal/godoc/util/util.go
+++ b/internal/godoc/util/util.go
@@ -2,16 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// Package util contains utility types and functions for godoc.
package util // import "golang.org/x/website/internal/godoc/util"
import (
+ "io/fs"
pathpkg "path"
"sync"
"time"
"unicode/utf8"
-
- "golang.org/x/website/internal/godoc/vfs"
)
// An RWValue wraps a value and permits mutually exclusive
@@ -66,7 +68,7 @@
// a text file, or if a significant chunk of the specified file looks like
// correct UTF-8; that is, if it is likely that the file contains human-
// readable text.
-func IsTextFile(fs vfs.Opener, filename string) bool {
+func IsTextFile(fsys fs.FS, filename string) bool {
// if the extension is known, use it for decision making
if isText, found := textExt[pathpkg.Ext(filename)]; found {
return isText
@@ -74,7 +76,7 @@
// the extension is not known; read an initial chunk
// of the file and check if it looks like text
- f, err := fs.Open(filename)
+ f, err := fsys.Open(filename)
if err != nil {
return false
}
diff --git a/internal/godoc/versions.go b/internal/godoc/versions.go
index 7342858..e0a16f7 100644
--- a/internal/godoc/versions.go
+++ b/internal/godoc/versions.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
// This file caches information about which standard library types, methods,
// and functions appeared in what version of Go
diff --git a/internal/godoc/versions_test.go b/internal/godoc/versions_test.go
index bfc05f6..1504b61 100644
--- a/internal/godoc/versions_test.go
+++ b/internal/godoc/versions_test.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.16
+// +build go1.16
+
package godoc
import (