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"
|
|
|
|
pathutil "path"
|
2010-03-30 18:37:42 -06:00
|
|
|
"regexp"
|
2010-03-19 16:20:20 -06:00
|
|
|
"runtime"
|
2009-12-15 16:33:31 -07:00
|
|
|
"strings"
|
|
|
|
"template"
|
|
|
|
"time"
|
|
|
|
"utf8"
|
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
|
|
|
|
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")
|
|
|
|
path = 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")
|
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
|
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() {
|
2010-02-16 12:20:55 -07:00
|
|
|
fsMap.Init(*path)
|
2010-03-19 16:20:20 -06:00
|
|
|
fileServer = http.FileServer(*goroot, "")
|
|
|
|
cmdHandler = httpHandler{"/cmd/", pathutil.Join(*goroot, "src/cmd"), false}
|
|
|
|
pkgHandler = httpHandler{"/pkg/", pathutil.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)
|
|
|
|
mux.HandleFunc("/", serveFile)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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] == '/')
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-16 11:40:07 -06:00
|
|
|
// binarySearch returns an index i such that (a[i] <= s < a[i+1]) || (s is not in a).
|
|
|
|
// The slice a must not be empty and sorted in increasing order.
|
|
|
|
// (See "A Method of Programming", E.W. Dijkstra).
|
|
|
|
//
|
|
|
|
func binarySearch(a []string, s string) int {
|
|
|
|
i, j := 0, len(a)
|
|
|
|
// i < j for non-empty a
|
|
|
|
for i+1 < j {
|
|
|
|
// 0 <= i < j <= len(a) && (a[i] <= s < a[j] || (s is not in a))
|
|
|
|
h := i + (j-i)/2 // i < h < j
|
|
|
|
if a[h] <= s {
|
|
|
|
i = h
|
|
|
|
} else { // s < a[h]
|
|
|
|
j = h
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// i+1 == j for non-empty a
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-14 12:16:36 -06:00
|
|
|
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
|
|
|
|
i := binarySearch(list, path)
|
|
|
|
// At this point we have (list[i] <= path < list[i+1]) || (path is not in list),
|
|
|
|
// thus path must be either longer (a child) of list[i], or shorter (a parent)
|
|
|
|
// of list[i+1] - assuming an "infinitely extended" list. However, binarySearch
|
|
|
|
// will return a 0 if path < list[0], so we must be careful in that case.
|
|
|
|
return i == 0 && isParentOf(path, list[0]) ||
|
|
|
|
isParentOf(list[i], path) ||
|
|
|
|
i+1 < len(list) && isParentOf(path, list[i+1])
|
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 {
|
|
|
|
d, err := os.Lstat(path)
|
|
|
|
return err == nil && isPkgDir(d)
|
|
|
|
}
|
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++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list[0:i], nil
|
2010-09-14 12:16:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func updateFilterFile() {
|
|
|
|
// for each user-defined file system mapping, compute
|
|
|
|
// respective directory tree w/o filter for accuracy
|
|
|
|
fsMap.Iterate(func(path string, value *RWValue) bool {
|
2010-09-14 19:58:09 -06:00
|
|
|
value.set(newDirectory(path, nil, -1))
|
2010-09-14 12:16:36 -06:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
// 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 {
|
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", err)
|
2010-09-14 12:16:36 -06:00
|
|
|
} else if len(list) == 0 {
|
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("no directory paths in file %s", *filter)
|
2010-09-14 12:16:36 -06:00
|
|
|
}
|
|
|
|
setPathFilter(list)
|
|
|
|
}
|
|
|
|
|
|
|
|
// for each user-defined file system mapping, compute
|
|
|
|
// respective directory tree quickly using pathFilter
|
|
|
|
go fsMap.Iterate(func(path string, value *RWValue) bool {
|
2010-09-14 19:58:09 -06:00
|
|
|
value.set(newDirectory(path, getPathFilter(), -1))
|
2010-09-14 12:16:36 -06:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
// 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
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
func absolutePath(path, defaultRoot string) string {
|
|
|
|
abspath := fsMap.ToAbsolute(path)
|
|
|
|
if abspath == "" {
|
|
|
|
// no user-defined mapping found; use default mapping
|
|
|
|
abspath = pathutil.Join(defaultRoot, path)
|
|
|
|
}
|
|
|
|
return abspath
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func relativePath(path string) string {
|
|
|
|
relpath := fsMap.ToRelative(path)
|
2010-11-17 12:03:33 -07:00
|
|
|
if relpath == "" {
|
|
|
|
// prefix must end in '/'
|
|
|
|
prefix := *goroot
|
|
|
|
if len(prefix) > 0 && prefix[len(prefix)-1] != '/' {
|
|
|
|
prefix += "/"
|
|
|
|
}
|
|
|
|
if strings.HasPrefix(path, prefix) {
|
|
|
|
// no user-defined mapping found; use default mapping
|
|
|
|
relpath = path[len(prefix):]
|
|
|
|
}
|
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-10-22 10:41:38 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// HTML formatting support
|
|
|
|
|
2010-08-26 15:36:13 -06:00
|
|
|
// aposescaper implements an io.Writer that escapes single quotes:
|
|
|
|
// ' is written as \' . It is used to escape text such that it can
|
|
|
|
// be used as the content of single-quoted string literals.
|
|
|
|
type aposescaper struct {
|
|
|
|
w io.Writer
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (e *aposescaper) Write(p []byte) (n int, err os.Error) {
|
|
|
|
backslash := []byte{'\\'}
|
|
|
|
var i, m int
|
|
|
|
for j, b := range p {
|
|
|
|
if b == '\'' {
|
|
|
|
m, err = e.w.Write(p[i:j])
|
|
|
|
n += m
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, err = e.w.Write(backslash)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
i = j
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m, err = e.w.Write(p[i:])
|
|
|
|
n += m
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-22 10:41:38 -06:00
|
|
|
// Styler implements a printer.Styler.
|
|
|
|
type Styler struct {
|
2009-12-15 16:33:31 -07:00
|
|
|
linetags bool
|
|
|
|
highlight string
|
2010-03-16 15:17:42 -06:00
|
|
|
objmap map[*ast.Object]int
|
2010-08-13 11:42:18 -06:00
|
|
|
idcount int
|
2010-03-16 15:17:42 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newStyler(highlight string) *Styler {
|
|
|
|
return &Styler{true, highlight, make(map[*ast.Object]int), 0}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-13 11:42:18 -06:00
|
|
|
// identId returns a number >= 0 identifying the *ast.Object
|
|
|
|
// denoted by name. If no object is denoted, the result is < 0.
|
|
|
|
//
|
|
|
|
// TODO(gri): Consider making this a mapping from popup info
|
|
|
|
// (for that name) to id, instead of *ast.Object
|
|
|
|
// to id. If a lot of the popup info is the same
|
|
|
|
// (e.g. type information), this will reduce the
|
|
|
|
// size of the html generated.
|
|
|
|
func (s *Styler) identId(name *ast.Ident) int {
|
|
|
|
obj := name.Obj
|
|
|
|
if obj == nil || s.objmap == nil {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
id, found := s.objmap[obj]
|
2010-03-16 15:17:42 -06:00
|
|
|
if !found {
|
2010-08-13 11:42:18 -06:00
|
|
|
// first occurence
|
|
|
|
id = s.idcount
|
|
|
|
s.objmap[obj] = id
|
|
|
|
s.idcount++
|
2010-03-16 15:17:42 -06:00
|
|
|
}
|
2010-08-13 11:42:18 -06:00
|
|
|
return id
|
2010-03-16 15:17:42 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-13 11:42:18 -06:00
|
|
|
// writeObjInfo writes the popup info corresponding to obj to w.
|
|
|
|
// The text is HTML-escaped and does not contain single quotes.
|
|
|
|
func writeObjInfo(w io.Writer, obj *ast.Object) {
|
|
|
|
// for now, show object kind and name; eventually
|
|
|
|
// do something more interesting (show declaration,
|
|
|
|
// for instance)
|
|
|
|
if obj.Kind != ast.Bad {
|
|
|
|
fmt.Fprintf(w, "%s ", obj.Kind)
|
2010-03-16 15:17:42 -06:00
|
|
|
}
|
2010-08-13 11:42:18 -06:00
|
|
|
template.HTMLEscape(w, []byte(obj.Name))
|
2010-08-26 15:36:13 -06:00
|
|
|
// show type if we know it
|
|
|
|
if obj.Type != nil && obj.Type.Expr != nil {
|
|
|
|
fmt.Fprint(w, " ")
|
|
|
|
writeNode(&aposescaper{w}, obj.Type.Expr, true, &defaultStyler)
|
|
|
|
}
|
2010-08-13 11:42:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// idList returns a Javascript array (source) with identifier popup
|
|
|
|
// information: The i'th array entry is a single-quoted string with
|
|
|
|
// the popup information for an identifier x with s.identId(x) == i,
|
|
|
|
// for 0 <= i < s.idcount.
|
|
|
|
func (s *Styler) idList() []byte {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprintln(&buf, "[")
|
|
|
|
|
|
|
|
if s.idcount > 0 {
|
|
|
|
// invert objmap: create an array [id]obj from map[obj]id
|
|
|
|
a := make([]*ast.Object, s.idcount)
|
|
|
|
for obj, id := range s.objmap {
|
|
|
|
a[id] = obj
|
|
|
|
}
|
|
|
|
|
|
|
|
// for each id, print object info as single-quoted Javascript string
|
|
|
|
for id, obj := range a {
|
|
|
|
printIndex := false // enable for debugging (but longer html)
|
|
|
|
if printIndex {
|
|
|
|
fmt.Fprintf(&buf, "/* %4d */ ", id)
|
|
|
|
}
|
|
|
|
fmt.Fprint(&buf, "'")
|
|
|
|
writeObjInfo(&buf, obj)
|
|
|
|
fmt.Fprint(&buf, "',\n")
|
|
|
|
}
|
2010-03-16 15:17:42 -06:00
|
|
|
}
|
2010-08-13 11:42:18 -06:00
|
|
|
|
|
|
|
fmt.Fprintln(&buf, "]")
|
|
|
|
return buf.Bytes()
|
2009-10-22 10:41:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 11:00:57 -07:00
|
|
|
// Use the defaultStyler when there is no specific styler.
|
2009-11-16 15:26:29 -07:00
|
|
|
// The defaultStyler does not emit line tags since they may
|
|
|
|
// interfere with tags emitted by templates.
|
|
|
|
// TODO(gri): Should emit line tags at the beginning of a line;
|
|
|
|
// never in the middle of code.
|
2009-11-03 11:00:57 -07:00
|
|
|
var defaultStyler Styler
|
|
|
|
|
|
|
|
|
2009-11-08 22:46:20 -07:00
|
|
|
func (s *Styler) LineTag(line int) (text []byte, tag printer.HTMLTag) {
|
2009-11-16 15:26:29 -07:00
|
|
|
if s.linetags {
|
|
|
|
tag = printer.HTMLTag{fmt.Sprintf(`<a id="L%d">`, line), "</a>"}
|
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-10-22 10:41:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-08 22:46:20 -07:00
|
|
|
func (s *Styler) Comment(c *ast.Comment, line []byte) (text []byte, tag printer.HTMLTag) {
|
2009-12-15 16:33:31 -07:00
|
|
|
text = line
|
2009-10-22 10:41:38 -06:00
|
|
|
// minimal syntax-coloring of comments for now - people will want more
|
|
|
|
// (don't do anything more until there's a button to turn it on/off)
|
2009-12-15 16:33:31 -07:00
|
|
|
tag = printer.HTMLTag{`<span class="comment">`, "</span>"}
|
|
|
|
return
|
2009-10-22 10:41:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-08 22:46:20 -07:00
|
|
|
func (s *Styler) BasicLit(x *ast.BasicLit) (text []byte, tag printer.HTMLTag) {
|
2009-12-15 16:33:31 -07:00
|
|
|
text = x.Value
|
|
|
|
return
|
2009-10-22 10:41:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-13 11:42:18 -06:00
|
|
|
func (s *Styler) Ident(name *ast.Ident) (text []byte, tag printer.HTMLTag) {
|
|
|
|
text = []byte(name.Name)
|
2010-03-16 15:17:42 -06:00
|
|
|
var str string
|
2010-08-13 11:42:18 -06:00
|
|
|
if id := s.identId(name); id >= 0 {
|
|
|
|
str = fmt.Sprintf(` id="%d"`, id)
|
2010-03-16 15:17:42 -06:00
|
|
|
}
|
2010-08-13 11:42:18 -06:00
|
|
|
if s.highlight == name.Name {
|
2010-03-16 15:17:42 -06:00
|
|
|
str += ` class="highlight"`
|
|
|
|
}
|
|
|
|
if str != "" {
|
|
|
|
tag = printer.HTMLTag{"<span" + str + ">", "</span>"}
|
2009-10-22 10:41:38 -06:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-10-22 10:41:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-08 22:46:20 -07:00
|
|
|
func (s *Styler) Token(tok token.Token) (text []byte, tag printer.HTMLTag) {
|
2010-02-25 17:01:29 -07:00
|
|
|
text = []byte(tok.String())
|
2009-12-15 16:33:31 -07:00
|
|
|
return
|
2009-10-22 10:41:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-17 15:10:49 -07:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Tab conversion
|
|
|
|
|
2010-02-25 17:01:29 -07:00
|
|
|
var spaces = []byte(" ") // 16 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
|
2009-12-17 15:10:49 -07:00
|
|
|
for i > len(spaces) {
|
|
|
|
i -= len(spaces)
|
|
|
|
if _, err = p.output.Write(spaces); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_, err = p.output.Write(spaces[0:i])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *tconv) Write(data []byte) (n int, err os.Error) {
|
|
|
|
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 {
|
|
|
|
case '\t', '\v':
|
|
|
|
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)
|
|
|
|
if p.state == collecting {
|
|
|
|
_, err = p.output.Write(data[pos:])
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Templates
|
|
|
|
|
2009-07-31 19:04:53 -06:00
|
|
|
// Write an AST-node to w; optionally html-escaped.
|
2009-10-27 11:34:31 -06:00
|
|
|
func writeNode(w io.Writer, node interface{}, html bool, styler printer.Styler) {
|
2010-01-11 13:58:13 -07:00
|
|
|
mode := printer.TabIndent | printer.UseSpaces
|
2009-07-31 19:04:53 -06:00
|
|
|
if html {
|
2009-11-09 13:07:39 -07:00
|
|
|
mode |= printer.GenHTML
|
2009-07-31 19:04:53 -06:00
|
|
|
}
|
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)
|
|
|
|
(&printer.Config{mode, *tabwidth, styler}).Fprint(&tconv{output: w}, node)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-31 19:04:53 -06:00
|
|
|
// Write text to w; optionally html-escaped.
|
|
|
|
func writeText(w io.Writer, text []byte, html bool) {
|
|
|
|
if html {
|
2009-12-15 16:33:31 -07:00
|
|
|
template.HTMLEscape(w, text)
|
|
|
|
return
|
2009-07-31 19:04:53 -06:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
w.Write(text)
|
2009-07-31 19:04:53 -06:00
|
|
|
}
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2009-07-31 19:04:53 -06:00
|
|
|
// Write anything to w; optionally html-escaped.
|
|
|
|
func writeAny(w io.Writer, x interface{}, html bool) {
|
2009-06-16 10:14:06 -06:00
|
|
|
switch v := x.(type) {
|
|
|
|
case []byte:
|
2009-11-09 13:07:39 -07:00
|
|
|
writeText(w, v, html)
|
2009-06-16 10:14:06 -06:00
|
|
|
case string:
|
2010-02-25 17:01:29 -07:00
|
|
|
writeText(w, []byte(v), html)
|
2009-12-01 10:15:05 -07:00
|
|
|
case ast.Decl, ast.Expr, ast.Stmt, *ast.File:
|
|
|
|
writeNode(w, x, html, &defaultStyler)
|
2009-07-31 19:04:53 -06:00
|
|
|
default:
|
|
|
|
if html {
|
2009-12-15 16:33:31 -07:00
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprint(&buf, x)
|
|
|
|
writeText(w, buf.Bytes(), true)
|
2009-07-31 19:04:53 -06:00
|
|
|
} else {
|
2009-11-09 13:07:39 -07:00
|
|
|
fmt.Fprint(w, x)
|
2009-07-31 19:04:53 -06:00
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Template formatter for "html" format.
|
|
|
|
func htmlFmt(w io.Writer, x interface{}, format string) {
|
2009-11-09 13:07:39 -07:00
|
|
|
writeAny(w, x, true)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-13 16:18:56 -07:00
|
|
|
// Template formatter for "html-esc" format.
|
|
|
|
func htmlEscFmt(w io.Writer, x interface{}, format string) {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
writeAny(&buf, x, false)
|
|
|
|
template.HTMLEscape(w, buf.Bytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
// Template formatter for "html-comment" format.
|
|
|
|
func htmlCommentFmt(w io.Writer, x interface{}, format string) {
|
2009-12-15 16:33:31 -07:00
|
|
|
var buf bytes.Buffer
|
|
|
|
writeAny(&buf, x, false)
|
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.
|
|
|
|
func textFmt(w io.Writer, x interface{}, format string) {
|
2009-11-09 13:07:39 -07:00
|
|
|
writeAny(w, x, false)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
// Template formatter for the various "url-xxx" formats.
|
|
|
|
func urlFmt(w io.Writer, x interface{}, format string) {
|
|
|
|
var path string
|
|
|
|
var line int
|
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 {
|
2009-12-15 16:33:31 -07:00
|
|
|
Pos() token.Position
|
2009-10-08 16:14:54 -06:00
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
switch t := x.(type) {
|
|
|
|
case string:
|
|
|
|
path = t
|
|
|
|
case positioner:
|
|
|
|
pos := t.Pos()
|
2009-08-03 10:53:00 -06:00
|
|
|
if pos.IsValid() {
|
2010-02-16 12:20:55 -07:00
|
|
|
path = pos.Filename
|
|
|
|
line = pos.Line
|
2009-08-03 10:53:00 -06:00
|
|
|
}
|
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
|
|
|
|
// map path
|
|
|
|
relpath := relativePath(path)
|
|
|
|
|
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":
|
|
|
|
// line id's in html-printed source are of the
|
|
|
|
// form "L%d" where %d stands for the line number
|
2010-03-24 17:28:59 -06:00
|
|
|
template.HTMLEscape(w, []byte(relpath))
|
2010-02-16 12:20:55 -07:00
|
|
|
fmt.Fprintf(w, "#L%d", line)
|
|
|
|
}
|
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.
|
|
|
|
func infoKindFmt(w io.Writer, x interface{}, format string) {
|
2009-12-15 16:33:31 -07:00
|
|
|
fmt.Fprintf(w, infoKinds[x.(SpotKind)]) // infoKind entries are html-escaped
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Template formatter for "infoLine" format.
|
|
|
|
func infoLineFmt(w io.Writer, x interface{}, format string) {
|
2009-12-15 16:33:31 -07:00
|
|
|
info := x.(SpotInfo)
|
|
|
|
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.
|
|
|
|
func infoSnippetFmt(w io.Writer, x interface{}, format string) {
|
2009-12-15 16:33:31 -07:00
|
|
|
info := x.(SpotInfo)
|
|
|
|
text := `<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
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
fmt.Fprint(w, text)
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-08 17:47:32 -07:00
|
|
|
// Template formatter for "padding" format.
|
|
|
|
func paddingFmt(w io.Writer, x interface{}, format string) {
|
|
|
|
for i := x.(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.
|
|
|
|
func timeFmt(w io.Writer, x interface{}, format string) {
|
2010-04-27 11:45:33 -06:00
|
|
|
template.HTMLEscape(w, []byte(time.SecondsToLocalTime(x.(int64)/1e9).String()))
|
2009-11-08 17:47:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-13 14:09:33 -07:00
|
|
|
// Template formatter for "dir/" format.
|
|
|
|
func dirslashFmt(w io.Writer, x interface{}, format string) {
|
2010-04-09 12:36:40 -06:00
|
|
|
if x.(*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.
|
|
|
|
func localnameFmt(w io.Writer, x interface{}, format string) {
|
|
|
|
_, localname := pathutil.Split(x.(string))
|
2010-02-25 17:01:29 -07:00
|
|
|
template.HTMLEscape(w, []byte(localname))
|
2010-02-16 12:20:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-16 10:14:06 -06:00
|
|
|
var fmap = template.FormatterMap{
|
2010-03-02 14:46:51 -07:00
|
|
|
"": textFmt,
|
|
|
|
"html": htmlFmt,
|
|
|
|
"html-esc": htmlEscFmt,
|
2009-06-16 10:14:06 -06:00
|
|
|
"html-comment": htmlCommentFmt,
|
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,
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func readTemplate(name string) *template.Template {
|
2010-03-19 16:20:20 -06:00
|
|
|
path := pathutil.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 {
|
2009-11-09 13:07:39 -07:00
|
|
|
log.Exitf("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 {
|
2009-11-09 13:07:39 -07:00
|
|
|
log.Exitf("%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,
|
2010-03-19 13:46:43 -06:00
|
|
|
searchText,
|
2010-03-16 15:17:42 -06:00
|
|
|
sourceHTML *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")
|
2010-03-16 15:17:42 -06:00
|
|
|
sourceHTML = readTemplate("source.html")
|
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
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
if err := godocHTML.Execute(&d, w); 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) {
|
|
|
|
w.SetHeader("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
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
|
|
|
|
if err := t.Execute(data, &buf); 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 serveGoSource(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
|
2010-08-13 11:42:18 -06:00
|
|
|
file, err := parser.ParseFile(abspath, nil, parser.ParseComments)
|
2009-12-01 10:15:05 -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("parser.ParseFile: %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-06-16 10:14:06 -06:00
|
|
|
}
|
|
|
|
|
2010-08-26 15:36:13 -06:00
|
|
|
// TODO(gri) enable once we are confident it works for all files
|
2010-08-13 11:42:18 -06:00
|
|
|
// augment AST with types; ignore errors (partial type information ok)
|
2010-08-26 15:36:13 -06:00
|
|
|
// typechecker.CheckFile(file, nil)
|
2010-08-13 11:42:18 -06:00
|
|
|
|
2009-12-15 16:33:31 -07:00
|
|
|
var buf bytes.Buffer
|
2010-03-16 15:17:42 -06:00
|
|
|
styler := newStyler(r.FormValue("h"))
|
|
|
|
writeNode(&buf, file, true, styler)
|
|
|
|
|
|
|
|
type SourceInfo struct {
|
2010-08-13 11:42:18 -06:00
|
|
|
IdList []byte
|
2010-03-16 15:17:42 -06:00
|
|
|
Source []byte
|
|
|
|
}
|
2010-08-13 11:42:18 -06:00
|
|
|
info := &SourceInfo{styler.idList(), buf.Bytes()}
|
2009-06-16 10:14:06 -06:00
|
|
|
|
2010-03-16 15:17:42 -06:00
|
|
|
contents := applyTemplate(sourceHTML, "sourceHTML", info)
|
2010-09-28 22:30:12 -06:00
|
|
|
servePage(w, "Source file "+relpath, "", "", contents)
|
2009-09-03 10:58:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
|
2009-11-08 22:46:20 -07:00
|
|
|
if canonical := pathutil.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
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-08 01:40:43 -07:00
|
|
|
// TODO(gri): Should have a mapping from extension to handler, eventually.
|
|
|
|
|
|
|
|
// textExt[x] is true if the extension x indicates a text file, and false otherwise.
|
|
|
|
var textExt = map[string]bool{
|
2009-12-15 16:33:31 -07:00
|
|
|
".css": false, // must be served raw
|
2010-03-02 14:46:51 -07:00
|
|
|
".js": false, // must be served raw
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func isTextFile(path string) bool {
|
|
|
|
// if the extension is known, use it for decision making
|
|
|
|
if isText, found := textExt[pathutil.Ext(path)]; found {
|
2009-11-09 13:07:39 -07:00
|
|
|
return isText
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// the extension is not known; read an initial chunk of
|
|
|
|
// file and check if it looks like correct UTF-8; if it
|
|
|
|
// does, it's probably a text file
|
2009-12-15 16:33:31 -07:00
|
|
|
f, err := os.Open(path, os.O_RDONLY, 0)
|
2009-11-08 01:40:43 -07:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return false
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
defer f.Close()
|
2009-11-08 01:40:43 -07:00
|
|
|
|
2009-12-15 16:33:31 -07:00
|
|
|
var buf [1024]byte
|
2010-05-27 15:51:47 -06:00
|
|
|
n, err := f.Read(buf[0:])
|
2009-11-08 01:40:43 -07:00
|
|
|
if err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return false
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
|
|
|
|
2009-12-15 16:33:31 -07:00
|
|
|
s := string(buf[0:n])
|
|
|
|
n -= utf8.UTFMax // make sure there's enough bytes for a complete unicode char
|
2009-11-08 01:40:43 -07:00
|
|
|
for i, c := range s {
|
|
|
|
if i > n {
|
2009-11-09 13:07:39 -07:00
|
|
|
break
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
|
|
|
if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' {
|
|
|
|
// decoding error or control character - not a text file
|
2009-11-09 13:07:39 -07:00
|
|
|
return false
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// likely a text file
|
2009-12-15 16:33:31 -07:00
|
|
|
return true
|
2009-11-08 01:40:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath 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
|
|
|
}
|
|
|
|
|
2009-12-15 16:33:31 -07:00
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprintln(&buf, "<pre>")
|
|
|
|
template.HTMLEscape(&buf, src)
|
|
|
|
fmt.Fprintln(&buf, "</pre>")
|
2009-11-08 01:40:43 -07:00
|
|
|
|
2010-09-28 22:30:12 -06:00
|
|
|
servePage(w, "Text file "+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 "/":
|
2010-09-28 22:30:12 -06:00
|
|
|
serveHTMLDoc(w, r, pathutil.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
|
|
|
|
2010-02-16 12:20:55 -07:00
|
|
|
switch pathutil.Ext(abspath) {
|
|
|
|
case ".html":
|
|
|
|
if strings.HasSuffix(abspath, "/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":
|
2010-09-28 22:30:12 -06:00
|
|
|
serveGoSource(w, r, abspath, relpath)
|
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
|
|
|
|
}
|
2010-02-16 12:20:55 -07:00
|
|
|
if index := abspath + "/index.html"; isTextFile(index) {
|
2010-09-28 22:30:12 -06:00
|
|
|
serveHTMLDoc(w, r, index, relativePath(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) {
|
2010-09-28 22:30:12 -06:00
|
|
|
serveTextFile(w, r, abspath, relpath)
|
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
|
|
|
|
genDoc // generate documentation
|
|
|
|
)
|
|
|
|
|
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
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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-
|
|
|
|
// directories, PageInfo.Dirs is nil. If a directory read error occured,
|
|
|
|
// 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
|
2010-03-12 19:16:21 -07:00
|
|
|
pkgs, err := parser.ParseDir(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.
|
|
|
|
dirpath, dirname := pathutil.Split(abspath)
|
|
|
|
|
|
|
|
// 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" {
|
|
|
|
_, dirname = pathutil.Split(pathutil.Clean(dirpath))
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
for name, _ := range pkgs {
|
|
|
|
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 {
|
2010-03-10 16:22:22 -07:00
|
|
|
pdoc = doc.NewPackageDoc(pkg, pathutil.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
|
|
|
|
2010-09-16 14:45:40 -06:00
|
|
|
return PageInfo{abspath, plist, 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
|
2010-02-16 12:20:55 -07:00
|
|
|
_, pkgname := pathutil.Split(pathutil.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:
|
2010-02-16 12:20:55 -07:00
|
|
|
title = "Directory " + relativePath(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 {
|
2009-12-15 16:33:31 -07:00
|
|
|
Query string
|
|
|
|
Hit *LookupResult
|
|
|
|
Alt *AltWords
|
|
|
|
Illegal bool
|
|
|
|
Accurate bool
|
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
|
2009-10-27 11:34:31 -06:00
|
|
|
if index, timestamp := searchIndex.get(); index != nil {
|
2009-12-15 16:33:31 -07:00
|
|
|
result.Hit, result.Alt, result.Illegal = index.(*Index).Lookup(query)
|
|
|
|
_, ts := fsTree.get()
|
|
|
|
result.Accurate = timestamp >= ts
|
2009-10-27 11:34:31 -06: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
|
2009-10-27 11:34:31 -06:00
|
|
|
if result.Hit != nil {
|
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
|
|
|
|
2009-10-30 11:58:53 -06:00
|
|
|
func indexer() {
|
|
|
|
for {
|
2009-12-15 16:33:31 -07:00
|
|
|
_, ts := fsTree.get()
|
2009-10-30 11:58:53 -06:00
|
|
|
if _, timestamp := searchIndex.get(); timestamp < ts {
|
|
|
|
// index possibly out of date - make a new one
|
|
|
|
// (could use a channel to send an explicit signal
|
|
|
|
// from the sync goroutine, but this solution is
|
|
|
|
// more decoupled, trivial, and works well enough)
|
2009-12-15 16:33:31 -07:00
|
|
|
start := time.Nanoseconds()
|
2010-03-19 16:20:20 -06:00
|
|
|
index := NewIndex(*goroot)
|
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
|
|
|
|
nwords, nspots := index.Size()
|
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("index updated (%gs, %d unique words, %d spots)", secs, nwords, nspots)
|
2009-10-27 11:34:31 -06:00
|
|
|
}
|
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("bytes=%d footprint=%d\n", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
|
2010-03-19 18:46:18 -06:00
|
|
|
runtime.GC()
|
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("bytes=%d footprint=%d\n", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|
2009-12-15 16:33:31 -07:00
|
|
|
time.Sleep(1 * 60e9) // try once a minute
|
2009-08-03 10:53:00 -06:00
|
|
|
}
|
2009-06-16 10:14:06 -06:00
|
|
|
}
|