mirror of
https://github.com/golang/go
synced 2024-11-18 11:04:42 -07:00
godoc: add util package, add start of vfs package
Move some code out of cmd/godoc. R=golang-dev, adg CC=golang-dev https://golang.org/cl/11413043
This commit is contained in:
parent
d79f4fe25b
commit
7526441b70
@ -19,6 +19,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/go.tools/godoc/vfs"
|
||||
)
|
||||
|
||||
// fs is the file system that godoc reads from and serves.
|
||||
@ -51,19 +53,13 @@ const debugNS = false
|
||||
// The FileSystem interface specifies the methods godoc is using
|
||||
// to access the file system for which it serves documentation.
|
||||
type FileSystem interface {
|
||||
Open(path string) (readSeekCloser, error)
|
||||
Open(path string) (vfs.ReadSeekCloser, error)
|
||||
Lstat(path string) (os.FileInfo, error)
|
||||
Stat(path string) (os.FileInfo, error)
|
||||
ReadDir(path string) ([]os.FileInfo, error)
|
||||
String() string
|
||||
}
|
||||
|
||||
type readSeekCloser interface {
|
||||
io.Reader
|
||||
io.Seeker
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// ReadFile reads the file named by path from fs and returns the contents.
|
||||
func ReadFile(fs FileSystem, path string) ([]byte, error) {
|
||||
rc, err := fs.Open(path)
|
||||
@ -97,7 +93,7 @@ func (root osFS) resolve(path string) string {
|
||||
return filepath.Join(string(root), path)
|
||||
}
|
||||
|
||||
func (root osFS) Open(path string) (readSeekCloser, error) {
|
||||
func (root osFS) Open(path string) (vfs.ReadSeekCloser, error) {
|
||||
f, err := os.Open(root.resolve(path))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -319,7 +315,7 @@ func (ns nameSpace) resolve(path string) []mountedFS {
|
||||
}
|
||||
|
||||
// Open implements the FileSystem Open method.
|
||||
func (ns nameSpace) Open(path string) (readSeekCloser, error) {
|
||||
func (ns nameSpace) Open(path string) (vfs.ReadSeekCloser, error) {
|
||||
var err error
|
||||
for _, m := range ns.resolve(path) {
|
||||
if debugNS {
|
||||
@ -552,7 +548,7 @@ func (h *httpDir) Readdir(count int) ([]os.FileInfo, error) {
|
||||
// httpFile implements http.File for a file (not directory) in a FileSystem.
|
||||
type httpFile struct {
|
||||
fs FileSystem
|
||||
readSeekCloser
|
||||
vfs.ReadSeekCloser
|
||||
name string
|
||||
}
|
||||
|
||||
|
@ -32,26 +32,13 @@ import (
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.google.com/p/go.tools/godoc/util"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Globals
|
||||
|
||||
type delayTime struct {
|
||||
RWValue
|
||||
}
|
||||
|
||||
func (dt *delayTime) backoff(max time.Duration) {
|
||||
dt.mutex.Lock()
|
||||
v := dt.value.(time.Duration) * 2
|
||||
if v > max {
|
||||
v = max
|
||||
}
|
||||
dt.value = v
|
||||
// don't change dt.timestamp - calling backoff indicates an error condition
|
||||
dt.mutex.Unlock()
|
||||
}
|
||||
|
||||
var (
|
||||
verbose = flag.Bool("v", false, "verbose mode")
|
||||
|
||||
@ -76,9 +63,9 @@ var (
|
||||
indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle")
|
||||
|
||||
// file system information
|
||||
fsTree RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now)
|
||||
fsModified RWValue // timestamp of last call to invalidateIndex
|
||||
docMetadata RWValue // mapping from paths to *Metadata
|
||||
fsTree util.RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now)
|
||||
fsModified util.RWValue // timestamp of last call to invalidateIndex
|
||||
docMetadata util.RWValue // mapping from paths to *Metadata
|
||||
|
||||
// http handlers
|
||||
fileServer http.Handler // default file server
|
||||
@ -112,7 +99,7 @@ func initFSTree() {
|
||||
log.Println("Warning: FSTree is nil")
|
||||
return
|
||||
}
|
||||
fsTree.set(dir)
|
||||
fsTree.Set(dir)
|
||||
invalidateIndex()
|
||||
}
|
||||
|
||||
@ -250,7 +237,7 @@ func infoKind_htmlFunc(info SpotInfo) string {
|
||||
func infoLineFunc(info SpotInfo) int {
|
||||
line := info.Lori()
|
||||
if info.IsIndex() {
|
||||
index, _ := searchIndex.get()
|
||||
index, _ := searchIndex.Get()
|
||||
if index != nil {
|
||||
line = index.(*Index).Snippet(line).Line
|
||||
} else {
|
||||
@ -266,7 +253,7 @@ func infoLineFunc(info SpotInfo) int {
|
||||
|
||||
func infoSnippet_htmlFunc(info SpotInfo) string {
|
||||
if info.IsIndex() {
|
||||
index, _ := searchIndex.get()
|
||||
index, _ := searchIndex.Get()
|
||||
// Snippet.Text was HTML-escaped when it was generated
|
||||
return index.(*Index).Snippet(info.Lori()).Text
|
||||
}
|
||||
@ -838,7 +825,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
|
||||
if redirect(w, r) {
|
||||
return
|
||||
}
|
||||
if index := pathpkg.Join(abspath, "index.html"); isTextFile(index) {
|
||||
if index := pathpkg.Join(abspath, "index.html"); util.IsTextFile(fs, index) {
|
||||
serveHTMLDoc(w, r, index, index)
|
||||
return
|
||||
}
|
||||
@ -846,7 +833,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if isTextFile(abspath) {
|
||||
if util.IsTextFile(fs, abspath) {
|
||||
if redirectFile(w, r) {
|
||||
return
|
||||
}
|
||||
@ -1168,7 +1155,7 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) *Pag
|
||||
// get directory information, if any
|
||||
var dir *Directory
|
||||
var timestamp time.Time
|
||||
if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil {
|
||||
if tree, ts := fsTree.Get(); tree != nil && tree.(*Directory) != nil {
|
||||
// directory tree is present; lookup respective directory
|
||||
// (may still fail if the file system was updated and the
|
||||
// new directory tree has not yet been computed)
|
||||
@ -1256,7 +1243,7 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// ----------------------------------------------------------------------------
|
||||
// Search
|
||||
|
||||
var searchIndex RWValue
|
||||
var searchIndex util.RWValue
|
||||
|
||||
type SearchResult struct {
|
||||
Query string
|
||||
@ -1276,7 +1263,7 @@ type SearchResult struct {
|
||||
func lookup(query string) (result SearchResult) {
|
||||
result.Query = query
|
||||
|
||||
index, timestamp := searchIndex.get()
|
||||
index, timestamp := searchIndex.Get()
|
||||
if index != nil {
|
||||
index := index.(*Index)
|
||||
|
||||
@ -1311,7 +1298,7 @@ func lookup(query string) (result SearchResult) {
|
||||
|
||||
// is the result accurate?
|
||||
if *indexEnabled {
|
||||
if _, ts := fsModified.get(); timestamp.Before(ts) {
|
||||
if _, ts := fsModified.Get(); timestamp.Before(ts) {
|
||||
// The index is older than the latest file system change under godoc's observation.
|
||||
result.Alert = "Indexing in progress: result may be inaccurate"
|
||||
}
|
||||
@ -1422,7 +1409,7 @@ func updateMetadata() {
|
||||
}
|
||||
}
|
||||
scan("/doc")
|
||||
docMetadata.set(metadata)
|
||||
docMetadata.Set(metadata)
|
||||
}
|
||||
|
||||
// Send a value on this channel to trigger a metadata refresh.
|
||||
@ -1455,7 +1442,7 @@ func refreshMetadataLoop() {
|
||||
// exists.
|
||||
//
|
||||
func metadataFor(relpath string) *Metadata {
|
||||
if m, _ := docMetadata.get(); m != nil {
|
||||
if m, _ := docMetadata.Get(); m != nil {
|
||||
meta := m.(map[string]*Metadata)
|
||||
// If metadata for this relpath exists, return it.
|
||||
if p := meta[relpath]; p != nil {
|
||||
@ -1479,7 +1466,7 @@ func metadataFor(relpath string) *Metadata {
|
||||
// under godoc's observation change so that the indexer is kicked on.
|
||||
//
|
||||
func invalidateIndex() {
|
||||
fsModified.set(nil)
|
||||
fsModified.Set(nil)
|
||||
refreshMetadata()
|
||||
}
|
||||
|
||||
@ -1487,16 +1474,16 @@ func invalidateIndex() {
|
||||
// than any of the file systems under godoc's observation.
|
||||
//
|
||||
func indexUpToDate() bool {
|
||||
_, fsTime := fsModified.get()
|
||||
_, siTime := searchIndex.get()
|
||||
_, fsTime := fsModified.Get()
|
||||
_, siTime := searchIndex.Get()
|
||||
return !fsTime.After(siTime)
|
||||
}
|
||||
|
||||
// feedDirnames feeds the directory names of all directories
|
||||
// under the file system given by root to channel c.
|
||||
//
|
||||
func feedDirnames(root *RWValue, c chan<- string) {
|
||||
if dir, _ := root.get(); dir != nil {
|
||||
func feedDirnames(root *util.RWValue, c chan<- string) {
|
||||
if dir, _ := root.Get(); dir != nil {
|
||||
for d := range dir.(*Directory).iter(false) {
|
||||
c <- d.Path
|
||||
}
|
||||
@ -1536,7 +1523,7 @@ func readIndex(filenames string) error {
|
||||
if err := x.Read(io.MultiReader(files...)); err != nil {
|
||||
return err
|
||||
}
|
||||
searchIndex.set(x)
|
||||
searchIndex.Set(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1547,7 +1534,7 @@ func updateIndex() {
|
||||
start := time.Now()
|
||||
index := NewIndex(fsDirnames(), *maxResults > 0, *indexThrottle)
|
||||
stop := time.Now()
|
||||
searchIndex.set(index)
|
||||
searchIndex.Set(index)
|
||||
if *verbose {
|
||||
secs := stop.Sub(start).Seconds()
|
||||
stats := index.Stats()
|
||||
|
@ -54,6 +54,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"code.google.com/p/go.tools/godoc/util"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -634,7 +636,7 @@ func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *
|
||||
return
|
||||
}
|
||||
|
||||
if isText(src) {
|
||||
if util.IsText(src) {
|
||||
// only add the file to the file set (for the full text index)
|
||||
file = x.fset.AddFile(filename, x.fset.Base(), len(src))
|
||||
file.SetLinesForContent(src)
|
||||
|
@ -212,7 +212,7 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
index, _ := searchIndex.get()
|
||||
index, _ := searchIndex.Get()
|
||||
err = index.(*Index).Write(f)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -27,6 +27,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/go.tools/godoc/vfs"
|
||||
)
|
||||
|
||||
// zipFI is the zip-file based implementation of FileInfo
|
||||
@ -107,7 +109,7 @@ func (fs *zipFS) stat(abspath string) (int, zipFI, error) {
|
||||
return i, zipFI{name, file}, nil
|
||||
}
|
||||
|
||||
func (fs *zipFS) Open(abspath string) (readSeekCloser, error) {
|
||||
func (fs *zipFS) Open(abspath string) (vfs.ReadSeekCloser, error) {
|
||||
_, fi, err := fs.stat(zipPath(abspath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1,44 +1,44 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
// This file contains support functionality for godoc.
|
||||
|
||||
package main
|
||||
// Package util contains utility types and functions for godoc.
|
||||
package util
|
||||
|
||||
import (
|
||||
"io"
|
||||
pathpkg "path"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.google.com/p/go.tools/godoc/vfs"
|
||||
)
|
||||
|
||||
// An RWValue wraps a value and permits mutually exclusive
|
||||
// access to it and records the time the value was last set.
|
||||
//
|
||||
type RWValue struct {
|
||||
mutex sync.RWMutex
|
||||
value interface{}
|
||||
timestamp time.Time // time of last set()
|
||||
}
|
||||
|
||||
func (v *RWValue) set(value interface{}) {
|
||||
func (v *RWValue) Set(value interface{}) {
|
||||
v.mutex.Lock()
|
||||
v.value = value
|
||||
v.timestamp = time.Now()
|
||||
v.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (v *RWValue) get() (interface{}, time.Time) {
|
||||
func (v *RWValue) Get() (interface{}, time.Time) {
|
||||
v.mutex.RLock()
|
||||
defer v.mutex.RUnlock()
|
||||
return v.value, v.timestamp
|
||||
}
|
||||
|
||||
// isText returns true if a significant prefix of s looks like correct UTF-8;
|
||||
// IsText returns whether a significant prefix of s looks like correct UTF-8;
|
||||
// that is, if it is likely that s is human-readable text.
|
||||
//
|
||||
func isText(s []byte) bool {
|
||||
func IsText(s []byte) bool {
|
||||
const max = 1024 // at least utf8.UTFMax
|
||||
if len(s) > max {
|
||||
s = s[0:max]
|
||||
@ -62,12 +62,16 @@ var textExt = map[string]bool{
|
||||
".js": false, // must be served raw
|
||||
}
|
||||
|
||||
// isTextFile returns true if the file has a known extension indicating
|
||||
// FileSystem is a minimal virtual filesystem.
|
||||
type FileSystem interface {
|
||||
Open(name string) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// IsTextFile returns whether the file has a known extension indicating
|
||||
// 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(filename string) bool {
|
||||
func IsTextFile(fs vfs.Opener, filename string) bool {
|
||||
// if the extension is known, use it for decision making
|
||||
if isText, found := textExt[pathpkg.Ext(filename)]; found {
|
||||
return isText
|
||||
@ -87,5 +91,5 @@ func isTextFile(filename string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return isText(buf[0:n])
|
||||
return IsText(buf[0:n])
|
||||
}
|
23
godoc/vfs/vfs.go
Normal file
23
godoc/vfs/vfs.go
Normal file
@ -0,0 +1,23 @@
|
||||
// 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 vfs defines virtual filesystem types.
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Opener is a minimal virtual filesystem that can only open
|
||||
// regular files.
|
||||
type Opener interface {
|
||||
Open(name string) (ReadSeekCloser, error)
|
||||
}
|
||||
|
||||
// A ReadSeekCloser can Read, Seek, and Close.
|
||||
type ReadSeekCloser interface {
|
||||
io.Reader
|
||||
io.Seeker
|
||||
io.Closer
|
||||
}
|
Loading…
Reference in New Issue
Block a user