1
0
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:
Brad Fitzpatrick 2013-07-17 14:44:18 +10:00
parent d79f4fe25b
commit 7526441b70
7 changed files with 77 additions and 63 deletions

View File

@ -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
}

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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
View 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
}