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"
|
|
|
|
"go/doc"
|
|
|
|
"go/parser"
|
|
|
|
"go/printer"
|
|
|
|
"go/token"
|
|
|
|
"http"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"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"
|
|
|
|
"template"
|
|
|
|
"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-01-19 15:33:05 -07:00
|
|
|
maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
// file system mapping
|
2010-09-14 12:16:36 -06:00
|
|
|
fsMap Mapping // user-defined mapping
|
|
|
|
fsTree RWValue // *Directory tree of packages, updated with each sync
|
|
|
|
pathFilter RWValue // filter used when building fsMap directory trees
|
2010-11-18 20:55:38 -07:00
|
|
|
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-01-26 22:49:30 -07:00
|
|
|
fsMap.Init(*pkgPath)
|
2010-03-19 16:20:20 -06:00
|
|
|
fileServer = http.FileServer(*goroot, "")
|
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) {
|
|
|
|
contents, err := ioutil.ReadFile(filename)
|
|
|
|
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-02-24 11:22:32 -07:00
|
|
|
d, e := os.Lstat(path)
|
|
|
|
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
|
|
|
}
|
2010-09-16 11:40:07 -06:00
|
|
|
list := canonicalizePaths(strings.Split(string(contents), "\n", -1), filter)
|
|
|
|
// for each parent path, remove all it's children q
|
|
|
|
// (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-21 18:15:59 -06:00
|
|
|
(&printer.Config{Mode: mode, Tabwidth: *tabwidth}).Fprint(&tconv{output: w}, fset, x)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-09 10:52:32 -07:00
|
|
|
// Write anything to w.
|
|
|
|
func writeAny(w io.Writer, fset *token.FileSet, x interface{}) {
|
|
|
|
switch v := x.(type) {
|
|
|
|
case []byte:
|
|
|
|
w.Write(v)
|
|
|
|
case string:
|
|
|
|
w.Write([]byte(v))
|
|
|
|
case ast.Decl, ast.Expr, ast.Stmt, *ast.File:
|
|
|
|
writeNode(w, fset, x)
|
|
|
|
default:
|
|
|
|
fmt.Fprint(w, x)
|
2009-07-31 19:04:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2011-02-09 10:52:32 -07:00
|
|
|
// Write anything html-escaped to w.
|
|
|
|
func writeAnyHTML(w io.Writer, fset *token.FileSet, x interface{}) {
|
2009-06-16 10:14:06 -06:00
|
|
|
switch v := x.(type) {
|
|
|
|
case []byte:
|
2011-02-09 10:52:32 -07:00
|
|
|
template.HTMLEscape(w, v)
|
2009-06-16 10:14:06 -06:00
|
|
|
case string:
|
2011-02-09 10:52:32 -07:00
|
|
|
template.HTMLEscape(w, []byte(v))
|
2009-12-01 10:15:05 -07:00
|
|
|
case ast.Decl, ast.Expr, ast.Stmt, *ast.File:
|
2011-02-09 10:52:32 -07:00
|
|
|
var buf bytes.Buffer
|
|
|
|
writeNode(&buf, fset, x)
|
|
|
|
FormatText(w, buf.Bytes(), -1, true, "", nil)
|
2009-07-31 19:04:53 -06:00
|
|
|
default:
|
2011-02-09 10:52:32 -07:00
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprint(&buf, x)
|
|
|
|
template.HTMLEscape(w, buf.Bytes())
|
2009-06-16 10:14:06 -06: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
|
|
|
func fileset(x []interface{}) *token.FileSet {
|
|
|
|
if len(x) > 1 {
|
|
|
|
if fset, ok := x[1].(*token.FileSet); ok {
|
|
|
|
return fset
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-13 16:18:56 -07:00
|
|
|
// Template formatter for "html-esc" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func htmlEscFmt(w io.Writer, format string, x ...interface{}) {
|
2011-02-09 10:52:32 -07:00
|
|
|
writeAnyHTML(w, fileset(x), x[0])
|
2010-01-13 16:18:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
// Template formatter for "html-comment" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func htmlCommentFmt(w io.Writer, format string, x ...interface{}) {
|
2009-12-15 16:33:31 -07:00
|
|
|
var buf bytes.Buffer
|
2011-02-09 10:52:32 -07:00
|
|
|
writeAny(&buf, fileset(x), x[0])
|
2010-03-19 14:01:45 -06:00
|
|
|
// TODO(gri) Provide list of words (e.g. function parameters)
|
|
|
|
// to be emphasized by ToHTML.
|
|
|
|
doc.ToHTML(w, buf.Bytes(), nil) // does html-escaping
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Template formatter for "" (default) format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func textFmt(w io.Writer, format string, x ...interface{}) {
|
2011-02-09 10:52:32 -07:00
|
|
|
writeAny(w, fileset(x), x[0])
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-12 14:13:07 -07:00
|
|
|
// Template formatter for "urlquery-esc" format.
|
|
|
|
func urlQueryEscFmt(w io.Writer, format string, x ...interface{}) {
|
|
|
|
var buf bytes.Buffer
|
2011-02-09 10:52:32 -07:00
|
|
|
writeAny(&buf, fileset(x), x[0])
|
2010-12-12 14:13:07 -07:00
|
|
|
template.HTMLEscape(w, []byte(http.URLEscape(string(buf.Bytes()))))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Template formatter for the various "url-xxx" formats excluding url-esc.
|
2010-12-01 14:33:49 -07:00
|
|
|
func urlFmt(w io.Writer, format string, x ...interface{}) {
|
2010-02-16 12:20:55 -07:00
|
|
|
var path string
|
|
|
|
var line int
|
2011-01-10 16:34:29 -07:00
|
|
|
var low, high int // selection
|
2009-11-03 20:40:26 -07:00
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
// determine path and position info, if any
|
|
|
|
type positioner interface {
|
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
|
|
|
Pos() token.Pos
|
2011-01-10 16:34:29 -07:00
|
|
|
End() token.Pos
|
2009-10-08 16:14:54 -06:00
|
|
|
}
|
2010-12-01 14:33:49 -07:00
|
|
|
switch t := x[0].(type) {
|
2010-02-16 12:20:55 -07:00
|
|
|
case string:
|
|
|
|
path = t
|
|
|
|
case positioner:
|
2011-01-10 16:34:29 -07:00
|
|
|
fset := fileset(x)
|
|
|
|
if p := t.Pos(); p.IsValid() {
|
|
|
|
pos := fset.Position(p)
|
2010-02-16 12:20:55 -07:00
|
|
|
path = pos.Filename
|
|
|
|
line = pos.Line
|
2011-01-10 16:34:29 -07:00
|
|
|
low = pos.Offset
|
|
|
|
}
|
|
|
|
if p := t.End(); p.IsValid() {
|
|
|
|
high = fset.Position(p).Offset
|
2009-08-03 10:53:00 -06: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
|
|
|
default:
|
|
|
|
// we should never reach here, but be resilient
|
|
|
|
// and assume the position is invalid (empty path,
|
|
|
|
// and line 0)
|
|
|
|
log.Printf("INTERNAL ERROR: urlFmt(%s) without a string or positioner", format)
|
2009-08-03 10:53:00 -06:00
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
|
|
|
|
// map path
|
2011-03-06 15:33:23 -07:00
|
|
|
relpath := relativeURL(path)
|
2010-02-16 12:20:55 -07:00
|
|
|
|
2010-03-24 17:28:59 -06:00
|
|
|
// convert to relative URLs so that they can also
|
|
|
|
// be used as relative file names in .txt templates
|
2010-02-16 12:20:55 -07:00
|
|
|
switch format {
|
|
|
|
default:
|
|
|
|
// we should never reach here, but be resilient
|
|
|
|
// and assume the url-pkg format instead
|
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("INTERNAL ERROR: urlFmt(%s)", format)
|
2010-02-16 12:20:55 -07:00
|
|
|
fallthrough
|
|
|
|
case "url-pkg":
|
|
|
|
// 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/"):]
|
|
|
|
}
|
2010-03-24 17:28:59 -06:00
|
|
|
template.HTMLEscape(w, []byte(pkgHandler.pattern[1:]+relpath)) // remove trailing '/' for relative URL
|
2010-02-16 12:20:55 -07:00
|
|
|
case "url-src":
|
2010-03-24 17:28:59 -06:00
|
|
|
template.HTMLEscape(w, []byte(relpath))
|
2010-02-16 12:20:55 -07:00
|
|
|
case "url-pos":
|
2011-01-10 16:34:29 -07:00
|
|
|
template.HTMLEscape(w, []byte(relpath))
|
|
|
|
// selection ranges are of form "s=low:high"
|
|
|
|
if low < high {
|
|
|
|
fmt.Fprintf(w, "?s=%d:%d", low, high)
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
// line id's in html-printed source are of the
|
|
|
|
// form "L%d" where %d stands for the line number
|
2011-01-10 16:34:29 -07:00
|
|
|
if line > 0 {
|
|
|
|
fmt.Fprintf(w, "#L%d", line)
|
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-08 21:17:04 -07:00
|
|
|
// Template formatter for "infoKind" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func infoKindFmt(w io.Writer, format string, x ...interface{}) {
|
|
|
|
fmt.Fprintf(w, infoKinds[x[0].(SpotKind)]) // infoKind entries are html-escaped
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Template formatter for "infoLine" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func infoLineFmt(w io.Writer, format string, x ...interface{}) {
|
|
|
|
info := x[0].(SpotInfo)
|
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
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
fmt.Fprintf(w, "%d", line)
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Template formatter for "infoSnippet" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func infoSnippetFmt(w io.Writer, format string, x ...interface{}) {
|
|
|
|
info := x[0].(SpotInfo)
|
2011-01-10 16:34:29 -07:00
|
|
|
text := []byte(`<span class="alert">no snippet text available</span>`)
|
2009-10-27 11:34:31 -06:00
|
|
|
if info.IsIndex() {
|
2009-12-15 16:33:31 -07:00
|
|
|
index, _ := searchIndex.get()
|
2009-10-28 17:19:09 -06:00
|
|
|
// no escaping of snippet text needed;
|
|
|
|
// snippet text is escaped when generated
|
2009-12-15 16:33:31 -07:00
|
|
|
text = index.(*Index).Snippet(info.Lori()).Text
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
2011-01-10 16:34:29 -07:00
|
|
|
w.Write(text)
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-08 17:47:32 -07:00
|
|
|
// Template formatter for "padding" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func paddingFmt(w io.Writer, format string, x ...interface{}) {
|
|
|
|
for i := x[0].(int); i > 0; i-- {
|
2009-11-09 13:07:39 -07:00
|
|
|
fmt.Fprint(w, `<td width="25"></td>`)
|
2009-11-08 17:47:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Template formatter for "time" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func timeFmt(w io.Writer, format string, x ...interface{}) {
|
|
|
|
template.HTMLEscape(w, []byte(time.SecondsToLocalTime(x[0].(int64)/1e9).String()))
|
2009-11-08 17:47:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-13 14:09:33 -07:00
|
|
|
// Template formatter for "dir/" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func dirslashFmt(w io.Writer, format string, x ...interface{}) {
|
|
|
|
if x[0].(*os.FileInfo).IsDirectory() {
|
2010-01-13 14:09:33 -07:00
|
|
|
w.Write([]byte{'/'})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
// Template formatter for "localname" format.
|
2010-12-01 14:33:49 -07:00
|
|
|
func localnameFmt(w io.Writer, format string, x ...interface{}) {
|
2011-03-06 15:33:23 -07:00
|
|
|
_, localname := filepath.Split(x[0].(string))
|
2010-02-25 17:01:29 -07:00
|
|
|
template.HTMLEscape(w, []byte(localname))
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-12 14:13:07 -07:00
|
|
|
// Template formatter for "numlines" format.
|
|
|
|
func numlinesFmt(w io.Writer, format string, x ...interface{}) {
|
|
|
|
list := x[0].([]int)
|
|
|
|
fmt.Fprintf(w, "%d", len(list))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
var fmap = template.FormatterMap{
|
2010-03-02 14:46:51 -07:00
|
|
|
"": textFmt,
|
|
|
|
"html-esc": htmlEscFmt,
|
2009-06-16 10:14:06 -06:00
|
|
|
"html-comment": htmlCommentFmt,
|
2010-12-12 14:13:07 -07:00
|
|
|
"urlquery-esc": urlQueryEscFmt,
|
2010-03-02 14:46:51 -07:00
|
|
|
"url-pkg": urlFmt,
|
|
|
|
"url-src": urlFmt,
|
|
|
|
"url-pos": urlFmt,
|
|
|
|
"infoKind": infoKindFmt,
|
|
|
|
"infoLine": infoLineFmt,
|
|
|
|
"infoSnippet": infoSnippetFmt,
|
|
|
|
"padding": paddingFmt,
|
|
|
|
"time": timeFmt,
|
|
|
|
"dir/": dirslashFmt,
|
|
|
|
"localname": localnameFmt,
|
2010-12-12 14:13:07 -07:00
|
|
|
"numlines": numlinesFmt,
|
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)
|
2009-12-15 16:33:31 -07:00
|
|
|
data, err := ioutil.ReadFile(path)
|
2009-06-16 10:14:06 -06:00
|
|
|
if err != nil {
|
2011-02-01 13:47:35 -07:00
|
|
|
log.Fatalf("ReadFile %s: %v", path, err)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
t, err := template.Parse(string(data), fmap)
|
2009-11-02 21:35:52 -07:00
|
|
|
if err != nil {
|
2011-02-01 13:47:35 -07:00
|
|
|
log.Fatalf("%s: %v", name, err)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
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 {
|
2010-09-16 14:45:40 -06:00
|
|
|
Title string
|
|
|
|
Subtitle string
|
|
|
|
PkgRoots []string
|
|
|
|
Query string
|
|
|
|
Version string
|
|
|
|
Menu []byte
|
|
|
|
Content []byte
|
2010-11-17 12:03:33 -07:00
|
|
|
}{
|
|
|
|
title,
|
|
|
|
subtitle,
|
|
|
|
fsMap.PrefixList(),
|
|
|
|
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
|
2010-02-16 12:20:55 -07:00
|
|
|
src, err := ioutil.ReadFile(abspath)
|
2009-10-01 15:08:00 -06: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.Printf("ioutil.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-02-16 12:20:55 -07: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) {
|
2010-02-16 12:20:55 -07:00
|
|
|
src, err := ioutil.ReadFile(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.Printf("ioutil.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
|
|
|
}
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
list, err := ioutil.ReadDir(abspath)
|
2009-11-07 22:12:46 -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.Printf("ioutil.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-01-13 14:09:33 -07:00
|
|
|
for _, d := range list {
|
|
|
|
if d.IsDirectory() {
|
|
|
|
d.Size = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
dir, err := os.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
|
|
|
|
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-06-16 10:14:06 -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
|
2010-04-09 12:36:40 -06:00
|
|
|
filter := func(d *os.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.
|
|
|
|
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()
|
|
|
|
pkgs, err := parser.ParseDir(fset, abspath, filter, parser.ParseComments)
|
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)
|
2010-03-29 19:06:53 -06:00
|
|
|
mode := exportsOnly
|
|
|
|
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 {
|
|
|
|
case h.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?
|
|
|
|
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"
|
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
|
|
|
}
|