2009-06-16 10:14:06 -06:00
|
|
|
// Copyright 2009 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 main
|
|
|
|
|
|
|
|
import (
|
2009-12-15 16:33:31 -07:00
|
|
|
"bytes"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
2011-07-01 22:07:06 -06:00
|
|
|
"go/build"
|
2009-12-15 16:33:31 -07:00
|
|
|
"go/doc"
|
|
|
|
"go/printer"
|
|
|
|
"go/token"
|
|
|
|
"http"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
2011-03-06 15:33:23 -07:00
|
|
|
"path"
|
|
|
|
"path/filepath"
|
2010-03-30 18:37:42 -06:00
|
|
|
"regexp"
|
2010-03-19 16:20:20 -06:00
|
|
|
"runtime"
|
go/ast: use token.Pos instead of token.Position; adjust all dependent code
Specifically:
* lib/godoc:
- provide file set (FSet) argument to formatters where needed
* src/cmd:
- cgo, ebnflint, godoc, gofmt, goinstall: provide file set (fset) where needed
- godoc: remove local binary search with sort.Search (change by rsc),
extract file set for formatters
* src/pkg:
- exp/eval: remove embedded token.Position fields from nodes and replace
with named token.Pos fields; add corresponding Pos() accessor methods
- go/token: added file.Line(), changed signature of File.Position()
* test/fixedbugs/:
- bug206.go: change test to not rely on token.Pos details
* added various extra comments
* Runs all.bash
* gofmt formats all of src, misc w/o changes
* godoc runs
* performance:
- The new version of godoc consumes about the same space after indexing
has completed, but indexing is half the speed. Significant space savings
are expected from smaller ASTs, but since they are thrown away after a
file has been indexed, this is not visible anymore. The slower indexing
time is due to the much more expensive computation of line information.
However, with the new compressed position information, indexing can be
rewritten and simplified. Furthermore, computing the line info can be
done more efficiently.
New godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
44381 godoc 0.0% 0:38.00 4 19 149 145M 184K 148M 176M
2010/12/03 17:58:35 index updated (39.231s, 18505 unique words, 386387 spots)
2010/12/03 17:58:35 bytes=90858456 footprint=199182584
2010/12/03 17:58:36 bytes=47858568 footprint=167295224
Old godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
23167 godoc 0.0% 0:22.02 4 17 132 129M 184K 132M 173M
2010/12/03 14:51:32 index updated (24.892s, 18765 unique words, 393830 spots)
2010/12/03 14:51:32 bytes=66404528 footprint=163907832
2010/12/03 14:51:32 bytes=46282224 footprint=163907832
The different numbers for unique words/spots stem from the fact the the
two workspaces are not exactly identical. The new godoc maintains a large
file set data structure during indexing which (probably) is the reason
for the larger heap (90858456 vs 66404528) before garbage collection.
R=rsc, r
CC=golang-dev
https://golang.org/cl/3050041
2010-12-06 15:23:18 -07:00
|
|
|
"sort"
|
2009-12-15 16:33:31 -07:00
|
|
|
"strings"
|
2011-08-16 22:55:57 -06:00
|
|
|
"template"
|
2009-12-15 16:33:31 -07:00
|
|
|
"time"
|
2009-06-16 10:14:06 -06:00
|
|
|
)
|
|
|
|
|
2009-10-22 10:41:38 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Globals
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2009-10-22 10:41:38 -06:00
|
|
|
type delayTime struct {
|
2009-12-15 16:33:31 -07:00
|
|
|
RWValue
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2009-10-22 10:41:38 -06:00
|
|
|
func (dt *delayTime) backoff(max int) {
|
2009-12-15 16:33:31 -07:00
|
|
|
dt.mutex.Lock()
|
|
|
|
v := dt.value.(int) * 2
|
2009-10-22 10:41:38 -06:00
|
|
|
if v > max {
|
2009-11-09 13:07:39 -07:00
|
|
|
v = max
|
2009-10-22 10:41:38 -06:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
dt.value = v
|
2010-11-18 20:55:38 -07:00
|
|
|
// don't change dt.timestamp - calling backoff indicates an error condition
|
2009-12-15 16:33:31 -07:00
|
|
|
dt.mutex.Unlock()
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2009-12-15 16:33:31 -07:00
|
|
|
verbose = flag.Bool("v", false, "verbose mode")
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-02-18 18:40:50 -07:00
|
|
|
// file system roots
|
2010-11-17 12:03:33 -07:00
|
|
|
// TODO(gri) consider the invariant that goroot always end in '/'
|
2010-09-14 12:16:36 -06:00
|
|
|
goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
|
2011-01-10 16:34:29 -07:00
|
|
|
testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)")
|
2011-01-26 22:49:30 -07:00
|
|
|
pkgPath = flag.String("path", "", "additional package directories (colon-separated)")
|
2010-09-20 17:19:17 -06:00
|
|
|
filter = flag.String("filter", "", "filter file containing permitted package directory paths")
|
2010-09-14 13:03:26 -06:00
|
|
|
filterMin = flag.Int("filter_minutes", 0, "filter file update interval in minutes; disabled if <= 0")
|
2010-09-14 12:16:36 -06:00
|
|
|
filterDelay delayTime // actual filter update interval in minutes; usually filterDelay == filterMin, but filterDelay may back off exponentially
|
2009-06-16 10:14:06 -06:00
|
|
|
|
|
|
|
// layout control
|
2010-09-16 14:45:40 -06:00
|
|
|
tabwidth = flag.Int("tabwidth", 4, "tab width")
|
|
|
|
showTimestamps = flag.Bool("timestamps", true, "show timestamps with directory listings")
|
2011-03-22 16:44:23 -06:00
|
|
|
templateDir = flag.String("templates", "", "directory containing alternate template files")
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2011-05-02 14:28:02 -06:00
|
|
|
// search index
|
|
|
|
indexEnabled = flag.Bool("index", false, "enable search index")
|
|
|
|
maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
// file system mapping
|
2011-07-19 09:22:20 -06:00
|
|
|
fs FileSystem // the underlying file system for godoc
|
|
|
|
fsHttp http.FileSystem // the underlying file system for http
|
|
|
|
fsMap Mapping // user-defined mapping
|
|
|
|
fsTree RWValue // *Directory tree of packages, updated with each sync
|
|
|
|
pathFilter RWValue // filter used when building fsMap directory trees
|
|
|
|
fsModified RWValue // timestamp of last call to invalidateIndex
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
// http handlers
|
|
|
|
fileServer http.Handler // default file server
|
|
|
|
cmdHandler httpHandler
|
|
|
|
pkgHandler httpHandler
|
|
|
|
)
|
2009-11-03 20:40:26 -07:00
|
|
|
|
2010-02-16 13:49:41 -07:00
|
|
|
func initHandlers() {
|
2011-07-01 22:07:06 -06:00
|
|
|
paths := filepath.SplitList(*pkgPath)
|
|
|
|
for _, t := range build.Path {
|
|
|
|
if t.Goroot {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
paths = append(paths, t.SrcDir())
|
|
|
|
}
|
|
|
|
fsMap.Init(paths)
|
|
|
|
|
2011-07-19 09:22:20 -06:00
|
|
|
fileServer = http.FileServer(fsHttp)
|
2011-03-06 15:33:23 -07:00
|
|
|
cmdHandler = httpHandler{"/cmd/", filepath.Join(*goroot, "src", "cmd"), false}
|
|
|
|
pkgHandler = httpHandler{"/pkg/", filepath.Join(*goroot, "src", "pkg"), true}
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func registerPublicHandlers(mux *http.ServeMux) {
|
|
|
|
mux.Handle(cmdHandler.pattern, &cmdHandler)
|
|
|
|
mux.Handle(pkgHandler.pattern, &pkgHandler)
|
2010-04-26 23:35:12 -06:00
|
|
|
mux.HandleFunc("/doc/codewalk/", codewalk)
|
|
|
|
mux.HandleFunc("/search", search)
|
2011-02-18 11:46:20 -07:00
|
|
|
mux.Handle("/robots.txt", fileServer)
|
2010-04-26 23:35:12 -06:00
|
|
|
mux.HandleFunc("/", serveFile)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2011-01-10 16:34:29 -07:00
|
|
|
func initFSTree() {
|
2011-03-06 15:33:23 -07:00
|
|
|
fsTree.set(newDirectory(filepath.Join(*goroot, *testDir), nil, -1))
|
2011-01-10 16:34:29 -07:00
|
|
|
invalidateIndex()
|
|
|
|
}
|
|
|
|
|
2010-09-14 12:16:36 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Directory filters
|
|
|
|
|
|
|
|
// isParentOf returns true if p is a parent of (or the same as) q
|
|
|
|
// where p and q are directory paths.
|
|
|
|
func isParentOf(p, q string) bool {
|
|
|
|
n := len(p)
|
|
|
|
return strings.HasPrefix(q, p) && (len(q) <= n || q[n] == '/')
|
|
|
|
}
|
|
|
|
|
|
|
|
func setPathFilter(list []string) {
|
|
|
|
if len(list) == 0 {
|
|
|
|
pathFilter.set(nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2010-09-16 11:40:07 -06:00
|
|
|
// len(list) > 0
|
2010-09-14 12:16:36 -06:00
|
|
|
pathFilter.set(func(path string) bool {
|
2010-09-16 11:40:07 -06:00
|
|
|
// list is sorted in increasing order and for each path all its children are removed
|
go/ast: use token.Pos instead of token.Position; adjust all dependent code
Specifically:
* lib/godoc:
- provide file set (FSet) argument to formatters where needed
* src/cmd:
- cgo, ebnflint, godoc, gofmt, goinstall: provide file set (fset) where needed
- godoc: remove local binary search with sort.Search (change by rsc),
extract file set for formatters
* src/pkg:
- exp/eval: remove embedded token.Position fields from nodes and replace
with named token.Pos fields; add corresponding Pos() accessor methods
- go/token: added file.Line(), changed signature of File.Position()
* test/fixedbugs/:
- bug206.go: change test to not rely on token.Pos details
* added various extra comments
* Runs all.bash
* gofmt formats all of src, misc w/o changes
* godoc runs
* performance:
- The new version of godoc consumes about the same space after indexing
has completed, but indexing is half the speed. Significant space savings
are expected from smaller ASTs, but since they are thrown away after a
file has been indexed, this is not visible anymore. The slower indexing
time is due to the much more expensive computation of line information.
However, with the new compressed position information, indexing can be
rewritten and simplified. Furthermore, computing the line info can be
done more efficiently.
New godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
44381 godoc 0.0% 0:38.00 4 19 149 145M 184K 148M 176M
2010/12/03 17:58:35 index updated (39.231s, 18505 unique words, 386387 spots)
2010/12/03 17:58:35 bytes=90858456 footprint=199182584
2010/12/03 17:58:36 bytes=47858568 footprint=167295224
Old godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
23167 godoc 0.0% 0:22.02 4 17 132 129M 184K 132M 173M
2010/12/03 14:51:32 index updated (24.892s, 18765 unique words, 393830 spots)
2010/12/03 14:51:32 bytes=66404528 footprint=163907832
2010/12/03 14:51:32 bytes=46282224 footprint=163907832
The different numbers for unique words/spots stem from the fact the the
two workspaces are not exactly identical. The new godoc maintains a large
file set data structure during indexing which (probably) is the reason
for the larger heap (90858456 vs 66404528) before garbage collection.
R=rsc, r
CC=golang-dev
https://golang.org/cl/3050041
2010-12-06 15:23:18 -07:00
|
|
|
i := sort.Search(len(list), func(i int) bool { return list[i] > path })
|
|
|
|
// Now we have list[i-1] <= path < list[i].
|
|
|
|
// Path may be a child of list[i-1] or a parent of list[i].
|
|
|
|
return i > 0 && isParentOf(list[i-1], path) || i < len(list) && isParentOf(path, list[i])
|
2010-09-14 12:16:36 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func getPathFilter() func(string) bool {
|
|
|
|
f, _ := pathFilter.get()
|
|
|
|
if f != nil {
|
|
|
|
return f.(func(string) bool)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2010-09-14 14:59:45 -06:00
|
|
|
// readDirList reads a file containing a newline-separated list
|
2010-09-14 12:16:36 -06:00
|
|
|
// of directory paths and returns the list of paths.
|
|
|
|
func readDirList(filename string) ([]string, os.Error) {
|
2011-06-15 15:06:35 -06:00
|
|
|
contents, err := fs.ReadFile(filename)
|
2010-09-14 12:16:36 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2010-09-16 11:40:07 -06:00
|
|
|
// create a sorted list of valid directory names
|
2010-09-14 12:16:36 -06:00
|
|
|
filter := func(path string) bool {
|
2011-06-15 15:06:35 -06:00
|
|
|
d, e := fs.Lstat(path)
|
2011-02-24 11:22:32 -07:00
|
|
|
if e != nil && err == nil {
|
|
|
|
// remember first error and return it from readDirList
|
|
|
|
// so we have at least some information if things go bad
|
|
|
|
err = e
|
|
|
|
}
|
|
|
|
return e == nil && isPkgDir(d)
|
2010-09-14 12:16:36 -06:00
|
|
|
}
|
2011-06-27 17:43:14 -06:00
|
|
|
list := canonicalizePaths(strings.Split(string(contents), "\n"), filter)
|
2011-07-13 11:54:51 -06:00
|
|
|
// for each parent path, remove all its children q
|
2010-09-16 11:40:07 -06:00
|
|
|
// (requirement for binary search to work when filtering)
|
|
|
|
i := 0
|
|
|
|
for _, q := range list {
|
|
|
|
if i == 0 || !isParentOf(list[i-1], q) {
|
|
|
|
list[i] = q
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
2011-02-24 11:22:32 -07:00
|
|
|
return list[0:i], err
|
2010-09-14 12:16:36 -06:00
|
|
|
}
|
|
|
|
|
2010-11-18 20:55:38 -07:00
|
|
|
// updateMappedDirs computes the directory tree for
|
|
|
|
// each user-defined file system mapping. If a filter
|
|
|
|
// is provided, it is used to filter directories.
|
|
|
|
//
|
|
|
|
func updateMappedDirs(filter func(string) bool) {
|
2010-11-19 15:05:12 -07:00
|
|
|
if !fsMap.IsEmpty() {
|
|
|
|
fsMap.Iterate(func(path string, value *RWValue) bool {
|
|
|
|
value.set(newDirectory(path, filter, -1))
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
invalidateIndex()
|
|
|
|
}
|
2010-11-18 20:55:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func updateFilterFile() {
|
|
|
|
updateMappedDirs(nil) // no filter for accuracy
|
2010-09-14 12:16:36 -06:00
|
|
|
|
|
|
|
// collect directory tree leaf node paths
|
|
|
|
var buf bytes.Buffer
|
|
|
|
fsMap.Iterate(func(_ string, value *RWValue) bool {
|
|
|
|
v, _ := value.get()
|
|
|
|
if v != nil && v.(*Directory) != nil {
|
|
|
|
v.(*Directory).writeLeafs(&buf)
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
// update filter file
|
2010-09-14 17:54:38 -06:00
|
|
|
if err := writeFileAtomically(*filter, buf.Bytes()); err != nil {
|
log: new interface
New logging interface simplifies and generalizes.
1) Loggers now have only one output.
2) log.Stdout, Stderr, Crash and friends are gone.
Logging is now always to standard error by default.
3) log.Panic* replaces log.Crash*.
4) Exiting and panicking are not part of the logger's state; instead
the functions Exit* and Panic* simply call Exit or panic after
printing.
5) There is now one 'standard logger'. Instead of calling Stderr,
use Print etc. There are now triples, by analogy with fmt:
Print, Println, Printf
What was log.Stderr is now best represented by log.Println,
since there are now separate Print and Println functions
(and methods).
6) New functions SetOutput, SetFlags, and SetPrefix allow global
editing of the standard logger's properties. This is new
functionality. For instance, one can call
log.SetFlags(log.Lshortfile|log.Ltime|log.Lmicroseconds)
to get all logging output to show file name, line number, and
time stamp.
In short, for most purposes
log.Stderr -> log.Println or log.Print
log.Stderrf -> log.Printf
log.Crash -> log.Panicln or log.Panic
log.Crashf -> log.Panicf
log.Exit -> log.Exitln or log.Exit
log.Exitf -> log.Exitf (no change)
This has a slight breakage: since loggers now write only to one
output, existing calls to log.New() need to delete the second argument.
Also, custom loggers with exit or panic properties will need to be
reworked.
All package code updated to new interface.
The test has been reworked somewhat.
The old interface will be removed after the new release.
For now, its elements are marked 'deprecated' in their comments.
Fixes #1184.
R=rsc
CC=golang-dev
https://golang.org/cl/2419042
2010-10-12 13:59:18 -06:00
|
|
|
log.Printf("writeFileAtomically(%s): %s", *filter, err)
|
2010-09-14 12:16:36 -06:00
|
|
|
filterDelay.backoff(24 * 60) // back off exponentially, but try at least once a day
|
|
|
|
} else {
|
|
|
|
filterDelay.set(*filterMin) // revert to regular filter update schedule
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func initDirTrees() {
|
|
|
|
// setup initial path filter
|
|
|
|
if *filter != "" {
|
|
|
|
list, err := readDirList(*filter)
|
|
|
|
if err != nil {
|
2011-02-24 17:24:51 -07:00
|
|
|
log.Printf("readDirList(%s): %s", *filter, err)
|
|
|
|
}
|
|
|
|
if *verbose || len(list) == 0 {
|
|
|
|
log.Printf("found %d directory paths in file %s", len(list), *filter)
|
2010-09-14 12:16:36 -06:00
|
|
|
}
|
|
|
|
setPathFilter(list)
|
|
|
|
}
|
|
|
|
|
2010-11-18 20:55:38 -07:00
|
|
|
go updateMappedDirs(getPathFilter()) // use filter for speed
|
2010-09-14 12:16:36 -06:00
|
|
|
|
|
|
|
// start filter update goroutine, if enabled.
|
|
|
|
if *filter != "" && *filterMin > 0 {
|
|
|
|
filterDelay.set(*filterMin) // initial filter update delay
|
|
|
|
go func() {
|
|
|
|
for {
|
2010-09-14 14:59:45 -06:00
|
|
|
if *verbose {
|
log: new interface
New logging interface simplifies and generalizes.
1) Loggers now have only one output.
2) log.Stdout, Stderr, Crash and friends are gone.
Logging is now always to standard error by default.
3) log.Panic* replaces log.Crash*.
4) Exiting and panicking are not part of the logger's state; instead
the functions Exit* and Panic* simply call Exit or panic after
printing.
5) There is now one 'standard logger'. Instead of calling Stderr,
use Print etc. There are now triples, by analogy with fmt:
Print, Println, Printf
What was log.Stderr is now best represented by log.Println,
since there are now separate Print and Println functions
(and methods).
6) New functions SetOutput, SetFlags, and SetPrefix allow global
editing of the standard logger's properties. This is new
functionality. For instance, one can call
log.SetFlags(log.Lshortfile|log.Ltime|log.Lmicroseconds)
to get all logging output to show file name, line number, and
time stamp.
In short, for most purposes
log.Stderr -> log.Println or log.Print
log.Stderrf -> log.Printf
log.Crash -> log.Panicln or log.Panic
log.Crashf -> log.Panicf
log.Exit -> log.Exitln or log.Exit
log.Exitf -> log.Exitf (no change)
This has a slight breakage: since loggers now write only to one
output, existing calls to log.New() need to delete the second argument.
Also, custom loggers with exit or panic properties will need to be
reworked.
All package code updated to new interface.
The test has been reworked somewhat.
The old interface will be removed after the new release.
For now, its elements are marked 'deprecated' in their comments.
Fixes #1184.
R=rsc
CC=golang-dev
https://golang.org/cl/2419042
2010-10-12 13:59:18 -06:00
|
|
|
log.Printf("start update of %s", *filter)
|
2010-09-14 14:59:45 -06:00
|
|
|
}
|
2010-09-14 12:16:36 -06:00
|
|
|
updateFilterFile()
|
2010-09-14 14:59:45 -06:00
|
|
|
delay, _ := filterDelay.get()
|
2010-09-14 12:16:36 -06:00
|
|
|
if *verbose {
|
log: new interface
New logging interface simplifies and generalizes.
1) Loggers now have only one output.
2) log.Stdout, Stderr, Crash and friends are gone.
Logging is now always to standard error by default.
3) log.Panic* replaces log.Crash*.
4) Exiting and panicking are not part of the logger's state; instead
the functions Exit* and Panic* simply call Exit or panic after
printing.
5) There is now one 'standard logger'. Instead of calling Stderr,
use Print etc. There are now triples, by analogy with fmt:
Print, Println, Printf
What was log.Stderr is now best represented by log.Println,
since there are now separate Print and Println functions
(and methods).
6) New functions SetOutput, SetFlags, and SetPrefix allow global
editing of the standard logger's properties. This is new
functionality. For instance, one can call
log.SetFlags(log.Lshortfile|log.Ltime|log.Lmicroseconds)
to get all logging output to show file name, line number, and
time stamp.
In short, for most purposes
log.Stderr -> log.Println or log.Print
log.Stderrf -> log.Printf
log.Crash -> log.Panicln or log.Panic
log.Crashf -> log.Panicf
log.Exit -> log.Exitln or log.Exit
log.Exitf -> log.Exitf (no change)
This has a slight breakage: since loggers now write only to one
output, existing calls to log.New() need to delete the second argument.
Also, custom loggers with exit or panic properties will need to be
reworked.
All package code updated to new interface.
The test has been reworked somewhat.
The old interface will be removed after the new release.
For now, its elements are marked 'deprecated' in their comments.
Fixes #1184.
R=rsc
CC=golang-dev
https://golang.org/cl/2419042
2010-10-12 13:59:18 -06:00
|
|
|
log.Printf("next filter update in %dmin", delay.(int))
|
2010-09-14 12:16:36 -06:00
|
|
|
}
|
|
|
|
time.Sleep(int64(delay.(int)) * 60e9)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
2010-09-07 18:21:00 -06:00
|
|
|
// Path mapping
|
2009-11-07 14:17:53 -07:00
|
|
|
|
2011-03-06 15:33:23 -07:00
|
|
|
// Absolute paths are file system paths (backslash-separated on Windows),
|
|
|
|
// but relative paths are always slash-separated.
|
|
|
|
|
|
|
|
func absolutePath(relpath, defaultRoot string) string {
|
|
|
|
abspath := fsMap.ToAbsolute(relpath)
|
2010-02-16 12:20:55 -07:00
|
|
|
if abspath == "" {
|
|
|
|
// no user-defined mapping found; use default mapping
|
2011-03-06 15:33:23 -07:00
|
|
|
abspath = filepath.Join(defaultRoot, filepath.FromSlash(relpath))
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
|
|
|
return abspath
|
|
|
|
}
|
|
|
|
|
2011-03-06 15:33:23 -07:00
|
|
|
func relativeURL(abspath string) string {
|
|
|
|
relpath := fsMap.ToRelative(abspath)
|
2010-11-17 12:03:33 -07:00
|
|
|
if relpath == "" {
|
2011-03-06 15:33:23 -07:00
|
|
|
// prefix must end in a path separator
|
2010-11-17 12:03:33 -07:00
|
|
|
prefix := *goroot
|
2011-03-06 15:33:23 -07:00
|
|
|
if len(prefix) > 0 && prefix[len(prefix)-1] != filepath.Separator {
|
|
|
|
prefix += string(filepath.Separator)
|
2010-11-17 12:03:33 -07:00
|
|
|
}
|
2011-03-06 15:33:23 -07:00
|
|
|
if strings.HasPrefix(abspath, prefix) {
|
2010-11-17 12:03:33 -07:00
|
|
|
// no user-defined mapping found; use default mapping
|
2011-03-06 15:33:23 -07:00
|
|
|
relpath = filepath.ToSlash(abspath[len(prefix):])
|
2010-11-17 12:03:33 -07:00
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
|
|
|
// Only if path is an invalid absolute path is relpath == ""
|
|
|
|
// at this point. This should never happen since absolute paths
|
|
|
|
// are only created via godoc for files that do exist. However,
|
|
|
|
// it is ok to return ""; it will simply provide a link to the
|
|
|
|
// top of the pkg or src directories.
|
|
|
|
return relpath
|
|
|
|
}
|
|
|
|
|
2009-12-17 15:10:49 -07:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Tab conversion
|
|
|
|
|
2011-02-09 16:06:05 -07:00
|
|
|
var spaces = []byte(" ") // 32 spaces seems like a good number
|
2009-12-17 15:10:49 -07:00
|
|
|
|
|
|
|
const (
|
|
|
|
indenting = iota
|
|
|
|
collecting
|
|
|
|
)
|
|
|
|
|
|
|
|
// A tconv is an io.Writer filter for converting leading tabs into spaces.
|
|
|
|
type tconv struct {
|
|
|
|
output io.Writer
|
|
|
|
state int // indenting or collecting
|
|
|
|
indent int // valid if state == indenting
|
|
|
|
}
|
|
|
|
|
2009-12-28 18:24:53 -07:00
|
|
|
func (p *tconv) writeIndent() (err os.Error) {
|
|
|
|
i := p.indent
|
2011-02-09 16:06:05 -07:00
|
|
|
for i >= len(spaces) {
|
2009-12-17 15:10:49 -07:00
|
|
|
i -= len(spaces)
|
|
|
|
if _, err = p.output.Write(spaces); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2011-02-09 16:06:05 -07:00
|
|
|
// i < len(spaces)
|
|
|
|
if i > 0 {
|
|
|
|
_, err = p.output.Write(spaces[0:i])
|
|
|
|
}
|
2009-12-17 15:10:49 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *tconv) Write(data []byte) (n int, err os.Error) {
|
2011-02-09 16:06:05 -07:00
|
|
|
if len(data) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2009-12-17 15:10:49 -07:00
|
|
|
pos := 0 // valid if p.state == collecting
|
|
|
|
var b byte
|
|
|
|
for n, b = range data {
|
|
|
|
switch p.state {
|
|
|
|
case indenting:
|
2009-12-28 18:24:53 -07:00
|
|
|
switch b {
|
2011-02-09 16:06:05 -07:00
|
|
|
case '\t':
|
2009-12-28 18:24:53 -07:00
|
|
|
p.indent += *tabwidth
|
|
|
|
case '\n':
|
|
|
|
p.indent = 0
|
|
|
|
if _, err = p.output.Write(data[n : n+1]); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case ' ':
|
2009-12-17 15:10:49 -07:00
|
|
|
p.indent++
|
2009-12-28 18:24:53 -07:00
|
|
|
default:
|
2009-12-17 15:10:49 -07:00
|
|
|
p.state = collecting
|
|
|
|
pos = n
|
2009-12-28 18:24:53 -07:00
|
|
|
if err = p.writeIndent(); err != nil {
|
2009-12-17 15:10:49 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case collecting:
|
|
|
|
if b == '\n' {
|
|
|
|
p.state = indenting
|
|
|
|
p.indent = 0
|
|
|
|
if _, err = p.output.Write(data[pos : n+1]); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
n = len(data)
|
2011-02-09 16:06:05 -07:00
|
|
|
if pos < n && p.state == collecting {
|
2009-12-17 15:10:49 -07:00
|
|
|
_, err = p.output.Write(data[pos:])
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Templates
|
|
|
|
|
2011-02-09 10:52:32 -07:00
|
|
|
// Write an AST node to w.
|
|
|
|
func writeNode(w io.Writer, fset *token.FileSet, x interface{}) {
|
2009-12-17 15:10:49 -07:00
|
|
|
// convert trailing tabs into spaces using a tconv filter
|
|
|
|
// to ensure a good outcome in most browsers (there may still
|
|
|
|
// be tabs in comments and strings, but converting those into
|
|
|
|
// the right number of spaces is much harder)
|
2011-02-09 16:06:05 -07:00
|
|
|
//
|
|
|
|
// TODO(gri) rethink printer flags - perhaps tconv can be eliminated
|
|
|
|
// with an another printer mode (which is more efficiently
|
|
|
|
// implemented in the printer than here with another layer)
|
2011-02-09 10:52:32 -07:00
|
|
|
mode := printer.TabIndent | printer.UseSpaces
|
2011-03-22 15:14:26 -06:00
|
|
|
(&printer.Config{mode, *tabwidth}).Fprint(&tconv{output: w}, fset, x)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func filenameFunc(path string) string {
|
|
|
|
_, localname := filepath.Split(path)
|
|
|
|
return localname
|
go/ast: use token.Pos instead of token.Position; adjust all dependent code
Specifically:
* lib/godoc:
- provide file set (FSet) argument to formatters where needed
* src/cmd:
- cgo, ebnflint, godoc, gofmt, goinstall: provide file set (fset) where needed
- godoc: remove local binary search with sort.Search (change by rsc),
extract file set for formatters
* src/pkg:
- exp/eval: remove embedded token.Position fields from nodes and replace
with named token.Pos fields; add corresponding Pos() accessor methods
- go/token: added file.Line(), changed signature of File.Position()
* test/fixedbugs/:
- bug206.go: change test to not rely on token.Pos details
* added various extra comments
* Runs all.bash
* gofmt formats all of src, misc w/o changes
* godoc runs
* performance:
- The new version of godoc consumes about the same space after indexing
has completed, but indexing is half the speed. Significant space savings
are expected from smaller ASTs, but since they are thrown away after a
file has been indexed, this is not visible anymore. The slower indexing
time is due to the much more expensive computation of line information.
However, with the new compressed position information, indexing can be
rewritten and simplified. Furthermore, computing the line info can be
done more efficiently.
New godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
44381 godoc 0.0% 0:38.00 4 19 149 145M 184K 148M 176M
2010/12/03 17:58:35 index updated (39.231s, 18505 unique words, 386387 spots)
2010/12/03 17:58:35 bytes=90858456 footprint=199182584
2010/12/03 17:58:36 bytes=47858568 footprint=167295224
Old godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
23167 godoc 0.0% 0:22.02 4 17 132 129M 184K 132M 173M
2010/12/03 14:51:32 index updated (24.892s, 18765 unique words, 393830 spots)
2010/12/03 14:51:32 bytes=66404528 footprint=163907832
2010/12/03 14:51:32 bytes=46282224 footprint=163907832
The different numbers for unique words/spots stem from the fact the the
two workspaces are not exactly identical. The new godoc maintains a large
file set data structure during indexing which (probably) is the reason
for the larger heap (90858456 vs 66404528) before garbage collection.
R=rsc, r
CC=golang-dev
https://golang.org/cl/3050041
2010-12-06 15:23:18 -07:00
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func fileInfoNameFunc(fi FileInfo) string {
|
|
|
|
name := fi.Name()
|
|
|
|
if fi.IsDirectory() {
|
|
|
|
name += "/"
|
2009-08-03 10:53:00 -06:00
|
|
|
}
|
2011-08-12 16:43:10 -06:00
|
|
|
return name
|
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func fileInfoTimeFunc(fi FileInfo) string {
|
|
|
|
if t := fi.Mtime_ns(); t != 0 {
|
|
|
|
return time.SecondsToLocalTime(t / 1e9).String()
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
2011-08-12 16:43:10 -06:00
|
|
|
return "" // don't return epoch if time is obviously not set
|
2009-08-03 10:53:00 -06:00
|
|
|
}
|
|
|
|
|
2009-11-08 21:17:04 -07:00
|
|
|
// The strings in infoKinds must be properly html-escaped.
|
|
|
|
var infoKinds = [nKinds]string{
|
|
|
|
PackageClause: "package clause",
|
2010-03-02 14:46:51 -07:00
|
|
|
ImportDecl: "import decl",
|
|
|
|
ConstDecl: "const decl",
|
|
|
|
TypeDecl: "type decl",
|
|
|
|
VarDecl: "var decl",
|
|
|
|
FuncDecl: "func decl",
|
|
|
|
MethodDecl: "method decl",
|
|
|
|
Use: "use",
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func infoKind_htmlFunc(kind SpotKind) string {
|
|
|
|
return infoKinds[kind] // infoKind entries are html-escaped
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func infoLineFunc(info SpotInfo) int {
|
2009-12-15 16:33:31 -07:00
|
|
|
line := info.Lori()
|
2009-10-27 11:34:31 -06:00
|
|
|
if info.IsIndex() {
|
2009-12-15 16:33:31 -07:00
|
|
|
index, _ := searchIndex.get()
|
2010-03-19 16:20:20 -06:00
|
|
|
if index != nil {
|
|
|
|
line = index.(*Index).Snippet(line).Line
|
|
|
|
} else {
|
|
|
|
// no line information available because
|
2010-03-19 23:48:08 -06:00
|
|
|
// we don't have an index - this should
|
|
|
|
// never happen; be conservative and don't
|
|
|
|
// crash
|
2010-03-19 16:20:20 -06:00
|
|
|
line = 0
|
|
|
|
}
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
2011-08-12 16:43:10 -06:00
|
|
|
return line
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func infoSnippet_htmlFunc(info SpotInfo) string {
|
2009-10-27 11:34:31 -06:00
|
|
|
if info.IsIndex() {
|
2009-12-15 16:33:31 -07:00
|
|
|
index, _ := searchIndex.get()
|
2011-08-12 16:43:10 -06:00
|
|
|
// Snippet.Text was HTML-escaped when it was generated
|
|
|
|
return index.(*Index).Snippet(info.Lori()).Text
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
2011-08-12 16:43:10 -06:00
|
|
|
return `<span class="alert">no snippet text available</span>`
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func nodeFunc(node interface{}, fset *token.FileSet) string {
|
2011-08-12 11:06:32 -06:00
|
|
|
var buf bytes.Buffer
|
|
|
|
writeNode(&buf, fset, node)
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func node_htmlFunc(node interface{}, fset *token.FileSet) string {
|
2011-08-12 11:06:32 -06:00
|
|
|
var buf1 bytes.Buffer
|
|
|
|
writeNode(&buf1, fset, node)
|
|
|
|
var buf2 bytes.Buffer
|
|
|
|
FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
|
|
|
|
return buf2.String()
|
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func comment_htmlFunc(comment string) string {
|
2011-08-12 11:06:32 -06:00
|
|
|
var buf bytes.Buffer
|
|
|
|
// TODO(gri) Provide list of words (e.g. function parameters)
|
|
|
|
// to be emphasized by ToHTML.
|
|
|
|
doc.ToHTML(&buf, []byte(comment), nil) // does html-escaping
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
func pkgLinkFunc(path string) string {
|
|
|
|
relpath := relativeURL(path)
|
|
|
|
// because of the irregular mapping under goroot
|
|
|
|
// we need to correct certain relative paths
|
|
|
|
if strings.HasPrefix(relpath, "src/pkg/") {
|
|
|
|
relpath = relpath[len("src/pkg/"):]
|
2011-08-12 11:06:32 -06:00
|
|
|
}
|
2011-08-12 16:43:10 -06:00
|
|
|
return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL
|
2011-08-12 11:06:32 -06:00
|
|
|
}
|
|
|
|
|
2011-08-12 17:28:55 -06:00
|
|
|
func posLink_urlFunc(node ast.Node, fset *token.FileSet) string {
|
2011-08-12 16:43:10 -06:00
|
|
|
var relpath string
|
|
|
|
var line int
|
|
|
|
var low, high int // selection
|
|
|
|
|
|
|
|
if p := node.Pos(); p.IsValid() {
|
|
|
|
pos := fset.Position(p)
|
|
|
|
relpath = relativeURL(pos.Filename)
|
|
|
|
line = pos.Line
|
|
|
|
low = pos.Offset
|
|
|
|
}
|
|
|
|
if p := node.End(); p.IsValid() {
|
|
|
|
high = fset.Position(p).Offset
|
2011-08-12 11:06:32 -06:00
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
var buf bytes.Buffer
|
2011-08-15 16:15:54 -06:00
|
|
|
template.HTMLEscape(&buf, []byte(relpath))
|
2011-08-12 16:43:10 -06:00
|
|
|
// selection ranges are of form "s=low:high"
|
|
|
|
if low < high {
|
2011-08-12 17:28:55 -06:00
|
|
|
fmt.Fprintf(&buf, "?s=%d:%d", low, high) // no need for URL escaping
|
2011-08-12 16:43:10 -06:00
|
|
|
// if we have a selection, position the page
|
|
|
|
// such that the selection is a bit below the top
|
|
|
|
line -= 10
|
|
|
|
if line < 1 {
|
|
|
|
line = 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// line id's in html-printed source are of the
|
|
|
|
// form "L%d" where %d stands for the line number
|
|
|
|
if line > 0 {
|
2011-08-12 17:28:55 -06:00
|
|
|
fmt.Fprintf(&buf, "#L%d", line) // no need for URL escaping
|
2011-08-12 16:43:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return buf.String()
|
2011-08-12 11:06:32 -06:00
|
|
|
}
|
|
|
|
|
2011-08-12 16:43:10 -06:00
|
|
|
// fmap describes the template functions installed with all godoc templates.
|
2011-08-12 17:28:55 -06:00
|
|
|
// Convention: template function names ending in "_html" or "_url" produce
|
|
|
|
// HTML- or URL-escaped strings; all other function results may
|
|
|
|
// require explicit escaping in the template.
|
2011-08-12 16:43:10 -06:00
|
|
|
var fmap = template.FuncMap{
|
|
|
|
// various helpers
|
|
|
|
"filename": filenameFunc,
|
|
|
|
"repeat": strings.Repeat,
|
|
|
|
|
|
|
|
// accss to FileInfos (directory listings)
|
2011-08-12 11:06:32 -06:00
|
|
|
"fileInfoName": fileInfoNameFunc,
|
|
|
|
"fileInfoTime": fileInfoTimeFunc,
|
2011-08-12 16:43:10 -06:00
|
|
|
|
|
|
|
// access to search result information
|
|
|
|
"infoKind_html": infoKind_htmlFunc,
|
|
|
|
"infoLine": infoLineFunc,
|
|
|
|
"infoSnippet_html": infoSnippet_htmlFunc,
|
|
|
|
|
|
|
|
// formatting of AST nodes
|
|
|
|
"node": nodeFunc,
|
|
|
|
"node_html": node_htmlFunc,
|
|
|
|
"comment_html": comment_htmlFunc,
|
|
|
|
|
|
|
|
// support for URL attributes
|
2011-08-12 17:28:55 -06:00
|
|
|
"pkgLink": pkgLinkFunc,
|
|
|
|
"srcLink": relativeURL,
|
|
|
|
"posLink_url": posLink_urlFunc,
|
2011-08-12 16:43:10 -06:00
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
|
|
|
|
func readTemplate(name string) *template.Template {
|
2011-03-06 15:33:23 -07:00
|
|
|
path := filepath.Join(*goroot, "lib", "godoc", name)
|
2011-03-22 16:44:23 -06:00
|
|
|
if *templateDir != "" {
|
|
|
|
defaultpath := path
|
|
|
|
path = filepath.Join(*templateDir, name)
|
2011-06-15 15:06:35 -06:00
|
|
|
if _, err := fs.Stat(path); err != nil {
|
2011-03-22 16:44:23 -06:00
|
|
|
log.Print("readTemplate:", err)
|
|
|
|
path = defaultpath
|
|
|
|
}
|
|
|
|
}
|
2011-08-20 13:39:38 -06:00
|
|
|
|
|
|
|
// use underlying file system fs to read the template file
|
|
|
|
// (cannot use template ParseFile functions directly)
|
|
|
|
data, err := fs.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("readTemplate: ", err)
|
|
|
|
}
|
|
|
|
// be explicit with errors (for app engine use)
|
|
|
|
t, err := template.New(name).Funcs(fmap).Parse(string(data))
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("readTemplate: ", err)
|
|
|
|
}
|
|
|
|
return t
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2009-10-22 10:41:38 -06:00
|
|
|
var (
|
2010-04-26 23:35:12 -06:00
|
|
|
codewalkHTML,
|
|
|
|
codewalkdirHTML,
|
2009-11-08 22:46:20 -07:00
|
|
|
dirlistHTML,
|
2010-03-02 18:23:07 -07:00
|
|
|
errorHTML,
|
|
|
|
godocHTML,
|
|
|
|
packageHTML,
|
|
|
|
packageText,
|
2010-03-16 15:17:42 -06:00
|
|
|
searchHTML,
|
2011-01-10 16:34:29 -07:00
|
|
|
searchText *template.Template
|
2009-10-22 10:41:38 -06:00
|
|
|
)
|
2009-06-16 10:14:06 -06:00
|
|
|
|
|
|
|
func readTemplates() {
|
2010-02-18 18:40:50 -07:00
|
|
|
// have to delay until after flags processing since paths depend on goroot
|
2010-04-26 23:35:12 -06:00
|
|
|
codewalkHTML = readTemplate("codewalk.html")
|
|
|
|
codewalkdirHTML = readTemplate("codewalkdir.html")
|
2009-12-15 16:33:31 -07:00
|
|
|
dirlistHTML = readTemplate("dirlist.html")
|
2010-02-16 12:20:55 -07:00
|
|
|
errorHTML = readTemplate("error.html")
|
2009-12-15 16:33:31 -07:00
|
|
|
godocHTML = readTemplate("godoc.html")
|
|
|
|
packageHTML = readTemplate("package.html")
|
|
|
|
packageText = readTemplate("package.txt")
|
|
|
|
searchHTML = readTemplate("search.html")
|
2010-03-19 13:46:43 -06:00
|
|
|
searchText = readTemplate("search.txt")
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Generic HTML wrapper
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func servePage(w http.ResponseWriter, title, subtitle, query string, content []byte) {
|
2010-11-17 12:03:33 -07:00
|
|
|
d := struct {
|
2011-05-02 14:28:02 -06:00
|
|
|
Title string
|
|
|
|
Subtitle string
|
|
|
|
PkgRoots []string
|
|
|
|
SearchBox bool
|
|
|
|
Query string
|
|
|
|
Version string
|
|
|
|
Menu []byte
|
|
|
|
Content []byte
|
2010-11-17 12:03:33 -07:00
|
|
|
}{
|
|
|
|
title,
|
|
|
|
subtitle,
|
|
|
|
fsMap.PrefixList(),
|
2011-05-02 14:28:02 -06:00
|
|
|
*indexEnabled,
|
2010-11-17 12:03:33 -07:00
|
|
|
query,
|
|
|
|
runtime.Version(),
|
|
|
|
nil,
|
|
|
|
content,
|
2009-12-15 16:33:31 -07:00
|
|
|
}
|
2009-10-01 15:08:00 -06:00
|
|
|
|
2011-02-09 15:23:01 -07:00
|
|
|
if err := godocHTML.Execute(w, &d); err != nil {
|
log: new interface
New logging interface simplifies and generalizes.
1) Loggers now have only one output.
2) log.Stdout, Stderr, Crash and friends are gone.
Logging is now always to standard error by default.
3) log.Panic* replaces log.Crash*.
4) Exiting and panicking are not part of the logger's state; instead
the functions Exit* and Panic* simply call Exit or panic after
printing.
5) There is now one 'standard logger'. Instead of calling Stderr,
use Print etc. There are now triples, by analogy with fmt:
Print, Println, Printf
What was log.Stderr is now best represented by log.Println,
since there are now separate Print and Println functions
(and methods).
6) New functions SetOutput, SetFlags, and SetPrefix allow global
editing of the standard logger's properties. This is new
functionality. For instance, one can call
log.SetFlags(log.Lshortfile|log.Ltime|log.Lmicroseconds)
to get all logging output to show file name, line number, and
time stamp.
In short, for most purposes
log.Stderr -> log.Println or log.Print
log.Stderrf -> log.Printf
log.Crash -> log.Panicln or log.Panic
log.Crashf -> log.Panicf
log.Exit -> log.Exitln or log.Exit
log.Exitf -> log.Exitf (no change)
This has a slight breakage: since loggers now write only to one
output, existing calls to log.New() need to delete the second argument.
Also, custom loggers with exit or panic properties will need to be
reworked.
All package code updated to new interface.
The test has been reworked somewhat.
The old interface will be removed after the new release.
For now, its elements are marked 'deprecated' in their comments.
Fixes #1184.
R=rsc
CC=golang-dev
https://golang.org/cl/2419042
2010-10-12 13:59:18 -06:00
|
|
|
log.Printf("godocHTML.Execute: %s", err)
|
2009-08-03 10:53:00 -06:00
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func serveText(w http.ResponseWriter, text []byte) {
|
2011-03-09 10:41:01 -07:00
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
2010-09-28 22:30:12 -06:00
|
|
|
w.Write(text)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Files
|
|
|
|
|
2009-10-01 15:08:00 -06:00
|
|
|
var (
|
2010-03-30 18:37:42 -06:00
|
|
|
titleRx = regexp.MustCompile(`<!-- title ([^\-]*)-->`)
|
|
|
|
subtitleRx = regexp.MustCompile(`<!-- subtitle ([^\-]*)-->`)
|
|
|
|
firstCommentRx = regexp.MustCompile(`<!--([^\-]*)-->`)
|
2009-10-01 15:08:00 -06:00
|
|
|
)
|
|
|
|
|
2010-03-30 18:37:42 -06:00
|
|
|
func extractString(src []byte, rx *regexp.Regexp) (s string) {
|
2010-08-12 12:28:50 -06:00
|
|
|
m := rx.FindSubmatch(src)
|
|
|
|
if m != nil {
|
|
|
|
s = strings.TrimSpace(string(m[1]))
|
2009-10-01 15:08:00 -06:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-10-01 15:08:00 -06:00
|
|
|
}
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
|
2009-10-01 15:08:00 -06:00
|
|
|
// get HTML body contents
|
2011-06-15 15:06:35 -06:00
|
|
|
src, err := fs.ReadFile(abspath)
|
2009-10-01 15:08:00 -06:00
|
|
|
if err != nil {
|
2011-06-15 15:06:35 -06:00
|
|
|
log.Printf("ReadFile: %s", err)
|
2010-09-28 22:30:12 -06:00
|
|
|
serveError(w, r, relpath, err)
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-10-01 15:08:00 -06:00
|
|
|
}
|
|
|
|
|
2010-01-13 14:09:33 -07:00
|
|
|
// if it begins with "<!DOCTYPE " assume it is standalone
|
|
|
|
// html that doesn't need the template wrapping.
|
2010-02-25 17:01:29 -07:00
|
|
|
if bytes.HasPrefix(src, []byte("<!DOCTYPE ")) {
|
2010-09-28 22:30:12 -06:00
|
|
|
w.Write(src)
|
2010-01-13 14:09:33 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2009-10-01 15:08:00 -06:00
|
|
|
// if it's the language spec, add tags to EBNF productions
|
2010-02-16 12:20:55 -07:00
|
|
|
if strings.HasSuffix(abspath, "go_spec.html") {
|
2009-12-15 16:33:31 -07:00
|
|
|
var buf bytes.Buffer
|
|
|
|
linkify(&buf, src)
|
|
|
|
src = buf.Bytes()
|
2009-10-01 15:08:00 -06:00
|
|
|
}
|
|
|
|
|
2010-03-30 18:37:42 -06:00
|
|
|
// get title and subtitle, if any
|
|
|
|
title := extractString(src, titleRx)
|
|
|
|
if title == "" {
|
|
|
|
// no title found; try first comment for backward-compatibility
|
|
|
|
title = extractString(src, firstCommentRx)
|
|
|
|
}
|
|
|
|
subtitle := extractString(src, subtitleRx)
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
servePage(w, title, subtitle, "", src)
|
2009-10-01 15:08:00 -06:00
|
|
|
}
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
func applyTemplate(t *template.Template, name string, data interface{}) []byte {
|
|
|
|
var buf bytes.Buffer
|
2011-02-09 15:23:01 -07:00
|
|
|
if err := t.Execute(&buf, data); err != nil {
|
log: new interface
New logging interface simplifies and generalizes.
1) Loggers now have only one output.
2) log.Stdout, Stderr, Crash and friends are gone.
Logging is now always to standard error by default.
3) log.Panic* replaces log.Crash*.
4) Exiting and panicking are not part of the logger's state; instead
the functions Exit* and Panic* simply call Exit or panic after
printing.
5) There is now one 'standard logger'. Instead of calling Stderr,
use Print etc. There are now triples, by analogy with fmt:
Print, Println, Printf
What was log.Stderr is now best represented by log.Println,
since there are now separate Print and Println functions
(and methods).
6) New functions SetOutput, SetFlags, and SetPrefix allow global
editing of the standard logger's properties. This is new
functionality. For instance, one can call
log.SetFlags(log.Lshortfile|log.Ltime|log.Lmicroseconds)
to get all logging output to show file name, line number, and
time stamp.
In short, for most purposes
log.Stderr -> log.Println or log.Print
log.Stderrf -> log.Printf
log.Crash -> log.Panicln or log.Panic
log.Crashf -> log.Panicf
log.Exit -> log.Exitln or log.Exit
log.Exitf -> log.Exitf (no change)
This has a slight breakage: since loggers now write only to one
output, existing calls to log.New() need to delete the second argument.
Also, custom loggers with exit or panic properties will need to be
reworked.
All package code updated to new interface.
The test has been reworked somewhat.
The old interface will be removed after the new release.
For now, its elements are marked 'deprecated' in their comments.
Fixes #1184.
R=rsc
CC=golang-dev
https://golang.org/cl/2419042
2010-10-12 13:59:18 -06:00
|
|
|
log.Printf("%s.Execute: %s", name, err)
|
2009-08-03 10:53:00 -06:00
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
return buf.Bytes()
|
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
|
2011-03-06 15:33:23 -07:00
|
|
|
if canonical := path.Clean(r.URL.Path) + "/"; r.URL.Path != canonical {
|
2010-09-28 22:30:12 -06:00
|
|
|
http.Redirect(w, r, canonical, http.StatusMovedPermanently)
|
2009-12-15 16:33:31 -07:00
|
|
|
redirected = true
|
2009-11-07 22:12:46 -07:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-11-07 22:12:46 -07:00
|
|
|
}
|
|
|
|
|
2011-01-10 16:34:29 -07:00
|
|
|
func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
|
2011-06-15 15:06:35 -06:00
|
|
|
src, err := fs.ReadFile(abspath)
|
2009-11-08 01:40:43 -07:00
|
|
|
if err != nil {
|
2011-06-15 15:06:35 -06:00
|
|
|
log.Printf("ReadFile: %s", err)
|
2010-09-28 22:30:12 -06:00
|
|
|
serveError(w, r, relpath, err)
|
2010-02-16 12:20:55 -07:00
|
|
|
return
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
|
|
|
|
2011-02-09 10:52:32 -07:00
|
|
|
var buf bytes.Buffer
|
|
|
|
buf.WriteString("<pre>")
|
2011-03-06 15:33:23 -07:00
|
|
|
FormatText(&buf, src, 1, filepath.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s")))
|
2011-02-09 10:52:32 -07:00
|
|
|
buf.WriteString("</pre>")
|
|
|
|
|
|
|
|
servePage(w, title+" "+relpath, "", "", buf.Bytes())
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
|
|
|
|
if redirect(w, r) {
|
2009-11-09 13:07:39 -07:00
|
|
|
return
|
2009-11-07 22:12:46 -07:00
|
|
|
}
|
|
|
|
|
2011-06-15 15:06:35 -06:00
|
|
|
list, err := fs.ReadDir(abspath)
|
2009-11-07 22:12:46 -07:00
|
|
|
if err != nil {
|
2011-06-15 15:06:35 -06:00
|
|
|
log.Printf("ReadDir: %s", err)
|
2010-09-28 22:30:12 -06:00
|
|
|
serveError(w, r, relpath, err)
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-11-07 22:12:46 -07:00
|
|
|
}
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
contents := applyTemplate(dirlistHTML, "dirlistHTML", list)
|
2010-09-28 22:30:12 -06:00
|
|
|
servePage(w, "Directory "+relpath, "", "", contents)
|
2009-11-07 22:12:46 -07:00
|
|
|
}
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func serveFile(w http.ResponseWriter, r *http.Request) {
|
2010-02-16 12:20:55 -07:00
|
|
|
relpath := r.URL.Path[1:] // serveFile URL paths start with '/'
|
2010-03-19 16:20:20 -06:00
|
|
|
abspath := absolutePath(relpath, *goroot)
|
2009-10-01 15:08:00 -06:00
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
// pick off special cases and hand the rest to the standard file server
|
2010-02-16 12:20:55 -07:00
|
|
|
switch r.URL.Path {
|
|
|
|
case "/":
|
2011-03-06 15:33:23 -07:00
|
|
|
serveHTMLDoc(w, r, filepath.Join(*goroot, "doc", "root.html"), "doc/root.html")
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
case "/doc/root.html":
|
2009-06-16 10:14:06 -06:00
|
|
|
// hide landing page from its real name
|
2010-09-28 22:30:12 -06:00
|
|
|
http.Redirect(w, r, "/", http.StatusMovedPermanently)
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
2009-10-01 15:08:00 -06:00
|
|
|
|
2011-03-06 15:33:23 -07:00
|
|
|
switch path.Ext(relpath) {
|
2010-02-16 12:20:55 -07:00
|
|
|
case ".html":
|
2011-03-06 15:33:23 -07:00
|
|
|
if strings.HasSuffix(relpath, "/index.html") {
|
2010-01-06 16:59:03 -07:00
|
|
|
// We'll show index.html for the directory.
|
|
|
|
// Use the dir/ version as canonical instead of dir/index.html.
|
2010-09-28 22:30:12 -06:00
|
|
|
http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently)
|
2010-01-06 16:59:03 -07:00
|
|
|
return
|
|
|
|
}
|
2010-09-28 22:30:12 -06:00
|
|
|
serveHTMLDoc(w, r, abspath, relpath)
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
case ".go":
|
2011-01-10 16:34:29 -07:00
|
|
|
serveTextFile(w, r, abspath, relpath, "Source file")
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2011-06-15 15:06:35 -06:00
|
|
|
dir, err := fs.Lstat(abspath)
|
2009-11-08 01:40:43 -07:00
|
|
|
if err != nil {
|
log: new interface
New logging interface simplifies and generalizes.
1) Loggers now have only one output.
2) log.Stdout, Stderr, Crash and friends are gone.
Logging is now always to standard error by default.
3) log.Panic* replaces log.Crash*.
4) Exiting and panicking are not part of the logger's state; instead
the functions Exit* and Panic* simply call Exit or panic after
printing.
5) There is now one 'standard logger'. Instead of calling Stderr,
use Print etc. There are now triples, by analogy with fmt:
Print, Println, Printf
What was log.Stderr is now best represented by log.Println,
since there are now separate Print and Println functions
(and methods).
6) New functions SetOutput, SetFlags, and SetPrefix allow global
editing of the standard logger's properties. This is new
functionality. For instance, one can call
log.SetFlags(log.Lshortfile|log.Ltime|log.Lmicroseconds)
to get all logging output to show file name, line number, and
time stamp.
In short, for most purposes
log.Stderr -> log.Println or log.Print
log.Stderrf -> log.Printf
log.Crash -> log.Panicln or log.Panic
log.Crashf -> log.Panicf
log.Exit -> log.Exitln or log.Exit
log.Exitf -> log.Exitf (no change)
This has a slight breakage: since loggers now write only to one
output, existing calls to log.New() need to delete the second argument.
Also, custom loggers with exit or panic properties will need to be
reworked.
All package code updated to new interface.
The test has been reworked somewhat.
The old interface will be removed after the new release.
For now, its elements are marked 'deprecated' in their comments.
Fixes #1184.
R=rsc
CC=golang-dev
https://golang.org/cl/2419042
2010-10-12 13:59:18 -06:00
|
|
|
log.Print(err)
|
2010-09-28 22:30:12 -06:00
|
|
|
serveError(w, r, relpath, err)
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
2009-11-07 22:12:46 -07:00
|
|
|
|
2009-11-08 01:40:43 -07:00
|
|
|
if dir != nil && dir.IsDirectory() {
|
2010-09-28 22:30:12 -06:00
|
|
|
if redirect(w, r) {
|
2010-01-31 23:17:25 -07:00
|
|
|
return
|
|
|
|
}
|
2011-03-06 15:33:23 -07:00
|
|
|
if index := filepath.Join(abspath, "index.html"); isTextFile(index) {
|
|
|
|
serveHTMLDoc(w, r, index, relativeURL(index))
|
2010-01-06 16:59:03 -07:00
|
|
|
return
|
|
|
|
}
|
2010-09-28 22:30:12 -06:00
|
|
|
serveDirectory(w, r, abspath, relpath)
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
2009-11-07 22:12:46 -07:00
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
if isTextFile(abspath) {
|
2011-01-10 16:34:29 -07:00
|
|
|
serveTextFile(w, r, abspath, relpath, "Text file")
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
2009-11-08 01:40:43 -07:00
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
fileServer.ServeHTTP(w, r)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Packages
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
// Fake package file and name for commands. Contains the command documentation.
|
|
|
|
const fakePkgFile = "doc.go"
|
2009-11-03 20:40:26 -07:00
|
|
|
const fakePkgName = "documentation"
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2011-08-15 20:05:52 -06:00
|
|
|
// Fake relative package path for built-ins. Documentation for all globals
|
|
|
|
// (not just exported ones) will be shown for packages in this directory.
|
|
|
|
const builtinPkgPath = "builtin/"
|
|
|
|
|
2010-03-29 19:06:53 -06:00
|
|
|
type PageInfoMode uint
|
|
|
|
|
|
|
|
const (
|
|
|
|
exportsOnly PageInfoMode = 1 << iota // only keep exported stuff
|
2011-02-01 13:47:35 -07:00
|
|
|
genDoc // generate documentation
|
2010-03-29 19:06:53 -06:00
|
|
|
)
|
|
|
|
|
2009-07-29 18:01:09 -06:00
|
|
|
type PageInfo struct {
|
2010-02-16 12:20:55 -07:00
|
|
|
Dirname string // directory containing the package
|
2010-03-12 19:16:21 -07:00
|
|
|
PList []string // list of package names found
|
go/ast: use token.Pos instead of token.Position; adjust all dependent code
Specifically:
* lib/godoc:
- provide file set (FSet) argument to formatters where needed
* src/cmd:
- cgo, ebnflint, godoc, gofmt, goinstall: provide file set (fset) where needed
- godoc: remove local binary search with sort.Search (change by rsc),
extract file set for formatters
* src/pkg:
- exp/eval: remove embedded token.Position fields from nodes and replace
with named token.Pos fields; add corresponding Pos() accessor methods
- go/token: added file.Line(), changed signature of File.Position()
* test/fixedbugs/:
- bug206.go: change test to not rely on token.Pos details
* added various extra comments
* Runs all.bash
* gofmt formats all of src, misc w/o changes
* godoc runs
* performance:
- The new version of godoc consumes about the same space after indexing
has completed, but indexing is half the speed. Significant space savings
are expected from smaller ASTs, but since they are thrown away after a
file has been indexed, this is not visible anymore. The slower indexing
time is due to the much more expensive computation of line information.
However, with the new compressed position information, indexing can be
rewritten and simplified. Furthermore, computing the line info can be
done more efficiently.
New godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
44381 godoc 0.0% 0:38.00 4 19 149 145M 184K 148M 176M
2010/12/03 17:58:35 index updated (39.231s, 18505 unique words, 386387 spots)
2010/12/03 17:58:35 bytes=90858456 footprint=199182584
2010/12/03 17:58:36 bytes=47858568 footprint=167295224
Old godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
23167 godoc 0.0% 0:22.02 4 17 132 129M 184K 132M 173M
2010/12/03 14:51:32 index updated (24.892s, 18765 unique words, 393830 spots)
2010/12/03 14:51:32 bytes=66404528 footprint=163907832
2010/12/03 14:51:32 bytes=46282224 footprint=163907832
The different numbers for unique words/spots stem from the fact the the
two workspaces are not exactly identical. The new godoc maintains a large
file set data structure during indexing which (probably) is the reason
for the larger heap (90858456 vs 66404528) before garbage collection.
R=rsc, r
CC=golang-dev
https://golang.org/cl/3050041
2010-12-06 15:23:18 -07:00
|
|
|
FSet *token.FileSet // corresponding file set
|
2010-03-12 19:16:21 -07:00
|
|
|
PAst *ast.File // nil if no single AST with package exports
|
|
|
|
PDoc *doc.PackageDoc // nil if no single package documentation
|
2010-03-10 16:22:22 -07:00
|
|
|
Dirs *DirList // nil if no directory information
|
2010-09-16 14:45:40 -06:00
|
|
|
DirTime int64 // directory time stamp in seconds since epoch
|
2010-02-16 12:20:55 -07:00
|
|
|
IsPkg bool // false if this is not documenting a real package
|
2010-08-10 10:52:02 -06:00
|
|
|
Err os.Error // directory read error or nil
|
2009-11-03 20:40:26 -07:00
|
|
|
}
|
|
|
|
|
2011-02-14 18:41:47 -07:00
|
|
|
func (info *PageInfo) IsEmpty() bool {
|
|
|
|
return info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil
|
|
|
|
}
|
|
|
|
|
2009-11-03 20:40:26 -07:00
|
|
|
type httpHandler struct {
|
2009-12-15 16:33:31 -07:00
|
|
|
pattern string // url pattern; e.g. "/pkg/"
|
|
|
|
fsRoot string // file system root to which the pattern is mapped
|
|
|
|
isPkg bool // true if this handler serves real package documentation (as opposed to command documentation)
|
2009-07-29 18:01:09 -06:00
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-03-12 19:16:21 -07:00
|
|
|
// getPageInfo returns the PageInfo for a package directory abspath. If the
|
2010-03-10 16:22:22 -07:00
|
|
|
// parameter genAST is set, an AST containing only the package exports is
|
|
|
|
// computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
|
2010-08-10 10:52:02 -06:00
|
|
|
// is extracted from the AST. If there is no corresponding package in the
|
|
|
|
// directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub-
|
2011-01-10 16:34:29 -07:00
|
|
|
// directories, PageInfo.Dirs is nil. If a directory read error occurred,
|
2010-08-10 10:52:02 -06:00
|
|
|
// PageInfo.Err is set to the respective error but the error is not logged.
|
2009-07-29 18:01:09 -06:00
|
|
|
//
|
2010-03-29 19:06:53 -06:00
|
|
|
func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo {
|
2009-11-02 23:44:01 -07:00
|
|
|
// filter function to select the desired .go files
|
2011-06-15 15:06:35 -06:00
|
|
|
filter := func(d FileInfo) bool {
|
2010-02-16 12:20:55 -07:00
|
|
|
// If we are looking at cmd documentation, only accept
|
|
|
|
// the special fakePkgFile containing the documentation.
|
2011-06-15 15:06:35 -06:00
|
|
|
return isPkgFile(d) && (h.isPkg || d.Name() == fakePkgFile)
|
2009-12-15 16:33:31 -07:00
|
|
|
}
|
2009-07-29 18:01:09 -06:00
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
// get package ASTs
|
go/ast: use token.Pos instead of token.Position; adjust all dependent code
Specifically:
* lib/godoc:
- provide file set (FSet) argument to formatters where needed
* src/cmd:
- cgo, ebnflint, godoc, gofmt, goinstall: provide file set (fset) where needed
- godoc: remove local binary search with sort.Search (change by rsc),
extract file set for formatters
* src/pkg:
- exp/eval: remove embedded token.Position fields from nodes and replace
with named token.Pos fields; add corresponding Pos() accessor methods
- go/token: added file.Line(), changed signature of File.Position()
* test/fixedbugs/:
- bug206.go: change test to not rely on token.Pos details
* added various extra comments
* Runs all.bash
* gofmt formats all of src, misc w/o changes
* godoc runs
* performance:
- The new version of godoc consumes about the same space after indexing
has completed, but indexing is half the speed. Significant space savings
are expected from smaller ASTs, but since they are thrown away after a
file has been indexed, this is not visible anymore. The slower indexing
time is due to the much more expensive computation of line information.
However, with the new compressed position information, indexing can be
rewritten and simplified. Furthermore, computing the line info can be
done more efficiently.
New godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
44381 godoc 0.0% 0:38.00 4 19 149 145M 184K 148M 176M
2010/12/03 17:58:35 index updated (39.231s, 18505 unique words, 386387 spots)
2010/12/03 17:58:35 bytes=90858456 footprint=199182584
2010/12/03 17:58:36 bytes=47858568 footprint=167295224
Old godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
23167 godoc 0.0% 0:22.02 4 17 132 129M 184K 132M 173M
2010/12/03 14:51:32 index updated (24.892s, 18765 unique words, 393830 spots)
2010/12/03 14:51:32 bytes=66404528 footprint=163907832
2010/12/03 14:51:32 bytes=46282224 footprint=163907832
The different numbers for unique words/spots stem from the fact the the
two workspaces are not exactly identical. The new godoc maintains a large
file set data structure during indexing which (probably) is the reason
for the larger heap (90858456 vs 66404528) before garbage collection.
R=rsc, r
CC=golang-dev
https://golang.org/cl/3050041
2010-12-06 15:23:18 -07:00
|
|
|
fset := token.NewFileSet()
|
2011-06-15 15:06:35 -06:00
|
|
|
pkgs, err := parseDir(fset, abspath, filter)
|
2010-08-10 10:52:02 -06:00
|
|
|
if err != nil && pkgs == nil {
|
|
|
|
// only report directory read errors, ignore parse errors
|
|
|
|
// (may be able to extract partial package information)
|
|
|
|
return PageInfo{Dirname: abspath, Err: err}
|
2010-01-04 18:26:01 -07:00
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
|
2010-03-12 19:16:21 -07:00
|
|
|
// select package
|
|
|
|
var pkg *ast.Package // selected package
|
|
|
|
var plist []string // list of other package (names), if any
|
|
|
|
if len(pkgs) == 1 {
|
|
|
|
// Exactly one package - select it.
|
|
|
|
for _, p := range pkgs {
|
2010-02-16 12:20:55 -07:00
|
|
|
pkg = p
|
|
|
|
}
|
2010-03-12 19:16:21 -07:00
|
|
|
|
|
|
|
} else if len(pkgs) > 1 {
|
|
|
|
// Multiple packages - select the best matching package: The
|
|
|
|
// 1st choice is the package with pkgname, the 2nd choice is
|
|
|
|
// the package with dirname, and the 3rd choice is a package
|
|
|
|
// that is not called "main" if there is exactly one such
|
|
|
|
// package. Otherwise, don't select a package.
|
2011-03-06 15:33:23 -07:00
|
|
|
dirpath, dirname := filepath.Split(abspath)
|
2010-03-12 19:16:21 -07:00
|
|
|
|
|
|
|
// If the dirname is "go" we might be in a sub-directory for
|
|
|
|
// .go files - use the outer directory name instead for better
|
|
|
|
// results.
|
|
|
|
if dirname == "go" {
|
2011-03-06 15:33:23 -07:00
|
|
|
_, dirname = filepath.Split(filepath.Clean(dirpath))
|
2010-03-12 19:16:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
var choice3 *ast.Package
|
|
|
|
loop:
|
|
|
|
for _, p := range pkgs {
|
|
|
|
switch {
|
|
|
|
case p.Name == pkgname:
|
|
|
|
pkg = p
|
|
|
|
break loop // 1st choice; we are done
|
|
|
|
case p.Name == dirname:
|
|
|
|
pkg = p // 2nd choice
|
|
|
|
case p.Name != "main":
|
|
|
|
choice3 = p
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if pkg == nil && len(pkgs) == 2 {
|
|
|
|
pkg = choice3
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the list of other packages
|
|
|
|
// (excluding the selected package, if any).
|
|
|
|
plist = make([]string, len(pkgs))
|
|
|
|
i := 0
|
2010-12-08 22:36:56 -07:00
|
|
|
for name := range pkgs {
|
2010-03-12 19:16:21 -07:00
|
|
|
if pkg == nil || name != pkg.Name {
|
|
|
|
plist[i] = name
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
plist = plist[0:i]
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2009-07-29 18:01:09 -06:00
|
|
|
// compute package documentation
|
2010-03-10 16:22:22 -07:00
|
|
|
var past *ast.File
|
2009-12-15 16:33:31 -07:00
|
|
|
var pdoc *doc.PackageDoc
|
2009-07-29 18:01:09 -06:00
|
|
|
if pkg != nil {
|
2010-03-29 19:06:53 -06:00
|
|
|
if mode&exportsOnly != 0 {
|
|
|
|
ast.PackageExports(pkg)
|
|
|
|
}
|
|
|
|
if mode&genDoc != 0 {
|
2011-03-06 15:33:23 -07:00
|
|
|
pdoc = doc.NewPackageDoc(pkg, path.Clean(relpath)) // no trailing '/' in importpath
|
2010-03-29 19:06:53 -06:00
|
|
|
} else {
|
2010-04-15 10:50:37 -06:00
|
|
|
past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments)
|
2010-03-10 16:22:22 -07:00
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2009-11-02 23:44:01 -07:00
|
|
|
// get directory information
|
2009-12-15 16:33:31 -07:00
|
|
|
var dir *Directory
|
2010-09-16 14:45:40 -06:00
|
|
|
var timestamp int64
|
|
|
|
if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil {
|
2009-11-02 23:44:01 -07:00
|
|
|
// directory tree is present; lookup respective directory
|
|
|
|
// (may still fail if the file system was updated and the
|
2010-02-16 12:20:55 -07:00
|
|
|
// new directory tree has not yet been computed)
|
2010-03-12 19:16:21 -07:00
|
|
|
dir = tree.(*Directory).lookup(abspath)
|
2010-09-16 14:45:40 -06:00
|
|
|
timestamp = ts
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
2010-09-14 12:16:36 -06:00
|
|
|
if dir == nil {
|
|
|
|
// the path may refer to a user-specified file system mapped
|
|
|
|
// via fsMap; lookup that mapping and corresponding RWValue
|
|
|
|
// if any
|
|
|
|
var v *RWValue
|
|
|
|
fsMap.Iterate(func(path string, value *RWValue) bool {
|
|
|
|
if isParentOf(path, abspath) {
|
|
|
|
// mapping found
|
|
|
|
v = value
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
if v != nil {
|
|
|
|
// found a RWValue associated with a user-specified file
|
|
|
|
// system; a non-nil RWValue stores a (possibly out-of-date)
|
|
|
|
// directory tree for that file system
|
2010-09-16 14:45:40 -06:00
|
|
|
if tree, ts := v.get(); tree != nil && tree.(*Directory) != nil {
|
2010-09-14 12:16:36 -06:00
|
|
|
dir = tree.(*Directory).lookup(abspath)
|
2010-09-16 14:45:40 -06:00
|
|
|
timestamp = ts
|
2010-09-14 12:16:36 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
if dir == nil {
|
2010-09-14 19:58:09 -06:00
|
|
|
// no directory tree present (too early after startup or
|
|
|
|
// command-line mode); compute one level for this page
|
|
|
|
// note: cannot use path filter here because in general
|
|
|
|
// it doesn't contain the fsTree path
|
|
|
|
dir = newDirectory(abspath, nil, 1)
|
2010-09-16 14:45:40 -06:00
|
|
|
timestamp = time.Seconds()
|
2009-11-02 23:44:01 -07:00
|
|
|
}
|
2009-11-05 23:25:46 -07:00
|
|
|
|
go/ast: use token.Pos instead of token.Position; adjust all dependent code
Specifically:
* lib/godoc:
- provide file set (FSet) argument to formatters where needed
* src/cmd:
- cgo, ebnflint, godoc, gofmt, goinstall: provide file set (fset) where needed
- godoc: remove local binary search with sort.Search (change by rsc),
extract file set for formatters
* src/pkg:
- exp/eval: remove embedded token.Position fields from nodes and replace
with named token.Pos fields; add corresponding Pos() accessor methods
- go/token: added file.Line(), changed signature of File.Position()
* test/fixedbugs/:
- bug206.go: change test to not rely on token.Pos details
* added various extra comments
* Runs all.bash
* gofmt formats all of src, misc w/o changes
* godoc runs
* performance:
- The new version of godoc consumes about the same space after indexing
has completed, but indexing is half the speed. Significant space savings
are expected from smaller ASTs, but since they are thrown away after a
file has been indexed, this is not visible anymore. The slower indexing
time is due to the much more expensive computation of line information.
However, with the new compressed position information, indexing can be
rewritten and simplified. Furthermore, computing the line info can be
done more efficiently.
New godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
44381 godoc 0.0% 0:38.00 4 19 149 145M 184K 148M 176M
2010/12/03 17:58:35 index updated (39.231s, 18505 unique words, 386387 spots)
2010/12/03 17:58:35 bytes=90858456 footprint=199182584
2010/12/03 17:58:36 bytes=47858568 footprint=167295224
Old godoc, immediately after indexing completed (best of three runs):
PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
23167 godoc 0.0% 0:22.02 4 17 132 129M 184K 132M 173M
2010/12/03 14:51:32 index updated (24.892s, 18765 unique words, 393830 spots)
2010/12/03 14:51:32 bytes=66404528 footprint=163907832
2010/12/03 14:51:32 bytes=46282224 footprint=163907832
The different numbers for unique words/spots stem from the fact the the
two workspaces are not exactly identical. The new godoc maintains a large
file set data structure during indexing which (probably) is the reason
for the larger heap (90858456 vs 66404528) before garbage collection.
R=rsc, r
CC=golang-dev
https://golang.org/cl/3050041
2010-12-06 15:23:18 -07:00
|
|
|
return PageInfo{abspath, plist, fset, past, pdoc, dir.listing(true), timestamp, h.isPkg, nil}
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if redirect(w, r) {
|
2009-11-09 13:07:39 -07:00
|
|
|
return
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
relpath := r.URL.Path[len(h.pattern):]
|
2010-02-19 12:41:48 -07:00
|
|
|
abspath := absolutePath(relpath, h.fsRoot)
|
2011-08-15 20:05:52 -06:00
|
|
|
var mode PageInfoMode
|
|
|
|
if relpath != builtinPkgPath {
|
|
|
|
mode = exportsOnly
|
|
|
|
}
|
2010-03-29 19:06:53 -06:00
|
|
|
if r.FormValue("m") != "src" {
|
|
|
|
mode |= genDoc
|
|
|
|
}
|
|
|
|
info := h.getPageInfo(abspath, relpath, r.FormValue("p"), mode)
|
2010-08-10 10:52:02 -06:00
|
|
|
if info.Err != nil {
|
log: new interface
New logging interface simplifies and generalizes.
1) Loggers now have only one output.
2) log.Stdout, Stderr, Crash and friends are gone.
Logging is now always to standard error by default.
3) log.Panic* replaces log.Crash*.
4) Exiting and panicking are not part of the logger's state; instead
the functions Exit* and Panic* simply call Exit or panic after
printing.
5) There is now one 'standard logger'. Instead of calling Stderr,
use Print etc. There are now triples, by analogy with fmt:
Print, Println, Printf
What was log.Stderr is now best represented by log.Println,
since there are now separate Print and Println functions
(and methods).
6) New functions SetOutput, SetFlags, and SetPrefix allow global
editing of the standard logger's properties. This is new
functionality. For instance, one can call
log.SetFlags(log.Lshortfile|log.Ltime|log.Lmicroseconds)
to get all logging output to show file name, line number, and
time stamp.
In short, for most purposes
log.Stderr -> log.Println or log.Print
log.Stderrf -> log.Printf
log.Crash -> log.Panicln or log.Panic
log.Crashf -> log.Panicf
log.Exit -> log.Exitln or log.Exit
log.Exitf -> log.Exitf (no change)
This has a slight breakage: since loggers now write only to one
output, existing calls to log.New() need to delete the second argument.
Also, custom loggers with exit or panic properties will need to be
reworked.
All package code updated to new interface.
The test has been reworked somewhat.
The old interface will be removed after the new release.
For now, its elements are marked 'deprecated' in their comments.
Fixes #1184.
R=rsc
CC=golang-dev
https://golang.org/cl/2419042
2010-10-12 13:59:18 -06:00
|
|
|
log.Print(info.Err)
|
2010-09-28 22:30:12 -06:00
|
|
|
serveError(w, r, relpath, info.Err)
|
2010-08-10 10:52:02 -06:00
|
|
|
return
|
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2009-10-28 17:19:09 -06:00
|
|
|
if r.FormValue("f") == "text" {
|
2010-02-16 12:20:55 -07:00
|
|
|
contents := applyTemplate(packageText, "packageText", info)
|
2010-09-28 22:30:12 -06:00
|
|
|
serveText(w, contents)
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2010-09-16 14:45:40 -06:00
|
|
|
var title, subtitle string
|
2010-03-10 16:22:22 -07:00
|
|
|
switch {
|
|
|
|
case info.PAst != nil:
|
2010-08-13 11:42:18 -06:00
|
|
|
title = "Package " + info.PAst.Name.Name
|
2010-03-10 16:22:22 -07:00
|
|
|
case info.PDoc != nil:
|
2009-11-03 20:40:26 -07:00
|
|
|
switch {
|
2011-08-15 20:05:52 -06:00
|
|
|
case info.IsPkg:
|
2009-11-09 13:07:39 -07:00
|
|
|
title = "Package " + info.PDoc.PackageName
|
2009-11-03 20:40:26 -07:00
|
|
|
case info.PDoc.PackageName == fakePkgName:
|
|
|
|
// assume that the directory name is the command name
|
2011-03-06 15:33:23 -07:00
|
|
|
_, pkgname := path.Split(path.Clean(relpath))
|
2009-12-15 16:33:31 -07:00
|
|
|
title = "Command " + pkgname
|
2009-11-03 20:40:26 -07:00
|
|
|
default:
|
2009-11-09 13:07:39 -07:00
|
|
|
title = "Command " + info.PDoc.PackageName
|
2009-11-03 20:40:26 -07:00
|
|
|
}
|
2010-03-10 16:22:22 -07:00
|
|
|
default:
|
2011-03-06 15:33:23 -07:00
|
|
|
title = "Directory " + relativeURL(info.Dirname)
|
2010-09-16 14:45:40 -06:00
|
|
|
if *showTimestamps {
|
|
|
|
subtitle = "Last update: " + time.SecondsToLocalTime(info.DirTime).String()
|
|
|
|
}
|
2009-10-01 15:08:00 -06:00
|
|
|
}
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
contents := applyTemplate(packageHTML, "packageHTML", info)
|
2010-09-28 22:30:12 -06:00
|
|
|
servePage(w, title, subtitle, "", contents)
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Search
|
|
|
|
|
|
|
|
var searchIndex RWValue
|
|
|
|
|
|
|
|
type SearchResult struct {
|
2011-01-10 16:34:29 -07:00
|
|
|
Query string
|
|
|
|
Alert string // error or warning message
|
|
|
|
|
|
|
|
// identifier matches
|
|
|
|
Hit *LookupResult // identifier matches of Query
|
|
|
|
Alt *AltWords // alternative identifiers to look for
|
|
|
|
|
|
|
|
// textual matches
|
|
|
|
Found int // number of textual occurrences found
|
|
|
|
Textual []FileLines // textual matches of Query
|
|
|
|
Complete bool // true if all textual occurrences of Query are reported
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
2010-03-19 13:46:43 -06:00
|
|
|
func lookup(query string) (result SearchResult) {
|
|
|
|
result.Query = query
|
2011-01-10 16:34:29 -07:00
|
|
|
|
2011-01-18 11:59:54 -07:00
|
|
|
index, timestamp := searchIndex.get()
|
|
|
|
if index != nil {
|
2010-12-10 15:40:22 -07:00
|
|
|
index := index.(*Index)
|
2011-01-19 13:48:10 -07:00
|
|
|
|
|
|
|
// identifier search
|
|
|
|
var err os.Error
|
|
|
|
result.Hit, result.Alt, err = index.Lookup(query)
|
2011-01-19 15:33:05 -07:00
|
|
|
if err != nil && *maxResults <= 0 {
|
2011-01-19 13:48:10 -07:00
|
|
|
// ignore the error if full text search is enabled
|
|
|
|
// since the query may be a valid regular expression
|
2011-01-10 16:34:29 -07:00
|
|
|
result.Alert = "Error in query string: " + err.String()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2011-01-19 13:48:10 -07:00
|
|
|
// full text search
|
2011-01-19 15:33:05 -07:00
|
|
|
if *maxResults > 0 && query != "" {
|
2011-01-19 13:48:10 -07:00
|
|
|
rx, err := regexp.Compile(query)
|
|
|
|
if err != nil {
|
|
|
|
result.Alert = "Error in query regular expression: " + err.String()
|
|
|
|
return
|
|
|
|
}
|
2011-01-19 15:33:05 -07:00
|
|
|
// If we get maxResults+1 results we know that there are more than
|
|
|
|
// maxResults results and thus the result may be incomplete (to be
|
|
|
|
// precise, we should remove one result from the result set, but
|
|
|
|
// nobody is going to count the results on the result page).
|
|
|
|
result.Found, result.Textual = index.LookupRegexp(rx, *maxResults+1)
|
|
|
|
result.Complete = result.Found <= *maxResults
|
|
|
|
if !result.Complete {
|
|
|
|
result.Found-- // since we looked for maxResults+1
|
|
|
|
}
|
2011-01-19 13:48:10 -07:00
|
|
|
}
|
2011-01-18 11:59:54 -07:00
|
|
|
}
|
2011-01-10 16:34:29 -07:00
|
|
|
|
2011-01-18 11:59:54 -07:00
|
|
|
// is the result accurate?
|
2011-05-02 14:28:02 -06:00
|
|
|
if *indexEnabled {
|
|
|
|
if _, ts := fsModified.get(); timestamp < ts {
|
|
|
|
// The index is older than the latest file system change
|
|
|
|
// under godoc's observation. Indexing may be in progress
|
|
|
|
// or start shortly (see indexer()).
|
|
|
|
result.Alert = "Indexing in progress: result may be inaccurate"
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result.Alert = "Search index disabled: no results available"
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
2011-01-18 11:59:54 -07:00
|
|
|
|
2010-03-19 13:46:43 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func search(w http.ResponseWriter, r *http.Request) {
|
2010-03-19 13:46:43 -06:00
|
|
|
query := strings.TrimSpace(r.FormValue("q"))
|
|
|
|
result := lookup(query)
|
2009-10-27 11:34:31 -06:00
|
|
|
|
2010-03-24 15:51:55 -06:00
|
|
|
if r.FormValue("f") == "text" {
|
|
|
|
contents := applyTemplate(searchText, "searchText", result)
|
2010-09-28 22:30:12 -06:00
|
|
|
serveText(w, contents)
|
2010-03-24 15:51:55 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2009-12-15 16:33:31 -07:00
|
|
|
var title string
|
2010-12-10 15:40:22 -07:00
|
|
|
if result.Hit != nil || len(result.Textual) > 0 {
|
2009-11-09 13:07:39 -07:00
|
|
|
title = fmt.Sprintf(`Results for query %q`, query)
|
2009-10-27 11:34:31 -06:00
|
|
|
} else {
|
2009-11-09 13:07:39 -07:00
|
|
|
title = fmt.Sprintf(`No results found for query %q`, query)
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
contents := applyTemplate(searchHTML, "searchHTML", result)
|
2010-09-28 22:30:12 -06:00
|
|
|
servePage(w, title, "", query, contents)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2010-02-16 12:20:55 -07:00
|
|
|
// Indexer
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-11-18 20:55:38 -07:00
|
|
|
// invalidateIndex should be called whenever any of the file systems
|
|
|
|
// under godoc's observation change so that the indexer is kicked on.
|
|
|
|
//
|
|
|
|
func invalidateIndex() {
|
|
|
|
fsModified.set(nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// indexUpToDate() returns true if the search index is not older
|
|
|
|
// than any of the file systems under godoc's observation.
|
|
|
|
//
|
|
|
|
func indexUpToDate() bool {
|
|
|
|
_, fsTime := fsModified.get()
|
|
|
|
_, siTime := searchIndex.get()
|
|
|
|
return fsTime <= 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 {
|
|
|
|
for d := range dir.(*Directory).iter(false) {
|
|
|
|
c <- d.Path
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// fsDirnames() returns a channel sending all directory names
|
|
|
|
// of all the file systems under godoc's observation.
|
|
|
|
//
|
|
|
|
func fsDirnames() <-chan string {
|
|
|
|
c := make(chan string, 256) // asynchronous for fewer context switches
|
|
|
|
go func() {
|
|
|
|
feedDirnames(&fsTree, c)
|
|
|
|
fsMap.Iterate(func(_ string, root *RWValue) bool {
|
|
|
|
feedDirnames(root, c)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
close(c)
|
|
|
|
}()
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2009-10-30 11:58:53 -06:00
|
|
|
func indexer() {
|
|
|
|
for {
|
2010-11-18 20:55:38 -07:00
|
|
|
if !indexUpToDate() {
|
2009-10-30 11:58:53 -06:00
|
|
|
// index possibly out of date - make a new one
|
2010-11-18 20:55:38 -07:00
|
|
|
if *verbose {
|
|
|
|
log.Printf("updating index...")
|
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
start := time.Nanoseconds()
|
2011-01-19 15:33:05 -07:00
|
|
|
index := NewIndex(fsDirnames(), *maxResults > 0)
|
2009-12-15 16:33:31 -07:00
|
|
|
stop := time.Nanoseconds()
|
|
|
|
searchIndex.set(index)
|
2009-10-30 11:58:53 -06:00
|
|
|
if *verbose {
|
2009-12-15 16:33:31 -07:00
|
|
|
secs := float64((stop-start)/1e6) / 1e3
|
2010-12-10 15:40:22 -07:00
|
|
|
stats := index.Stats()
|
2010-12-12 14:13:07 -07:00
|
|
|
log.Printf("index updated (%gs, %d bytes of source, %d files, %d lines, %d unique words, %d spots)",
|
|
|
|
secs, stats.Bytes, stats.Files, stats.Lines, stats.Words, stats.Spots)
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
2011-01-10 16:34:29 -07:00
|
|
|
log.Printf("before GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
|
2010-03-19 18:46:18 -06:00
|
|
|
runtime.GC()
|
2011-01-10 16:34:29 -07:00
|
|
|
log.Printf("after GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
|
|
|
|
}
|
|
|
|
var delay int64 = 60 * 1e9 // by default, try every 60s
|
|
|
|
if *testDir != "" {
|
|
|
|
// in test mode, try once a second for fast startup
|
|
|
|
delay = 1 * 1e9
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
2011-01-10 16:34:29 -07:00
|
|
|
time.Sleep(delay)
|
2009-08-03 10:53:00 -06:00
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|