Add generated file

This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
xing-yang
2018-07-12 10:55:15 -07:00
parent 36b1de0341
commit e213d1890d
17729 changed files with 5090889 additions and 0 deletions

31
vendor/golang.org/x/tools/godoc/README.md generated vendored Normal file
View File

@@ -0,0 +1,31 @@
# godoc
This directory contains most of the code for running a godoc server. The
executable lives at golang.org/x/tools/cmd/godoc.
## Development mode
In production, CSS/JS/template assets need to be compiled into the godoc
binary. It can be tedious to recompile assets every time, but you can pass a
flag to load CSS/JS/templates from disk every time a page loads:
```
godoc -templates=$GOPATH/src/golang.org/x/tools/godoc/static -http=:6060
```
## Recompiling static assets
The files that live at `static/style.css`, `static/jquery.js` and so on are not
present in the final binary. They are placed into `static/static.go` by running
`go generate`. So to compile a change and test it in your browser:
1) Make changes to e.g. `static/style.css`.
2) Run `go generate golang.org/x/tools/godoc/static` so `static/static.go` picks
up the change.
3) Run `go install golang.org/x/tools/cmd/godoc` so the compiled `godoc` binary
picks up the change.
4) Run `godoc -http=:6060` and view your changes in the browser. You may need
to disable your browser's cache to avoid reloading a stale file.

111
vendor/golang.org/x/tools/godoc/analysis/README generated vendored Normal file
View File

@@ -0,0 +1,111 @@
Type and Pointer Analysis to-do list
====================================
Alan Donovan <adonovan@google.com>
Overall design
--------------
We should re-run the type and pointer analyses periodically,
as we do with the indexer.
Version skew: how to mitigate the bad effects of stale URLs in old pages?
We could record the file's length/CRC32/mtime in the go/loader, and
refuse to decorate it with links unless they match at serving time.
Use the VFS mechanism when (a) enumerating packages and (b) loading
them. (Requires planned changes to go/loader.)
Future work: shard this using map/reduce for larger corpora.
Testing: how does one test that a web page "looks right"?
Bugs
----
(*ssa.Program).Create requires transitively error-free packages. We
can make this more robust by making the requirement transitively free
of "hard" errors; soft errors are fine.
Markup of compiler errors is slightly buggy because they overlap with
other selections (e.g. Idents). Fix.
User Interface
--------------
CALLGRAPH:
- Add a search box: given a search node, expand path from each entry
point to it.
- Cause hovering over a given node to highlight that node, and all
nodes that are logically identical to it.
- Initially expand the callgraph trees (but not their toggle divs).
CALLEES:
- The '(' links are not very discoverable. Highlight them?
Type info:
- In the source viewer's lower pane, use a toggle div around the
IMPLEMENTS and METHODSETS lists, like we do in the pacakge view.
Only expand them initially if short.
- Include IMPLEMENTS and METHOD SETS information in search index.
- URLs in IMPLEMENTS/METHOD SETS always link to source, even from the
package docs view. This makes sense for links to non-exported
types, but links to exported types and funcs should probably go to
other package docs.
- Suppress toggle divs for empty method sets.
Misc:
- The [X] button in the lower pane is subject to scrolling.
- Should the lower pane be floating? An iframe?
When we change document.location by clicking on a link, it will go away.
How do we prevent that (a la Gmail's chat windows)?
- Progress/status: for each file, display its analysis status, one of:
- not in analysis scope
- type analysis running...
- type analysis complete
(+ optionally: there were type errors in this file)
And if PTA requested:
- type analysis complete; PTA not attempted due to type errors
- PTA running...
- PTA complete
- Scroll the selection into view, e.g. the vertical center, or better
still, under the pointer (assuming we have a mouse).
More features
-------------
Display the REFERRERS relation? (Useful but potentially large.)
Display the INSTANTIATIONS relation? i.e. given a type T, show the set of
syntactic constructs that can instantiate it:
var x T
x := T{...}
x = new(T)
x = make([]T, n)
etc
+ all INSTANTIATIONS of all S defined as struct{t T} or [n]T
(Potentially a lot of information.)
(Add this to guru too.)
Optimisations
-------------
Each call to addLink takes a (per-file) lock. The locking is
fine-grained so server latency isn't terrible, but overall it makes
the link computation quite slow. Batch update might be better.
Memory usage is now about 1.5GB for GOROOT + go.tools. It used to be 700MB.
Optimize for time and space. The main slowdown is the network I/O
time caused by an increase in page size of about 3x: about 2x from
HTML, and 0.7--2.1x from JSON (unindented vs indented). The JSON
contains a lot of filenames (e.g. 820 copies of 16 distinct
filenames). 20% of the HTML is L%d spans (now disabled). The HTML
also contains lots of tooltips for long struct/interface types.
De-dup or just abbreviate? The actual formatting is very fast.

613
vendor/golang.org/x/tools/godoc/analysis/analysis.go generated vendored Normal file
View File

@@ -0,0 +1,613 @@
// Copyright 2014 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 analysis performs type and pointer analysis
// and generates mark-up for the Go source view.
//
// The Run method populates a Result object by running type and
// (optionally) pointer analysis. The Result object is thread-safe
// and at all times may be accessed by a serving thread, even as it is
// progressively populated as analysis facts are derived.
//
// The Result is a mapping from each godoc file URL
// (e.g. /src/fmt/print.go) to information about that file. The
// information is a list of HTML markup links and a JSON array of
// structured data values. Some of the links call client-side
// JavaScript functions that index this array.
//
// The analysis computes mark-up for the following relations:
//
// IMPORTS: for each ast.ImportSpec, the package that it denotes.
//
// RESOLUTION: for each ast.Ident, its kind and type, and the location
// of its definition.
//
// METHOD SETS, IMPLEMENTS: for each ast.Ident defining a named type,
// its method-set, the set of interfaces it implements or is
// implemented by, and its size/align values.
//
// CALLERS, CALLEES: for each function declaration ('func' token), its
// callers, and for each call-site ('(' token), its callees.
//
// CALLGRAPH: the package docs include an interactive viewer for the
// intra-package call graph of "fmt".
//
// CHANNEL PEERS: for each channel operation make/<-/close, the set of
// other channel ops that alias the same channel(s).
//
// ERRORS: for each locus of a frontend (scanner/parser/type) error, the
// location is highlighted in red and hover text provides the compiler
// error message.
//
package analysis // import "golang.org/x/tools/godoc/analysis"
import (
"fmt"
"go/build"
"go/scanner"
"go/token"
"go/types"
"html"
"io"
"log"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
// -- links ------------------------------------------------------------
// A Link is an HTML decoration of the bytes [Start, End) of a file.
// Write is called before/after those bytes to emit the mark-up.
type Link interface {
Start() int
End() int
Write(w io.Writer, _ int, start bool) // the godoc.LinkWriter signature
}
// An <a> element.
type aLink struct {
start, end int // =godoc.Segment
title string // hover text
onclick string // JS code (NB: trusted)
href string // URL (NB: trusted)
}
func (a aLink) Start() int { return a.start }
func (a aLink) End() int { return a.end }
func (a aLink) Write(w io.Writer, _ int, start bool) {
if start {
fmt.Fprintf(w, `<a title='%s'`, html.EscapeString(a.title))
if a.onclick != "" {
fmt.Fprintf(w, ` onclick='%s'`, html.EscapeString(a.onclick))
}
if a.href != "" {
// TODO(adonovan): I think that in principle, a.href must first be
// url.QueryEscape'd, but if I do that, a leading slash becomes "%2F",
// which causes the browser to treat the path as relative, not absolute.
// WTF?
fmt.Fprintf(w, ` href='%s'`, html.EscapeString(a.href))
}
fmt.Fprintf(w, ">")
} else {
fmt.Fprintf(w, "</a>")
}
}
// An <a class='error'> element.
type errorLink struct {
start int
msg string
}
func (e errorLink) Start() int { return e.start }
func (e errorLink) End() int { return e.start + 1 }
func (e errorLink) Write(w io.Writer, _ int, start bool) {
// <span> causes havoc, not sure why, so use <a>.
if start {
fmt.Fprintf(w, `<a class='error' title='%s'>`, html.EscapeString(e.msg))
} else {
fmt.Fprintf(w, "</a>")
}
}
// -- fileInfo ---------------------------------------------------------
// FileInfo holds analysis information for the source file view.
// Clients must not mutate it.
type FileInfo struct {
Data []interface{} // JSON serializable values
Links []Link // HTML link markup
}
// A fileInfo is the server's store of hyperlinks and JSON data for a
// particular file.
type fileInfo struct {
mu sync.Mutex
data []interface{} // JSON objects
links []Link
sorted bool
hasErrors bool // TODO(adonovan): surface this in the UI
}
// addLink adds a link to the Go source file fi.
func (fi *fileInfo) addLink(link Link) {
fi.mu.Lock()
fi.links = append(fi.links, link)
fi.sorted = false
if _, ok := link.(errorLink); ok {
fi.hasErrors = true
}
fi.mu.Unlock()
}
// addData adds the structured value x to the JSON data for the Go
// source file fi. Its index is returned.
func (fi *fileInfo) addData(x interface{}) int {
fi.mu.Lock()
index := len(fi.data)
fi.data = append(fi.data, x)
fi.mu.Unlock()
return index
}
// get returns the file info in external form.
// Callers must not mutate its fields.
func (fi *fileInfo) get() FileInfo {
var r FileInfo
// Copy slices, to avoid races.
fi.mu.Lock()
r.Data = append(r.Data, fi.data...)
if !fi.sorted {
sort.Sort(linksByStart(fi.links))
fi.sorted = true
}
r.Links = append(r.Links, fi.links...)
fi.mu.Unlock()
return r
}
// PackageInfo holds analysis information for the package view.
// Clients must not mutate it.
type PackageInfo struct {
CallGraph []*PCGNodeJSON
CallGraphIndex map[string]int
Types []*TypeInfoJSON
}
type pkgInfo struct {
mu sync.Mutex
callGraph []*PCGNodeJSON
callGraphIndex map[string]int // keys are (*ssa.Function).RelString()
types []*TypeInfoJSON // type info for exported types
}
func (pi *pkgInfo) setCallGraph(callGraph []*PCGNodeJSON, callGraphIndex map[string]int) {
pi.mu.Lock()
pi.callGraph = callGraph
pi.callGraphIndex = callGraphIndex
pi.mu.Unlock()
}
func (pi *pkgInfo) addType(t *TypeInfoJSON) {
pi.mu.Lock()
pi.types = append(pi.types, t)
pi.mu.Unlock()
}
// get returns the package info in external form.
// Callers must not mutate its fields.
func (pi *pkgInfo) get() PackageInfo {
var r PackageInfo
// Copy slices, to avoid races.
pi.mu.Lock()
r.CallGraph = append(r.CallGraph, pi.callGraph...)
r.CallGraphIndex = pi.callGraphIndex
r.Types = append(r.Types, pi.types...)
pi.mu.Unlock()
return r
}
// -- Result -----------------------------------------------------------
// Result contains the results of analysis.
// The result contains a mapping from filenames to a set of HTML links
// and JavaScript data referenced by the links.
type Result struct {
mu sync.Mutex // guards maps (but not their contents)
status string // global analysis status
fileInfos map[string]*fileInfo // keys are godoc file URLs
pkgInfos map[string]*pkgInfo // keys are import paths
}
// fileInfo returns the fileInfo for the specified godoc file URL,
// constructing it as needed. Thread-safe.
func (res *Result) fileInfo(url string) *fileInfo {
res.mu.Lock()
fi, ok := res.fileInfos[url]
if !ok {
if res.fileInfos == nil {
res.fileInfos = make(map[string]*fileInfo)
}
fi = new(fileInfo)
res.fileInfos[url] = fi
}
res.mu.Unlock()
return fi
}
// Status returns a human-readable description of the current analysis status.
func (res *Result) Status() string {
res.mu.Lock()
defer res.mu.Unlock()
return res.status
}
func (res *Result) setStatusf(format string, args ...interface{}) {
res.mu.Lock()
res.status = fmt.Sprintf(format, args...)
log.Printf(format, args...)
res.mu.Unlock()
}
// FileInfo returns new slices containing opaque JSON values and the
// HTML link markup for the specified godoc file URL. Thread-safe.
// Callers must not mutate the elements.
// It returns "zero" if no data is available.
//
func (res *Result) FileInfo(url string) (fi FileInfo) {
return res.fileInfo(url).get()
}
// pkgInfo returns the pkgInfo for the specified import path,
// constructing it as needed. Thread-safe.
func (res *Result) pkgInfo(importPath string) *pkgInfo {
res.mu.Lock()
pi, ok := res.pkgInfos[importPath]
if !ok {
if res.pkgInfos == nil {
res.pkgInfos = make(map[string]*pkgInfo)
}
pi = new(pkgInfo)
res.pkgInfos[importPath] = pi
}
res.mu.Unlock()
return pi
}
// PackageInfo returns new slices of JSON values for the callgraph and
// type info for the specified package. Thread-safe.
// Callers must not mutate its fields.
// PackageInfo returns "zero" if no data is available.
//
func (res *Result) PackageInfo(importPath string) PackageInfo {
return res.pkgInfo(importPath).get()
}
// -- analysis ---------------------------------------------------------
type analysis struct {
result *Result
prog *ssa.Program
ops []chanOp // all channel ops in program
allNamed []*types.Named // all "defined" (formerly "named") types in the program
ptaConfig pointer.Config
path2url map[string]string // maps openable path to godoc file URL (/src/fmt/print.go)
pcgs map[*ssa.Package]*packageCallGraph
}
// fileAndOffset returns the file and offset for a given pos.
func (a *analysis) fileAndOffset(pos token.Pos) (fi *fileInfo, offset int) {
return a.fileAndOffsetPosn(a.prog.Fset.Position(pos))
}
// fileAndOffsetPosn returns the file and offset for a given position.
func (a *analysis) fileAndOffsetPosn(posn token.Position) (fi *fileInfo, offset int) {
url := a.path2url[posn.Filename]
return a.result.fileInfo(url), posn.Offset
}
// posURL returns the URL of the source extent [pos, pos+len).
func (a *analysis) posURL(pos token.Pos, len int) string {
if pos == token.NoPos {
return ""
}
posn := a.prog.Fset.Position(pos)
url := a.path2url[posn.Filename]
return fmt.Sprintf("%s?s=%d:%d#L%d",
url, posn.Offset, posn.Offset+len, posn.Line)
}
// ----------------------------------------------------------------------
// Run runs program analysis and computes the resulting markup,
// populating *result in a thread-safe manner, first with type
// information then later with pointer analysis information if
// enabled by the pta flag.
//
func Run(pta bool, result *Result) {
conf := loader.Config{
AllowErrors: true,
}
// Silence the default error handler.
// Don't print all errors; we'll report just
// one per errant package later.
conf.TypeChecker.Error = func(e error) {}
var roots, args []string // roots[i] ends with os.PathSeparator
// Enumerate packages in $GOROOT.
root := filepath.Join(build.Default.GOROOT, "src") + string(os.PathSeparator)
roots = append(roots, root)
args = allPackages(root)
log.Printf("GOROOT=%s: %s\n", root, args)
// Enumerate packages in $GOPATH.
for i, dir := range filepath.SplitList(build.Default.GOPATH) {
root := filepath.Join(dir, "src") + string(os.PathSeparator)
roots = append(roots, root)
pkgs := allPackages(root)
log.Printf("GOPATH[%d]=%s: %s\n", i, root, pkgs)
args = append(args, pkgs...)
}
// Uncomment to make startup quicker during debugging.
//args = []string{"golang.org/x/tools/cmd/godoc"}
//args = []string{"fmt"}
if _, err := conf.FromArgs(args, true); err != nil {
// TODO(adonovan): degrade gracefully, not fail totally.
// (The crippling case is a parse error in an external test file.)
result.setStatusf("Analysis failed: %s.", err) // import error
return
}
result.setStatusf("Loading and type-checking packages...")
iprog, err := conf.Load()
if iprog != nil {
// Report only the first error of each package.
for _, info := range iprog.AllPackages {
for _, err := range info.Errors {
fmt.Fprintln(os.Stderr, err)
break
}
}
log.Printf("Loaded %d packages.", len(iprog.AllPackages))
}
if err != nil {
result.setStatusf("Loading failed: %s.\n", err)
return
}
// Create SSA-form program representation.
// Only the transitively error-free packages are used.
prog := ssautil.CreateProgram(iprog, ssa.GlobalDebug)
// Create a "testmain" package for each package with tests.
for _, pkg := range prog.AllPackages() {
if testmain := prog.CreateTestMainPackage(pkg); testmain != nil {
log.Printf("Adding tests for %s", pkg.Pkg.Path())
}
}
// Build SSA code for bodies of all functions in the whole program.
result.setStatusf("Constructing SSA form...")
prog.Build()
log.Print("SSA construction complete")
a := analysis{
result: result,
prog: prog,
pcgs: make(map[*ssa.Package]*packageCallGraph),
}
// Build a mapping from openable filenames to godoc file URLs,
// i.e. "/src/" plus path relative to GOROOT/src or GOPATH[i]/src.
a.path2url = make(map[string]string)
for _, info := range iprog.AllPackages {
nextfile:
for _, f := range info.Files {
if f.Pos() == 0 {
continue // e.g. files generated by cgo
}
abs := iprog.Fset.File(f.Pos()).Name()
// Find the root to which this file belongs.
for _, root := range roots {
rel := strings.TrimPrefix(abs, root)
if len(rel) < len(abs) {
a.path2url[abs] = "/src/" + filepath.ToSlash(rel)
continue nextfile
}
}
log.Printf("Can't locate file %s (package %q) beneath any root",
abs, info.Pkg.Path())
}
}
// Add links for scanner, parser, type-checker errors.
// TODO(adonovan): fix: these links can overlap with
// identifier markup, causing the renderer to emit some
// characters twice.
errors := make(map[token.Position][]string)
for _, info := range iprog.AllPackages {
for _, err := range info.Errors {
switch err := err.(type) {
case types.Error:
posn := a.prog.Fset.Position(err.Pos)
errors[posn] = append(errors[posn], err.Msg)
case scanner.ErrorList:
for _, e := range err {
errors[e.Pos] = append(errors[e.Pos], e.Msg)
}
default:
log.Printf("Package %q has error (%T) without position: %v\n",
info.Pkg.Path(), err, err)
}
}
}
for posn, errs := range errors {
fi, offset := a.fileAndOffsetPosn(posn)
fi.addLink(errorLink{
start: offset,
msg: strings.Join(errs, "\n"),
})
}
// ---------- type-based analyses ----------
// Compute the all-pairs IMPLEMENTS relation.
// Collect all named types, even local types
// (which can have methods via promotion)
// and the built-in "error".
errorType := types.Universe.Lookup("error").Type().(*types.Named)
a.allNamed = append(a.allNamed, errorType)
for _, info := range iprog.AllPackages {
for _, obj := range info.Defs {
if obj, ok := obj.(*types.TypeName); ok {
if named, ok := obj.Type().(*types.Named); ok {
a.allNamed = append(a.allNamed, named)
}
}
}
}
log.Print("Computing implements relation...")
facts := computeImplements(&a.prog.MethodSets, a.allNamed)
// Add the type-based analysis results.
log.Print("Extracting type info...")
for _, info := range iprog.AllPackages {
a.doTypeInfo(info, facts)
}
a.visitInstrs(pta)
result.setStatusf("Type analysis complete.")
if pta {
mainPkgs := ssautil.MainPackages(prog.AllPackages())
log.Print("Transitively error-free main packages: ", mainPkgs)
a.pointer(mainPkgs)
}
}
// visitInstrs visits all SSA instructions in the program.
func (a *analysis) visitInstrs(pta bool) {
log.Print("Visit instructions...")
for fn := range ssautil.AllFunctions(a.prog) {
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
// CALLEES (static)
// (Dynamic calls require pointer analysis.)
//
// We use the SSA representation to find the static callee,
// since in many cases it does better than the
// types.Info.{Refs,Selection} information. For example:
//
// defer func(){}() // static call to anon function
// f := func(){}; f() // static call to anon function
// f := fmt.Println; f() // static call to named function
//
// The downside is that we get no static callee information
// for packages that (transitively) contain errors.
if site, ok := instr.(ssa.CallInstruction); ok {
if callee := site.Common().StaticCallee(); callee != nil {
// TODO(adonovan): callgraph: elide wrappers.
// (Do static calls ever go to wrappers?)
if site.Common().Pos() != token.NoPos {
a.addCallees(site, []*ssa.Function{callee})
}
}
}
if !pta {
continue
}
// CHANNEL PEERS
// Collect send/receive/close instructions in the whole ssa.Program.
for _, op := range chanOps(instr) {
a.ops = append(a.ops, op)
a.ptaConfig.AddQuery(op.ch) // add channel ssa.Value to PTA query
}
}
}
}
log.Print("Visit instructions complete")
}
// pointer runs the pointer analysis.
func (a *analysis) pointer(mainPkgs []*ssa.Package) {
// Run the pointer analysis and build the complete callgraph.
a.ptaConfig.Mains = mainPkgs
a.ptaConfig.BuildCallGraph = true
a.ptaConfig.Reflection = false // (for now)
a.result.setStatusf("Pointer analysis running...")
ptares, err := pointer.Analyze(&a.ptaConfig)
if err != nil {
// If this happens, it indicates a bug.
a.result.setStatusf("Pointer analysis failed: %s.", err)
return
}
log.Print("Pointer analysis complete.")
// Add the results of pointer analysis.
a.result.setStatusf("Computing channel peers...")
a.doChannelPeers(ptares.Queries)
a.result.setStatusf("Computing dynamic call graph edges...")
a.doCallgraph(ptares.CallGraph)
a.result.setStatusf("Analysis complete.")
}
type linksByStart []Link
func (a linksByStart) Less(i, j int) bool { return a[i].Start() < a[j].Start() }
func (a linksByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a linksByStart) Len() int { return len(a) }
// allPackages returns a new sorted slice of all packages beneath the
// specified package root directory, e.g. $GOROOT/src or $GOPATH/src.
// Derived from from go/ssa/stdlib_test.go
// root must end with os.PathSeparator.
//
// TODO(adonovan): use buildutil.AllPackages when the tree thaws.
func allPackages(root string) []string {
var pkgs []string
filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if info == nil {
return nil // non-existent root directory?
}
if !info.IsDir() {
return nil // not a directory
}
// Prune the search if we encounter any of these names:
base := filepath.Base(path)
if base == "testdata" || strings.HasPrefix(base, ".") {
return filepath.SkipDir
}
pkg := filepath.ToSlash(strings.TrimPrefix(path, root))
switch pkg {
case "builtin":
return filepath.SkipDir
case "":
return nil // ignore root of tree
}
pkgs = append(pkgs, pkg)
return nil
})
return pkgs
}

351
vendor/golang.org/x/tools/godoc/analysis/callgraph.go generated vendored Normal file
View File

@@ -0,0 +1,351 @@
// Copyright 2014 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 analysis
// This file computes the CALLERS and CALLEES relations from the call
// graph. CALLERS/CALLEES information is displayed in the lower pane
// when a "func" token or ast.CallExpr.Lparen is clicked, respectively.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"log"
"math/big"
"sort"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
)
// doCallgraph computes the CALLEES and CALLERS relations.
func (a *analysis) doCallgraph(cg *callgraph.Graph) {
log.Print("Deleting synthetic nodes...")
// TODO(adonovan): opt: DeleteSyntheticNodes is asymptotically
// inefficient and can be (unpredictably) slow.
cg.DeleteSyntheticNodes()
log.Print("Synthetic nodes deleted")
// Populate nodes of package call graphs (PCGs).
for _, n := range cg.Nodes {
a.pcgAddNode(n.Func)
}
// Within each PCG, sort funcs by name.
for _, pcg := range a.pcgs {
pcg.sortNodes()
}
calledFuncs := make(map[ssa.CallInstruction]map[*ssa.Function]bool)
callingSites := make(map[*ssa.Function]map[ssa.CallInstruction]bool)
for _, n := range cg.Nodes {
for _, e := range n.Out {
if e.Site == nil {
continue // a call from a synthetic node such as <root>
}
// Add (site pos, callee) to calledFuncs.
// (Dynamic calls only.)
callee := e.Callee.Func
a.pcgAddEdge(n.Func, callee)
if callee.Synthetic != "" {
continue // call of a package initializer
}
if e.Site.Common().StaticCallee() == nil {
// dynamic call
// (CALLEES information for static calls
// is computed using SSA information.)
lparen := e.Site.Common().Pos()
if lparen != token.NoPos {
fns := calledFuncs[e.Site]
if fns == nil {
fns = make(map[*ssa.Function]bool)
calledFuncs[e.Site] = fns
}
fns[callee] = true
}
}
// Add (callee, site) to callingSites.
fns := callingSites[callee]
if fns == nil {
fns = make(map[ssa.CallInstruction]bool)
callingSites[callee] = fns
}
fns[e.Site] = true
}
}
// CALLEES.
log.Print("Callees...")
for site, fns := range calledFuncs {
var funcs funcsByPos
for fn := range fns {
funcs = append(funcs, fn)
}
sort.Sort(funcs)
a.addCallees(site, funcs)
}
// CALLERS
log.Print("Callers...")
for callee, sites := range callingSites {
pos := funcToken(callee)
if pos == token.NoPos {
log.Printf("CALLERS: skipping %s: no pos", callee)
continue
}
var this *types.Package // for relativizing names
if callee.Pkg != nil {
this = callee.Pkg.Pkg
}
// Compute sites grouped by parent, with text and URLs.
sitesByParent := make(map[*ssa.Function]sitesByPos)
for site := range sites {
fn := site.Parent()
sitesByParent[fn] = append(sitesByParent[fn], site)
}
var funcs funcsByPos
for fn := range sitesByParent {
funcs = append(funcs, fn)
}
sort.Sort(funcs)
v := callersJSON{
Callee: callee.String(),
Callers: []callerJSON{}, // (JS wants non-nil)
}
for _, fn := range funcs {
caller := callerJSON{
Func: prettyFunc(this, fn),
Sites: []anchorJSON{}, // (JS wants non-nil)
}
sites := sitesByParent[fn]
sort.Sort(sites)
for _, site := range sites {
pos := site.Common().Pos()
if pos != token.NoPos {
caller.Sites = append(caller.Sites, anchorJSON{
Text: fmt.Sprintf("%d", a.prog.Fset.Position(pos).Line),
Href: a.posURL(pos, len("(")),
})
}
}
v.Callers = append(v.Callers, caller)
}
fi, offset := a.fileAndOffset(pos)
fi.addLink(aLink{
start: offset,
end: offset + len("func"),
title: fmt.Sprintf("%d callers", len(sites)),
onclick: fmt.Sprintf("onClickCallers(%d)", fi.addData(v)),
})
}
// PACKAGE CALLGRAPH
log.Print("Package call graph...")
for pkg, pcg := range a.pcgs {
// Maps (*ssa.Function).RelString() to index in JSON CALLGRAPH array.
index := make(map[string]int)
// Treat exported functions (and exported methods of
// exported named types) as roots even if they aren't
// actually called from outside the package.
for i, n := range pcg.nodes {
if i == 0 || n.fn.Object() == nil || !n.fn.Object().Exported() {
continue
}
recv := n.fn.Signature.Recv()
if recv == nil || deref(recv.Type()).(*types.Named).Obj().Exported() {
roots := &pcg.nodes[0].edges
roots.SetBit(roots, i, 1)
}
index[n.fn.RelString(pkg.Pkg)] = i
}
json := a.pcgJSON(pcg)
// TODO(adonovan): pkg.Path() is not unique!
// It is possible to declare a non-test package called x_test.
a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
}
}
// addCallees adds client data and links for the facts that site calls fns.
func (a *analysis) addCallees(site ssa.CallInstruction, fns []*ssa.Function) {
v := calleesJSON{
Descr: site.Common().Description(),
Callees: []anchorJSON{}, // (JS wants non-nil)
}
var this *types.Package // for relativizing names
if p := site.Parent().Package(); p != nil {
this = p.Pkg
}
for _, fn := range fns {
v.Callees = append(v.Callees, anchorJSON{
Text: prettyFunc(this, fn),
Href: a.posURL(funcToken(fn), len("func")),
})
}
fi, offset := a.fileAndOffset(site.Common().Pos())
fi.addLink(aLink{
start: offset,
end: offset + len("("),
title: fmt.Sprintf("%d callees", len(v.Callees)),
onclick: fmt.Sprintf("onClickCallees(%d)", fi.addData(v)),
})
}
// -- utilities --------------------------------------------------------
// stable order within packages but undefined across packages.
type funcsByPos []*ssa.Function
func (a funcsByPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
func (a funcsByPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a funcsByPos) Len() int { return len(a) }
type sitesByPos []ssa.CallInstruction
func (a sitesByPos) Less(i, j int) bool { return a[i].Common().Pos() < a[j].Common().Pos() }
func (a sitesByPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a sitesByPos) Len() int { return len(a) }
func funcToken(fn *ssa.Function) token.Pos {
switch syntax := fn.Syntax().(type) {
case *ast.FuncLit:
return syntax.Type.Func
case *ast.FuncDecl:
return syntax.Type.Func
}
return token.NoPos
}
// prettyFunc pretty-prints fn for the user interface.
// TODO(adonovan): return HTML so we have more markup freedom.
func prettyFunc(this *types.Package, fn *ssa.Function) string {
if fn.Parent() != nil {
return fmt.Sprintf("%s in %s",
types.TypeString(fn.Signature, types.RelativeTo(this)),
prettyFunc(this, fn.Parent()))
}
if fn.Synthetic != "" && fn.Name() == "init" {
// (This is the actual initializer, not a declared 'func init').
if fn.Pkg.Pkg == this {
return "package initializer"
}
return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
}
return fn.RelString(this)
}
// -- intra-package callgraph ------------------------------------------
// pcgNode represents a node in the package call graph (PCG).
type pcgNode struct {
fn *ssa.Function
pretty string // cache of prettyFunc(fn)
edges big.Int // set of callee func indices
}
// A packageCallGraph represents the intra-package edges of the global call graph.
// The zeroth node indicates "all external functions".
type packageCallGraph struct {
nodeIndex map[*ssa.Function]int // maps func to node index (a small int)
nodes []*pcgNode // maps node index to node
}
// sortNodes populates pcg.nodes in name order and updates the nodeIndex.
func (pcg *packageCallGraph) sortNodes() {
nodes := make([]*pcgNode, 0, len(pcg.nodeIndex))
nodes = append(nodes, &pcgNode{fn: nil, pretty: "<external>"})
for fn := range pcg.nodeIndex {
nodes = append(nodes, &pcgNode{
fn: fn,
pretty: prettyFunc(fn.Pkg.Pkg, fn),
})
}
sort.Sort(pcgNodesByPretty(nodes[1:]))
for i, n := range nodes {
pcg.nodeIndex[n.fn] = i
}
pcg.nodes = nodes
}
func (pcg *packageCallGraph) addEdge(caller, callee *ssa.Function) {
var callerIndex int
if caller.Pkg == callee.Pkg {
// intra-package edge
callerIndex = pcg.nodeIndex[caller]
if callerIndex < 1 {
panic(caller)
}
}
edges := &pcg.nodes[callerIndex].edges
edges.SetBit(edges, pcg.nodeIndex[callee], 1)
}
func (a *analysis) pcgAddNode(fn *ssa.Function) {
if fn.Pkg == nil {
return
}
pcg, ok := a.pcgs[fn.Pkg]
if !ok {
pcg = &packageCallGraph{nodeIndex: make(map[*ssa.Function]int)}
a.pcgs[fn.Pkg] = pcg
}
pcg.nodeIndex[fn] = -1
}
func (a *analysis) pcgAddEdge(caller, callee *ssa.Function) {
if callee.Pkg != nil {
a.pcgs[callee.Pkg].addEdge(caller, callee)
}
}
// pcgJSON returns a new slice of callgraph JSON values.
func (a *analysis) pcgJSON(pcg *packageCallGraph) []*PCGNodeJSON {
var nodes []*PCGNodeJSON
for _, n := range pcg.nodes {
// TODO(adonovan): why is there no good way to iterate
// over the set bits of a big.Int?
var callees []int
nbits := n.edges.BitLen()
for j := 0; j < nbits; j++ {
if n.edges.Bit(j) == 1 {
callees = append(callees, j)
}
}
var pos token.Pos
if n.fn != nil {
pos = funcToken(n.fn)
}
nodes = append(nodes, &PCGNodeJSON{
Func: anchorJSON{
Text: n.pretty,
Href: a.posURL(pos, len("func")),
},
Callees: callees,
})
}
return nodes
}
type pcgNodesByPretty []*pcgNode
func (a pcgNodesByPretty) Less(i, j int) bool { return a[i].pretty < a[j].pretty }
func (a pcgNodesByPretty) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a pcgNodesByPretty) Len() int { return len(a) }

195
vendor/golang.org/x/tools/godoc/analysis/implements.go generated vendored Normal file
View File

@@ -0,0 +1,195 @@
// Copyright 2014 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 analysis
// This file computes the "implements" relation over all pairs of
// named types in the program. (The mark-up is done by typeinfo.go.)
// TODO(adonovan): do we want to report implements(C, I) where C and I
// belong to different packages and at least one is not exported?
import (
"go/types"
"sort"
"golang.org/x/tools/go/types/typeutil"
)
// computeImplements computes the "implements" relation over all pairs
// of named types in allNamed.
func computeImplements(cache *typeutil.MethodSetCache, allNamed []*types.Named) map[*types.Named]implementsFacts {
// Information about a single type's method set.
type msetInfo struct {
typ types.Type
mset *types.MethodSet
mask1, mask2 uint64
}
initMsetInfo := func(info *msetInfo, typ types.Type) {
info.typ = typ
info.mset = cache.MethodSet(typ)
for i := 0; i < info.mset.Len(); i++ {
name := info.mset.At(i).Obj().Name()
info.mask1 |= 1 << methodBit(name[0])
info.mask2 |= 1 << methodBit(name[len(name)-1])
}
}
// satisfies(T, U) reports whether type T satisfies type U.
// U must be an interface.
//
// Since there are thousands of types (and thus millions of
// pairs of types) and types.Assignable(T, U) is relatively
// expensive, we compute assignability directly from the
// method sets. (At least one of T and U must be an
// interface.)
//
// We use a trick (thanks gri!) related to a Bloom filter to
// quickly reject most tests, which are false. For each
// method set, we precompute a mask, a set of bits, one per
// distinct initial byte of each method name. Thus the mask
// for io.ReadWriter would be {'R','W'}. AssignableTo(T, U)
// cannot be true unless mask(T)&mask(U)==mask(U).
//
// As with a Bloom filter, we can improve precision by testing
// additional hashes, e.g. using the last letter of each
// method name, so long as the subset mask property holds.
//
// When analyzing the standard library, there are about 1e6
// calls to satisfies(), of which 0.6% return true. With a
// 1-hash filter, 95% of calls avoid the expensive check; with
// a 2-hash filter, this grows to 98.2%.
satisfies := func(T, U *msetInfo) bool {
return T.mask1&U.mask1 == U.mask1 &&
T.mask2&U.mask2 == U.mask2 &&
containsAllIdsOf(T.mset, U.mset)
}
// Information about a named type N, and perhaps also *N.
type namedInfo struct {
isInterface bool
base msetInfo // N
ptr msetInfo // *N, iff N !isInterface
}
var infos []namedInfo
// Precompute the method sets and their masks.
for _, N := range allNamed {
var info namedInfo
initMsetInfo(&info.base, N)
_, info.isInterface = N.Underlying().(*types.Interface)
if !info.isInterface {
initMsetInfo(&info.ptr, types.NewPointer(N))
}
if info.base.mask1|info.ptr.mask1 == 0 {
continue // neither N nor *N has methods
}
infos = append(infos, info)
}
facts := make(map[*types.Named]implementsFacts)
// Test all pairs of distinct named types (T, U).
// TODO(adonovan): opt: compute (U, T) at the same time.
for t := range infos {
T := &infos[t]
var to, from, fromPtr []types.Type
for u := range infos {
if t == u {
continue
}
U := &infos[u]
switch {
case T.isInterface && U.isInterface:
if satisfies(&U.base, &T.base) {
to = append(to, U.base.typ)
}
if satisfies(&T.base, &U.base) {
from = append(from, U.base.typ)
}
case T.isInterface: // U concrete
if satisfies(&U.base, &T.base) {
to = append(to, U.base.typ)
} else if satisfies(&U.ptr, &T.base) {
to = append(to, U.ptr.typ)
}
case U.isInterface: // T concrete
if satisfies(&T.base, &U.base) {
from = append(from, U.base.typ)
} else if satisfies(&T.ptr, &U.base) {
fromPtr = append(fromPtr, U.base.typ)
}
}
}
// Sort types (arbitrarily) to avoid nondeterminism.
sort.Sort(typesByString(to))
sort.Sort(typesByString(from))
sort.Sort(typesByString(fromPtr))
facts[T.base.typ.(*types.Named)] = implementsFacts{to, from, fromPtr}
}
return facts
}
type implementsFacts struct {
to []types.Type // named or ptr-to-named types assignable to interface T
from []types.Type // named interfaces assignable from T
fromPtr []types.Type // named interfaces assignable only from *T
}
type typesByString []types.Type
func (p typesByString) Len() int { return len(p) }
func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// methodBit returns the index of x in [a-zA-Z], or 52 if not found.
func methodBit(x byte) uint64 {
switch {
case 'a' <= x && x <= 'z':
return uint64(x - 'a')
case 'A' <= x && x <= 'Z':
return uint64(26 + x - 'A')
}
return 52 // all other bytes
}
// containsAllIdsOf reports whether the method identifiers of T are a
// superset of those in U. If U belongs to an interface type, the
// result is equal to types.Assignable(T, U), but is cheaper to compute.
//
// TODO(gri): make this a method of *types.MethodSet.
//
func containsAllIdsOf(T, U *types.MethodSet) bool {
t, tlen := 0, T.Len()
u, ulen := 0, U.Len()
for t < tlen && u < ulen {
tMeth := T.At(t).Obj()
uMeth := U.At(u).Obj()
tId := tMeth.Id()
uId := uMeth.Id()
if tId > uId {
// U has a method T lacks: fail.
return false
}
if tId < uId {
// T has a method U lacks: ignore it.
t++
continue
}
// U and T both have a method of this Id. Check types.
if !types.Identical(tMeth.Type(), uMeth.Type()) {
return false // type mismatch
}
u++
t++
}
return u == ulen
}

69
vendor/golang.org/x/tools/godoc/analysis/json.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
// Copyright 2014 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 analysis
// This file defines types used by client-side JavaScript.
type anchorJSON struct {
Text string // HTML
Href string // URL
}
type commOpJSON struct {
Op anchorJSON
Fn string
}
// JavaScript's onClickComm() expects a commJSON.
type commJSON struct {
Ops []commOpJSON
}
// Indicates one of these forms of fact about a type T:
// T "is implemented by <ByKind> type <Other>" (ByKind != "", e.g. "array")
// T "implements <Other>" (ByKind == "")
type implFactJSON struct {
ByKind string `json:",omitempty"`
Other anchorJSON
}
// Implements facts are grouped by form, for ease of reading.
type implGroupJSON struct {
Descr string
Facts []implFactJSON
}
// JavaScript's onClickIdent() expects a TypeInfoJSON.
type TypeInfoJSON struct {
Name string // type name
Size, Align int64
Methods []anchorJSON
ImplGroups []implGroupJSON
}
// JavaScript's onClickCallees() expects a calleesJSON.
type calleesJSON struct {
Descr string
Callees []anchorJSON // markup for called function
}
type callerJSON struct {
Func string
Sites []anchorJSON
}
// JavaScript's onClickCallers() expects a callersJSON.
type callersJSON struct {
Callee string
Callers []callerJSON
}
// JavaScript's cgAddChild requires a global array of PCGNodeJSON
// called CALLGRAPH, representing the intra-package call graph.
// The first element is special and represents "all external callers".
type PCGNodeJSON struct {
Func anchorJSON
Callees []int // indices within CALLGRAPH of nodes called by this one
}

154
vendor/golang.org/x/tools/godoc/analysis/peers.go generated vendored Normal file
View File

@@ -0,0 +1,154 @@
// Copyright 2014 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 analysis
// This file computes the channel "peers" relation over all pairs of
// channel operations in the program. The peers are displayed in the
// lower pane when a channel operation (make, <-, close) is clicked.
// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
// then enable reflection in PTA.
import (
"fmt"
"go/token"
"go/types"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
)
func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
addSendRecv := func(j *commJSON, op chanOp) {
j.Ops = append(j.Ops, commOpJSON{
Op: anchorJSON{
Text: op.mode,
Href: a.posURL(op.pos, op.len),
},
Fn: prettyFunc(nil, op.fn),
})
}
// Build an undirected bipartite multigraph (binary relation)
// of MakeChan ops and send/recv/close ops.
//
// TODO(adonovan): opt: use channel element types to partition
// the O(n^2) problem into subproblems.
aliasedOps := make(map[*ssa.MakeChan][]chanOp)
opToMakes := make(map[chanOp][]*ssa.MakeChan)
for _, op := range a.ops {
// Combine the PT sets from all contexts.
var makes []*ssa.MakeChan // aliased ops
ptr, ok := ptsets[op.ch]
if !ok {
continue // e.g. channel op in dead code
}
for _, label := range ptr.PointsTo().Labels() {
makechan, ok := label.Value().(*ssa.MakeChan)
if !ok {
continue // skip intrinsically-created channels for now
}
if makechan.Pos() == token.NoPos {
continue // not possible?
}
makes = append(makes, makechan)
aliasedOps[makechan] = append(aliasedOps[makechan], op)
}
opToMakes[op] = makes
}
// Now that complete relation is built, build links for ops.
for _, op := range a.ops {
v := commJSON{
Ops: []commOpJSON{}, // (JS wants non-nil)
}
ops := make(map[chanOp]bool)
for _, makechan := range opToMakes[op] {
v.Ops = append(v.Ops, commOpJSON{
Op: anchorJSON{
Text: "made",
Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
len("make")),
},
Fn: makechan.Parent().RelString(op.fn.Package().Pkg),
})
for _, op := range aliasedOps[makechan] {
ops[op] = true
}
}
for op := range ops {
addSendRecv(&v, op)
}
// Add links for each aliased op.
fi, offset := a.fileAndOffset(op.pos)
fi.addLink(aLink{
start: offset,
end: offset + op.len,
title: "show channel ops",
onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
})
}
// Add links for makechan ops themselves.
for makechan, ops := range aliasedOps {
v := commJSON{
Ops: []commOpJSON{}, // (JS wants non-nil)
}
for _, op := range ops {
addSendRecv(&v, op)
}
fi, offset := a.fileAndOffset(makechan.Pos())
fi.addLink(aLink{
start: offset - len("make"),
end: offset,
title: "show channel ops",
onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
})
}
}
// -- utilities --------------------------------------------------------
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
// Derived from cmd/guru/peers.go.
type chanOp struct {
ch ssa.Value
mode string // sent|received|closed
pos token.Pos
len int
fn *ssa.Function
}
// chanOps returns a slice of all the channel operations in the instruction.
// Derived from cmd/guru/peers.go.
func chanOps(instr ssa.Instruction) []chanOp {
fn := instr.Parent()
var ops []chanOp
switch instr := instr.(type) {
case *ssa.UnOp:
if instr.Op == token.ARROW {
// TODO(adonovan): don't assume <-ch; could be 'range ch'.
ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
}
case *ssa.Send:
ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
case *ssa.Select:
for _, st := range instr.States {
mode := "received"
if st.Dir == types.SendOnly {
mode = "sent"
}
ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
}
case ssa.CallInstruction:
call := instr.Common()
if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
pos := instr.Common().Pos()
ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
}
}
return ops
}

234
vendor/golang.org/x/tools/godoc/analysis/typeinfo.go generated vendored Normal file
View File

@@ -0,0 +1,234 @@
// Copyright 2014 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 analysis
// This file computes the markup for information from go/types:
// IMPORTS, identifier RESOLUTION, METHOD SETS, size/alignment, and
// the IMPLEMENTS relation.
//
// IMPORTS links connect import specs to the documentation for the
// imported package.
//
// RESOLUTION links referring identifiers to their defining
// identifier, and adds tooltips for kind and type.
//
// METHOD SETS, size/alignment, and the IMPLEMENTS relation are
// displayed in the lower pane when a type's defining identifier is
// clicked.
import (
"fmt"
"go/types"
"reflect"
"strconv"
"strings"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types/typeutil"
)
// TODO(adonovan): audit to make sure it's safe on ill-typed packages.
// TODO(adonovan): use same Sizes as loader.Config.
var sizes = types.StdSizes{WordSize: 8, MaxAlign: 8}
func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Named]implementsFacts) {
// We must not assume the corresponding SSA packages were
// created (i.e. were transitively error-free).
// IMPORTS
for _, f := range info.Files {
// Package decl.
fi, offset := a.fileAndOffset(f.Name.Pos())
fi.addLink(aLink{
start: offset,
end: offset + len(f.Name.Name),
title: "Package docs for " + info.Pkg.Path(),
// TODO(adonovan): fix: we're putting the untrusted Path()
// into a trusted field. What's the appropriate sanitizer?
href: "/pkg/" + info.Pkg.Path(),
})
// Import specs.
for _, imp := range f.Imports {
// Remove quotes.
L := int(imp.End()-imp.Path.Pos()) - len(`""`)
path, _ := strconv.Unquote(imp.Path.Value)
fi, offset := a.fileAndOffset(imp.Path.Pos())
fi.addLink(aLink{
start: offset + 1,
end: offset + 1 + L,
title: "Package docs for " + path,
// TODO(adonovan): fix: we're putting the untrusted path
// into a trusted field. What's the appropriate sanitizer?
href: "/pkg/" + path,
})
}
}
// RESOLUTION
qualifier := types.RelativeTo(info.Pkg)
for id, obj := range info.Uses {
// Position of the object definition.
pos := obj.Pos()
Len := len(obj.Name())
// Correct the position for non-renaming import specs.
// import "sync/atomic"
// ^^^^^^^^^^^
if obj, ok := obj.(*types.PkgName); ok && id.Name == obj.Imported().Name() {
// Assume this is a non-renaming import.
// NB: not true for degenerate renamings: `import foo "foo"`.
pos++
Len = len(obj.Imported().Path())
}
if obj.Pkg() == nil {
continue // don't mark up built-ins.
}
fi, offset := a.fileAndOffset(id.NamePos)
fi.addLink(aLink{
start: offset,
end: offset + len(id.Name),
title: types.ObjectString(obj, qualifier),
href: a.posURL(pos, Len),
})
}
// IMPLEMENTS & METHOD SETS
for _, obj := range info.Defs {
if obj, ok := obj.(*types.TypeName); ok {
if named, ok := obj.Type().(*types.Named); ok {
a.namedType(named, implements)
}
}
}
}
func (a *analysis) namedType(T *types.Named, implements map[*types.Named]implementsFacts) {
obj := T.Obj()
qualifier := types.RelativeTo(obj.Pkg())
v := &TypeInfoJSON{
Name: obj.Name(),
Size: sizes.Sizeof(T),
Align: sizes.Alignof(T),
Methods: []anchorJSON{}, // (JS wants non-nil)
}
// addFact adds the fact "is implemented by T" (by) or
// "implements T" (!by) to group.
addFact := func(group *implGroupJSON, T types.Type, by bool) {
Tobj := deref(T).(*types.Named).Obj()
var byKind string
if by {
// Show underlying kind of implementing type,
// e.g. "slice", "array", "struct".
s := reflect.TypeOf(T.Underlying()).String()
byKind = strings.ToLower(strings.TrimPrefix(s, "*types."))
}
group.Facts = append(group.Facts, implFactJSON{
ByKind: byKind,
Other: anchorJSON{
Href: a.posURL(Tobj.Pos(), len(Tobj.Name())),
Text: types.TypeString(T, qualifier),
},
})
}
// IMPLEMENTS
if r, ok := implements[T]; ok {
if isInterface(T) {
// "T is implemented by <conc>" ...
// "T is implemented by <iface>"...
// "T implements <iface>"...
group := implGroupJSON{
Descr: types.TypeString(T, qualifier),
}
// Show concrete types first; use two passes.
for _, sub := range r.to {
if !isInterface(sub) {
addFact(&group, sub, true)
}
}
for _, sub := range r.to {
if isInterface(sub) {
addFact(&group, sub, true)
}
}
for _, super := range r.from {
addFact(&group, super, false)
}
v.ImplGroups = append(v.ImplGroups, group)
} else {
// T is concrete.
if r.from != nil {
// "T implements <iface>"...
group := implGroupJSON{
Descr: types.TypeString(T, qualifier),
}
for _, super := range r.from {
addFact(&group, super, false)
}
v.ImplGroups = append(v.ImplGroups, group)
}
if r.fromPtr != nil {
// "*C implements <iface>"...
group := implGroupJSON{
Descr: "*" + types.TypeString(T, qualifier),
}
for _, psuper := range r.fromPtr {
addFact(&group, psuper, false)
}
v.ImplGroups = append(v.ImplGroups, group)
}
}
}
// METHOD SETS
for _, sel := range typeutil.IntuitiveMethodSet(T, &a.prog.MethodSets) {
meth := sel.Obj().(*types.Func)
pos := meth.Pos() // may be 0 for error.Error
v.Methods = append(v.Methods, anchorJSON{
Href: a.posURL(pos, len(meth.Name())),
Text: types.SelectionString(sel, qualifier),
})
}
// Since there can be many specs per decl, we
// can't attach the link to the keyword 'type'
// (as we do with 'func'); we use the Ident.
fi, offset := a.fileAndOffset(obj.Pos())
fi.addLink(aLink{
start: offset,
end: offset + len(obj.Name()),
title: fmt.Sprintf("type info for %s", obj.Name()),
onclick: fmt.Sprintf("onClickTypeInfo(%d)", fi.addData(v)),
})
// Add info for exported package-level types to the package info.
if obj.Exported() && isPackageLevel(obj) {
// TODO(adonovan): Path is not unique!
// It is possible to declare a non-test package called x_test.
a.result.pkgInfo(obj.Pkg().Path()).addType(v)
}
}
// -- utilities --------------------------------------------------------
func isInterface(T types.Type) bool { return types.IsInterface(T) }
// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
if p, ok := typ.Underlying().(*types.Pointer); ok {
return p.Elem()
}
return typ
}
// isPackageLevel reports whether obj is a package-level object.
func isPackageLevel(obj types.Object) bool {
return obj.Pkg().Scope().Lookup(obj.Name()) == obj
}

13
vendor/golang.org/x/tools/godoc/appengine.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
// Copyright 2015 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.
// +build appengine
package godoc
import "google.golang.org/appengine"
func init() {
onAppengine = !appengine.IsDevAppServer()
}

217
vendor/golang.org/x/tools/godoc/cmdline.go generated vendored Normal file
View File

@@ -0,0 +1,217 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
import (
"fmt"
"go/ast"
"go/build"
"io"
"log"
"os"
pathpkg "path"
"path/filepath"
"regexp"
"strings"
"golang.org/x/tools/godoc/vfs"
)
const (
target = "/target"
cmdPrefix = "cmd/"
srcPrefix = "src/"
toolsPath = "golang.org/x/tools/cmd/"
)
// CommandLine returns godoc results to w.
// Note that it may add a /target path to fs.
func CommandLine(w io.Writer, fs vfs.NameSpace, pres *Presentation, args []string) error {
path := args[0]
srcMode := pres.SrcMode
cmdMode := strings.HasPrefix(path, cmdPrefix)
if strings.HasPrefix(path, srcPrefix) {
path = strings.TrimPrefix(path, srcPrefix)
srcMode = true
}
var abspath, relpath string
if cmdMode {
path = strings.TrimPrefix(path, cmdPrefix)
} else {
abspath, relpath = paths(fs, pres, path)
}
var mode PageInfoMode
if relpath == builtinPkgPath {
// the fake built-in package contains unexported identifiers
mode = NoFiltering | NoTypeAssoc
}
if pres.AllMode {
mode |= NoFiltering
}
if srcMode {
// only filter exports if we don't have explicit command-line filter arguments
if len(args) > 1 {
mode |= NoFiltering
}
mode |= ShowSource
}
// First, try as package unless forced as command.
var info *PageInfo
if !cmdMode {
info = pres.GetPkgPageInfo(abspath, relpath, mode)
}
// Second, try as command (if the path is not absolute).
var cinfo *PageInfo
if !filepath.IsAbs(path) {
// First try go.tools/cmd.
abspath = pathpkg.Join(pres.PkgFSRoot(), toolsPath+path)
cinfo = pres.GetCmdPageInfo(abspath, relpath, mode)
if cinfo.IsEmpty() {
// Then try $GOROOT/src/cmd.
abspath = pathpkg.Join(pres.CmdFSRoot(), cmdPrefix, path)
cinfo = pres.GetCmdPageInfo(abspath, relpath, mode)
}
}
// determine what to use
if info == nil || info.IsEmpty() {
if cinfo != nil && !cinfo.IsEmpty() {
// only cinfo exists - switch to cinfo
info = cinfo
}
} else if cinfo != nil && !cinfo.IsEmpty() {
// both info and cinfo exist - use cinfo if info
// contains only subdirectory information
if info.PAst == nil && info.PDoc == nil {
info = cinfo
} else if relpath != target {
// The above check handles the case where an operating system path
// is provided (see documentation for paths below). In that case,
// relpath is set to "/target" (in anticipation of accessing packages there),
// and is therefore not expected to match a command.
fmt.Fprintf(w, "use 'godoc %s%s' for documentation on the %s command \n\n", cmdPrefix, relpath, relpath)
}
}
if info == nil {
return fmt.Errorf("%s: no such directory or package", args[0])
}
if info.Err != nil {
return info.Err
}
if info.PDoc != nil && info.PDoc.ImportPath == target {
// Replace virtual /target with actual argument from command line.
info.PDoc.ImportPath = args[0]
}
// If we have more than one argument, use the remaining arguments for filtering.
if len(args) > 1 {
info.IsFiltered = true
filterInfo(args[1:], info)
}
packageText := pres.PackageText
if pres.HTMLMode {
packageText = pres.PackageHTML
}
if err := packageText.Execute(w, info); err != nil {
return err
}
return nil
}
// paths determines the paths to use.
//
// If we are passed an operating system path like . or ./foo or /foo/bar or c:\mysrc,
// we need to map that path somewhere in the fs name space so that routines
// like getPageInfo will see it. We use the arbitrarily-chosen virtual path "/target"
// for this. That is, if we get passed a directory like the above, we map that
// directory so that getPageInfo sees it as /target.
// Returns the absolute and relative paths.
func paths(fs vfs.NameSpace, pres *Presentation, path string) (abspath, relpath string) {
if filepath.IsAbs(path) {
fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace)
return target, target
}
if build.IsLocalImport(path) {
cwd, err := os.Getwd()
if err != nil {
log.Printf("error while getting working directory: %v", err)
}
path = filepath.Join(cwd, path)
fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace)
return target, target
}
bp, err := build.Import(path, "", build.FindOnly)
if err != nil {
log.Printf("error while importing build package: %v", err)
}
if bp.Dir != "" && bp.ImportPath != "" {
fs.Bind(target, vfs.OS(bp.Dir), "/", vfs.BindReplace)
return target, bp.ImportPath
}
return pathpkg.Join(pres.PkgFSRoot(), path), path
}
// filterInfo updates info to include only the nodes that match the given
// filter args.
func filterInfo(args []string, info *PageInfo) {
rx, err := makeRx(args)
if err != nil {
log.Fatalf("illegal regular expression from %v: %v", args, err)
}
filter := func(s string) bool { return rx.MatchString(s) }
switch {
case info.PAst != nil:
newPAst := map[string]*ast.File{}
for name, a := range info.PAst {
cmap := ast.NewCommentMap(info.FSet, a, a.Comments)
a.Comments = []*ast.CommentGroup{} // remove all comments.
ast.FilterFile(a, filter)
if len(a.Decls) > 0 {
newPAst[name] = a
}
for _, d := range a.Decls {
// add back the comments associated with d only
comments := cmap.Filter(d).Comments()
a.Comments = append(a.Comments, comments...)
}
}
info.PAst = newPAst // add only matching files.
case info.PDoc != nil:
info.PDoc.Filter(filter)
}
}
// Does s look like a regular expression?
func isRegexp(s string) bool {
return strings.ContainsAny(s, ".(|)*+?^$[]")
}
// Make a regular expression of the form
// names[0]|names[1]|...names[len(names)-1].
// Returns an error if the regular expression is illegal.
func makeRx(names []string) (*regexp.Regexp, error) {
if len(names) == 0 {
return nil, fmt.Errorf("no expression provided")
}
s := ""
for i, name := range names {
if i > 0 {
s += "|"
}
if isRegexp(name) {
s += name
} else {
s += "^" + name + "$" // must match exactly
}
}
return regexp.Compile(s)
}

312
vendor/golang.org/x/tools/godoc/cmdline_test.go generated vendored Normal file
View File

@@ -0,0 +1,312 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
import (
"bytes"
"go/build"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"testing"
"text/template"
"golang.org/x/tools/godoc/vfs"
"golang.org/x/tools/godoc/vfs/mapfs"
)
// setupGoroot creates temporary directory to act as GOROOT when running tests
// that depend upon the build package. It updates build.Default to point to the
// new GOROOT.
// It returns a function that can be called to reset build.Default and remove
// the temporary directory.
func setupGoroot(t *testing.T) (cleanup func()) {
var stdLib = map[string]string{
"src/fmt/fmt.go": `// Package fmt implements formatted I/O.
package fmt
type Stringer interface {
String() string
}
`,
}
goroot, err := ioutil.TempDir("", "cmdline_test")
if err != nil {
t.Fatal(err)
}
origContext := build.Default
build.Default = build.Context{
GOROOT: goroot,
Compiler: "gc",
}
for relname, contents := range stdLib {
name := filepath.Join(goroot, relname)
if err := os.MkdirAll(filepath.Dir(name), 0770); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(name, []byte(contents), 0770); err != nil {
t.Fatal(err)
}
}
return func() {
if err := os.RemoveAll(goroot); err != nil {
t.Log(err)
}
build.Default = origContext
}
}
func TestPaths(t *testing.T) {
cleanup := setupGoroot(t)
defer cleanup()
pres := &Presentation{
pkgHandler: handlerServer{
fsRoot: "/fsroot",
},
}
fs := make(vfs.NameSpace)
absPath := "/foo/fmt"
if runtime.GOOS == "windows" {
absPath = `c:\foo\fmt`
}
for _, tc := range []struct {
desc string
path string
expAbs string
expRel string
}{
{
"Absolute path",
absPath,
"/target",
"/target",
},
{
"Local import",
"../foo/fmt",
"/target",
"/target",
},
{
"Import",
"fmt",
"/target",
"fmt",
},
{
"Default",
"unknownpkg",
"/fsroot/unknownpkg",
"unknownpkg",
},
} {
abs, rel := paths(fs, pres, tc.path)
if abs != tc.expAbs || rel != tc.expRel {
t.Errorf("%s: paths(%q) = %s,%s; want %s,%s", tc.desc, tc.path, abs, rel, tc.expAbs, tc.expRel)
}
}
}
func TestMakeRx(t *testing.T) {
for _, tc := range []struct {
desc string
names []string
exp string
}{
{
desc: "empty string",
names: []string{""},
exp: `^$`,
},
{
desc: "simple text",
names: []string{"a"},
exp: `^a$`,
},
{
desc: "two words",
names: []string{"foo", "bar"},
exp: `^foo$|^bar$`,
},
{
desc: "word & non-trivial",
names: []string{"foo", `ab?c`},
exp: `^foo$|ab?c`,
},
{
desc: "bad regexp",
names: []string{`(."`},
exp: `(."`,
},
} {
expRE, expErr := regexp.Compile(tc.exp)
if re, err := makeRx(tc.names); !reflect.DeepEqual(err, expErr) && !reflect.DeepEqual(re, expRE) {
t.Errorf("%s: makeRx(%v) = %q,%q; want %q,%q", tc.desc, tc.names, re, err, expRE, expErr)
}
}
}
func TestCommandLine(t *testing.T) {
cleanup := setupGoroot(t)
defer cleanup()
mfs := mapfs.New(map[string]string{
"src/bar/bar.go": `// Package bar is an example.
package bar
`,
"src/foo/foo.go": `// Package foo.
package foo
// First function is first.
func First() {
}
// Second function is second.
func Second() {
}
// unexported function is third.
func unexported() {
}
`,
"src/gen/gen.go": `// Package gen
package gen
//line notgen.go:3
// F doc //line 1 should appear
// line 2 should appear
func F()
//line foo.go:100`, // no newline on end to check corner cases!
"src/vet/vet.go": `// Package vet
package vet
`,
"src/cmd/go/doc.go": `// The go command
package main
`,
"src/cmd/gofmt/doc.go": `// The gofmt command
package main
`,
"src/cmd/vet/vet.go": `// The vet command
package main
`,
})
fs := make(vfs.NameSpace)
fs.Bind("/", mfs, "/", vfs.BindReplace)
c := NewCorpus(fs)
p := &Presentation{Corpus: c}
p.cmdHandler = handlerServer{
p: p,
c: c,
pattern: "/cmd/",
fsRoot: "/src",
}
p.pkgHandler = handlerServer{
p: p,
c: c,
pattern: "/pkg/",
fsRoot: "/src",
exclude: []string{"/src/cmd"},
}
p.initFuncMap()
p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{$info := .}}{{$filtered := .IsFiltered}}{{if $filtered}}{{range .PAst}}{{range .Decls}}{{node $info .}}{{end}}{{end}}{{else}}{{with .PAst}}{{range $filename, $ast := .}}{{$filename}}:
{{node $ $ast}}{{end}}{{end}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{with .Funcs}}
{{range .}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}{{end}}{{end}}{{end}}`))
for _, tc := range []struct {
desc string
args []string
all bool
exp string
err bool
}{
{
desc: "standard package",
args: []string{"fmt"},
exp: "PACKAGE Package fmt implements formatted I/O.\n",
},
{
desc: "package",
args: []string{"bar"},
exp: "PACKAGE Package bar is an example.\n",
},
{
desc: "package w. filter",
args: []string{"foo", "First"},
exp: "PACKAGE \nfunc First()\n First function is first.\n",
},
{
desc: "package w. bad filter",
args: []string{"foo", "DNE"},
exp: "PACKAGE ",
},
{
desc: "source mode",
args: []string{"src/bar"},
exp: "bar/bar.go:\n// Package bar is an example.\npackage bar\n",
},
{
desc: "source mode w. filter",
args: []string{"src/foo", "Second"},
exp: "// Second function is second.\nfunc Second() {\n}",
},
{
desc: "package w. unexported filter",
args: []string{"foo", "unexported"},
all: true,
exp: "PACKAGE \nfunc unexported()\n unexported function is third.\n",
},
{
desc: "package w. unexported filter",
args: []string{"foo", "unexported"},
all: false,
exp: "PACKAGE ",
},
{
desc: "package w. //line comments",
args: []string{"gen", "F"},
exp: "PACKAGE \nfunc F()\n F doc //line 1 should appear line 2 should appear\n",
},
{
desc: "command",
args: []string{"go"},
exp: "COMMAND The go command\n",
},
{
desc: "forced command",
args: []string{"cmd/gofmt"},
exp: "COMMAND The gofmt command\n",
},
{
desc: "bad arg",
args: []string{"doesnotexist"},
err: true,
},
{
desc: "both command and package",
args: []string{"vet"},
exp: "use 'godoc cmd/vet' for documentation on the vet command \n\nPACKAGE Package vet\n",
},
{
desc: "root directory",
args: []string{"/"},
exp: "",
},
} {
p.AllMode = tc.all
w := new(bytes.Buffer)
err := CommandLine(w, fs, p, tc.args)
if got, want := w.String(), tc.exp; got != want || tc.err == (err == nil) {
t.Errorf("%s: CommandLine(%v), All(%v) = %q (%v); want %q (%v)",
tc.desc, tc.args, tc.all, got, err, want, tc.err)
}
}
}

164
vendor/golang.org/x/tools/godoc/corpus.go generated vendored Normal file
View File

@@ -0,0 +1,164 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
import (
"errors"
pathpkg "path"
"sync"
"time"
"golang.org/x/tools/godoc/analysis"
"golang.org/x/tools/godoc/util"
"golang.org/x/tools/godoc/vfs"
)
// A Corpus holds all the state related to serving and indexing a
// collection of Go code.
//
// Construct a new Corpus with NewCorpus, then modify options,
// then call its Init method.
type Corpus struct {
fs vfs.FileSystem
// Verbose logging.
Verbose bool
// IndexEnabled controls whether indexing is enabled.
IndexEnabled bool
// IndexFiles specifies a glob pattern specifying index files.
// If not empty, the index is read from these files in sorted
// order.
IndexFiles string
// IndexThrottle specifies the indexing throttle value
// between 0.0 and 1.0. At 0.0, the indexer always sleeps.
// At 1.0, the indexer never sleeps. Because 0.0 is useless
// and redundant with setting IndexEnabled to false, the
// zero value for IndexThrottle means 0.9.
IndexThrottle float64
// IndexInterval specifies the time to sleep between reindexing
// all the sources.
// If zero, a default is used. If negative, the index is only
// built once.
IndexInterval time.Duration
// IndexDocs enables indexing of Go documentation.
// This will produce search results for exported types, functions,
// methods, variables, and constants, and will link to the godoc
// documentation for those identifiers.
IndexDocs bool
// IndexGoCode enables indexing of Go source code.
// This will produce search results for internal and external identifiers
// and will link to both declarations and uses of those identifiers in
// source code.
IndexGoCode bool
// IndexFullText enables full-text indexing.
// This will provide search results for any matching text in any file that
// is indexed, including non-Go files (see whitelisted in index.go).
// Regexp searching is supported via full-text indexing.
IndexFullText bool
// MaxResults optionally specifies the maximum results for indexing.
MaxResults int
// SummarizePackage optionally specifies a function to
// summarize a package. It exists as an optimization to
// avoid reading files to parse package comments.
//
// If SummarizePackage returns false for ok, the caller
// ignores all return values and parses the files in the package
// as if SummarizePackage were nil.
//
// If showList is false, the package is hidden from the
// package listing.
SummarizePackage func(pkg string) (summary string, showList, ok bool)
// IndexDirectory optionally specifies a function to determine
// whether the provided directory should be indexed. The dir
// will be of the form "/src/cmd/6a", "/doc/play",
// "/src/io", etc.
// If nil, all directories are indexed if indexing is enabled.
IndexDirectory func(dir string) bool
testDir string // TODO(bradfitz,adg): migrate old godoc flag? looks unused.
// Send a value on this channel to trigger a metadata refresh.
// It is buffered so that if a signal is not lost if sent
// during a refresh.
refreshMetadataSignal chan bool
// file system information
fsTree util.RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now)
fsModified util.RWValue // timestamp of last call to invalidateIndex
docMetadata util.RWValue // mapping from paths to *Metadata
// SearchIndex is the search index in use.
searchIndex util.RWValue
// Analysis is the result of type and pointer analysis.
Analysis analysis.Result
// flag to check whether a corpus is initialized or not
initMu sync.RWMutex
initDone bool
}
// NewCorpus returns a new Corpus from a filesystem.
// The returned corpus has all indexing enabled and MaxResults set to 1000.
// Change or set any options on Corpus before calling the Corpus.Init method.
func NewCorpus(fs vfs.FileSystem) *Corpus {
c := &Corpus{
fs: fs,
refreshMetadataSignal: make(chan bool, 1),
MaxResults: 1000,
IndexEnabled: true,
IndexDocs: true,
IndexGoCode: true,
IndexFullText: true,
}
return c
}
func (c *Corpus) CurrentIndex() (*Index, time.Time) {
v, t := c.searchIndex.Get()
idx, _ := v.(*Index)
return idx, t
}
func (c *Corpus) FSModifiedTime() time.Time {
_, ts := c.fsModified.Get()
return ts
}
// Init initializes Corpus, once options on Corpus are set.
// It must be called before any subsequent method calls.
func (c *Corpus) Init() error {
if err := c.initFSTree(); err != nil {
return err
}
c.updateMetadata()
go c.refreshMetadataLoop()
c.initMu.Lock()
c.initDone = true
c.initMu.Unlock()
return nil
}
func (c *Corpus) initFSTree() error {
dir := c.newDirectory(pathpkg.Join("/", c.testDir), -1)
if dir == nil {
return errors.New("godoc: corpus fstree is nil")
}
c.fsTree.Set(dir)
c.invalidateIndex()
return nil
}

383
vendor/golang.org/x/tools/godoc/dirtrees.go generated vendored Normal file
View File

@@ -0,0 +1,383 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains the code dealing with package directory trees.
package godoc
import (
"go/doc"
"go/parser"
"go/token"
"log"
"os"
pathpkg "path"
"runtime"
"sort"
"strings"
"golang.org/x/tools/godoc/vfs"
)
// Conventional name for directories containing test data.
// Excluded from directory trees.
//
const testdataDirName = "testdata"
type Directory struct {
Depth int
Path string // directory path; includes Name
Name string // directory name
HasPkg bool // true if the directory contains at least one package
Synopsis string // package documentation, if any
RootType vfs.RootType // root type of the filesystem containing the directory
Dirs []*Directory // subdirectories
}
func isGoFile(fi os.FileInfo) bool {
name := fi.Name()
return !fi.IsDir() &&
len(name) > 0 && name[0] != '.' && // ignore .files
pathpkg.Ext(name) == ".go"
}
func isPkgFile(fi os.FileInfo) bool {
return isGoFile(fi) &&
!strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
}
func isPkgDir(fi os.FileInfo) bool {
name := fi.Name()
return fi.IsDir() && len(name) > 0 &&
name[0] != '_' && name[0] != '.' // ignore _files and .files
}
type treeBuilder struct {
c *Corpus
maxDepth int
}
// ioGate is a semaphore controlling VFS activity (ReadDir, parseFile, etc).
// Send before an operation and receive after.
var ioGate = make(chan struct{}, 20)
// workGate controls the number of concurrent workers. Too many concurrent
// workers and performance degrades and the race detector gets overwhelmed. If
// we cannot check out a concurrent worker, work is performed by the main thread
// instead of spinning up another goroutine.
var workGate = make(chan struct{}, runtime.NumCPU()*4)
func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory {
if name == testdataDirName {
return nil
}
if depth >= b.maxDepth {
// return a dummy directory so that the parent directory
// doesn't get discarded just because we reached the max
// directory depth
return &Directory{
Depth: depth,
Path: path,
Name: name,
}
}
var synopses [3]string // prioritized package documentation (0 == highest priority)
show := true // show in package listing
hasPkgFiles := false
haveSummary := false
if hook := b.c.SummarizePackage; hook != nil {
if summary, show0, ok := hook(strings.TrimPrefix(path, "/src/")); ok {
hasPkgFiles = true
show = show0
synopses[0] = summary
haveSummary = true
}
}
ioGate <- struct{}{}
list, err := b.c.fs.ReadDir(path)
<-ioGate
if err != nil {
// TODO: propagate more. See golang.org/issue/14252.
// For now:
if b.c.Verbose {
log.Printf("newDirTree reading %s: %v", path, err)
}
}
// determine number of subdirectories and if there are package files
var dirchs []chan *Directory
var dirs []*Directory
for _, d := range list {
filename := pathpkg.Join(path, d.Name())
switch {
case isPkgDir(d):
name := d.Name()
select {
case workGate <- struct{}{}:
ch := make(chan *Directory, 1)
dirchs = append(dirchs, ch)
go func() {
ch <- b.newDirTree(fset, filename, name, depth+1)
<-workGate
}()
default:
// no free workers, do work synchronously
dir := b.newDirTree(fset, filename, name, depth+1)
if dir != nil {
dirs = append(dirs, dir)
}
}
case !haveSummary && isPkgFile(d):
// looks like a package file, but may just be a file ending in ".go";
// don't just count it yet (otherwise we may end up with hasPkgFiles even
// though the directory doesn't contain any real package files - was bug)
// no "optimal" package synopsis yet; continue to collect synopses
ioGate <- struct{}{}
const flags = parser.ParseComments | parser.PackageClauseOnly
file, err := b.c.parseFile(fset, filename, flags)
<-ioGate
if err != nil {
if b.c.Verbose {
log.Printf("Error parsing %v: %v", filename, err)
}
break
}
hasPkgFiles = true
if file.Doc != nil {
// prioritize documentation
i := -1
switch file.Name.Name {
case name:
i = 0 // normal case: directory name matches package name
case "main":
i = 1 // directory contains a main package
default:
i = 2 // none of the above
}
if 0 <= i && i < len(synopses) && synopses[i] == "" {
synopses[i] = doc.Synopsis(file.Doc.Text())
}
}
haveSummary = synopses[0] != ""
}
}
// create subdirectory tree
for _, ch := range dirchs {
if d := <-ch; d != nil {
dirs = append(dirs, d)
}
}
// We need to sort the dirs slice because
// it is appended again after reading from dirchs.
sort.Slice(dirs, func(i, j int) bool {
return dirs[i].Name < dirs[j].Name
})
// if there are no package files and no subdirectories
// containing package files, ignore the directory
if !hasPkgFiles && len(dirs) == 0 {
return nil
}
// select the highest-priority synopsis for the directory entry, if any
synopsis := ""
for _, synopsis = range synopses {
if synopsis != "" {
break
}
}
return &Directory{
Depth: depth,
Path: path,
Name: name,
HasPkg: hasPkgFiles && show, // TODO(bradfitz): add proper Hide field?
Synopsis: synopsis,
RootType: b.c.fs.RootType(path),
Dirs: dirs,
}
}
// newDirectory creates a new package directory tree with at most maxDepth
// levels, anchored at root. The result tree is pruned such that it only
// contains directories that contain package files or that contain
// subdirectories containing package files (transitively). If a non-nil
// pathFilter is provided, directory paths additionally must be accepted
// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is
// provided for maxDepth, nodes at larger depths are pruned as well; they
// are assumed to contain package files even if their contents are not known
// (i.e., in this case the tree may contain directories w/o any package files).
//
func (c *Corpus) newDirectory(root string, maxDepth int) *Directory {
// The root could be a symbolic link so use Stat not Lstat.
d, err := c.fs.Stat(root)
// If we fail here, report detailed error messages; otherwise
// is is hard to see why a directory tree was not built.
switch {
case err != nil:
log.Printf("newDirectory(%s): %s", root, err)
return nil
case root != "/" && !isPkgDir(d):
log.Printf("newDirectory(%s): not a package directory", root)
return nil
case root == "/" && !d.IsDir():
log.Printf("newDirectory(%s): not a directory", root)
return nil
}
if maxDepth < 0 {
maxDepth = 1e6 // "infinity"
}
b := treeBuilder{c, maxDepth}
// the file set provided is only for local parsing, no position
// information escapes and thus we don't need to save the set
return b.newDirTree(token.NewFileSet(), root, d.Name(), 0)
}
func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) {
if dir != nil {
if !skipRoot {
c <- dir
}
for _, d := range dir.Dirs {
d.walk(c, false)
}
}
}
func (dir *Directory) iter(skipRoot bool) <-chan *Directory {
c := make(chan *Directory)
go func() {
dir.walk(c, skipRoot)
close(c)
}()
return c
}
func (dir *Directory) lookupLocal(name string) *Directory {
for _, d := range dir.Dirs {
if d.Name == name {
return d
}
}
return nil
}
func splitPath(p string) []string {
p = strings.TrimPrefix(p, "/")
if p == "" {
return nil
}
return strings.Split(p, "/")
}
// lookup looks for the *Directory for a given path, relative to dir.
func (dir *Directory) lookup(path string) *Directory {
d := splitPath(dir.Path)
p := splitPath(path)
i := 0
for i < len(d) {
if i >= len(p) || d[i] != p[i] {
return nil
}
i++
}
for dir != nil && i < len(p) {
dir = dir.lookupLocal(p[i])
i++
}
return dir
}
// DirEntry describes a directory entry. The Depth and Height values
// are useful for presenting an entry in an indented fashion.
//
type DirEntry struct {
Depth int // >= 0
Height int // = DirList.MaxHeight - Depth, > 0
Path string // directory path; includes Name, relative to DirList root
Name string // directory name
HasPkg bool // true if the directory contains at least one package
Synopsis string // package documentation, if any
RootType vfs.RootType // root type of the filesystem containing the direntry
}
type DirList struct {
MaxHeight int // directory tree height, > 0
List []DirEntry
}
// hasThirdParty checks whether a list of directory entries has packages outside
// the standard library or not.
func hasThirdParty(list []DirEntry) bool {
for _, entry := range list {
if entry.RootType == vfs.RootTypeGoPath {
return true
}
}
return false
}
// listing creates a (linear) directory listing from a directory tree.
// If skipRoot is set, the root directory itself is excluded from the list.
// If filter is set, only the directory entries whose paths match the filter
// are included.
//
func (root *Directory) listing(skipRoot bool, filter func(string) bool) *DirList {
if root == nil {
return nil
}
// determine number of entries n and maximum height
n := 0
minDepth := 1 << 30 // infinity
maxDepth := 0
for d := range root.iter(skipRoot) {
n++
if minDepth > d.Depth {
minDepth = d.Depth
}
if maxDepth < d.Depth {
maxDepth = d.Depth
}
}
maxHeight := maxDepth - minDepth + 1
if n == 0 {
return nil
}
// create list
list := make([]DirEntry, 0, n)
for d := range root.iter(skipRoot) {
if filter != nil && !filter(d.Path) {
continue
}
var p DirEntry
p.Depth = d.Depth - minDepth
p.Height = maxHeight - p.Depth
// the path is relative to root.Path - remove the root.Path
// prefix (the prefix should always be present but avoid
// crashes and check)
path := strings.TrimPrefix(d.Path, root.Path)
// remove leading separator if any - path must be relative
path = strings.TrimPrefix(path, "/")
p.Path = path
p.Name = d.Name
p.HasPkg = d.HasPkg
p.Synopsis = d.Synopsis
p.RootType = d.RootType
list = append(list, p)
}
return &DirList{maxHeight, list}
}

64
vendor/golang.org/x/tools/godoc/dirtrees_test.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
// Copyright 2018 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 godoc
import (
"go/build"
"path/filepath"
"runtime"
"sort"
"testing"
"golang.org/x/tools/godoc/vfs"
"golang.org/x/tools/godoc/vfs/gatefs"
)
func TestNewDirTree(t *testing.T) {
fsGate := make(chan bool, 20)
rootfs := gatefs.New(vfs.OS(runtime.GOROOT()), fsGate)
fs := vfs.NameSpace{}
fs.Bind("/", rootfs, "/", vfs.BindReplace)
c := NewCorpus(fs)
// 3 levels deep is enough for testing
dir := c.newDirectory("/", 3)
processDir(t, dir)
}
func processDir(t *testing.T, dir *Directory) {
var list []string
for _, d := range dir.Dirs {
list = append(list, d.Name)
// recursively process the lower level
processDir(t, d)
}
if sort.StringsAreSorted(list) == false {
t.Errorf("list: %v is not sorted\n", list)
}
}
func BenchmarkNewDirectory(b *testing.B) {
if testing.Short() {
b.Skip("not running tests requiring large file scan in short mode")
}
fsGate := make(chan bool, 20)
goroot := runtime.GOROOT()
rootfs := gatefs.New(vfs.OS(goroot), fsGate)
fs := vfs.NameSpace{}
fs.Bind("/", rootfs, "/", vfs.BindReplace)
for _, p := range filepath.SplitList(build.Default.GOPATH) {
fs.Bind("/src/golang.org", gatefs.New(vfs.OS(p), fsGate), "/src/golang.org", vfs.BindAfter)
}
b.ResetTimer()
b.ReportAllocs()
for tries := 0; tries < b.N; tries++ {
corpus := NewCorpus(fs)
corpus.newDirectory("/", -1)
}
}

559
vendor/golang.org/x/tools/godoc/dl/dl.go generated vendored Normal file
View File

@@ -0,0 +1,559 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build appengine
// Package dl implements a simple downloads frontend server.
//
// It accepts HTTP POST requests to create a new download metadata entity, and
// lists entities with sorting and filtering.
// It is designed to run only on the instance of godoc that serves golang.org.
package dl
import (
"crypto/hmac"
"crypto/md5"
"encoding/json"
"fmt"
"html/template"
"io"
"net/http"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"google.golang.org/appengine"
"google.golang.org/appengine/datastore"
"google.golang.org/appengine/log"
"google.golang.org/appengine/memcache"
)
const (
downloadBaseURL = "https://dl.google.com/go/"
cacheKey = "download_list_3" // increment if listTemplateData changes
cacheDuration = time.Hour
)
func RegisterHandlers(mux *http.ServeMux) {
mux.Handle("/dl", http.RedirectHandler("/dl/", http.StatusFound))
mux.HandleFunc("/dl/", getHandler) // also serves listHandler
mux.HandleFunc("/dl/upload", uploadHandler)
mux.HandleFunc("/dl/init", initHandler)
}
// File represents a file on the golang.org downloads page.
// It should be kept in sync with the upload code in x/build/cmd/release.
type File struct {
Filename string `json:"filename"`
OS string `json:"os"`
Arch string `json:"arch"`
Version string `json:"version"`
Checksum string `json:"-" datastore:",noindex"` // SHA1; deprecated
ChecksumSHA256 string `json:"sha256" datastore:",noindex"`
Size int64 `json:"size" datastore:",noindex"`
Kind string `json:"kind"` // "archive", "installer", "source"
Uploaded time.Time `json:"-"`
}
func (f File) ChecksumType() string {
if f.ChecksumSHA256 != "" {
return "SHA256"
}
return "SHA1"
}
func (f File) PrettyChecksum() string {
if f.ChecksumSHA256 != "" {
return f.ChecksumSHA256
}
return f.Checksum
}
func (f File) PrettyOS() string {
if f.OS == "darwin" {
switch {
case strings.Contains(f.Filename, "osx10.8"):
return "OS X 10.8+"
case strings.Contains(f.Filename, "osx10.6"):
return "OS X 10.6+"
}
}
return pretty(f.OS)
}
func (f File) PrettySize() string {
const mb = 1 << 20
if f.Size == 0 {
return ""
}
if f.Size < mb {
// All Go releases are >1mb, but handle this case anyway.
return fmt.Sprintf("%v bytes", f.Size)
}
return fmt.Sprintf("%.0fMB", float64(f.Size)/mb)
}
var primaryPorts = map[string]bool{
"darwin/amd64": true,
"linux/386": true,
"linux/amd64": true,
"linux/armv6l": true,
"windows/386": true,
"windows/amd64": true,
}
func (f File) PrimaryPort() bool {
if f.Kind == "source" {
return true
}
return primaryPorts[f.OS+"/"+f.Arch]
}
func (f File) Highlight() bool {
switch {
case f.Kind == "source":
return true
case f.Arch == "amd64" && f.OS == "linux":
return true
case f.Arch == "amd64" && f.Kind == "installer":
switch f.OS {
case "windows":
return true
case "darwin":
if !strings.Contains(f.Filename, "osx10.6") {
return true
}
}
}
return false
}
func (f File) URL() string {
return downloadBaseURL + f.Filename
}
type Release struct {
Version string `json:"version"`
Stable bool `json:"stable"`
Files []File `json:"files"`
Visible bool `json:"-"` // show files on page load
SplitPortTable bool `json:"-"` // whether files should be split by primary/other ports.
}
type Feature struct {
// The File field will be filled in by the first stable File
// whose name matches the given fileRE.
File
fileRE *regexp.Regexp
Platform string // "Microsoft Windows", "Apple macOS", "Linux"
Requirements string // "Windows XP and above, 64-bit Intel Processor"
}
// featuredFiles lists the platforms and files to be featured
// at the top of the downloads page.
var featuredFiles = []Feature{
{
Platform: "Microsoft Windows",
Requirements: "Windows XP SP3 or later, Intel 64-bit processor",
fileRE: regexp.MustCompile(`\.windows-amd64\.msi$`),
},
{
Platform: "Apple macOS",
Requirements: "macOS 10.8 or later, Intel 64-bit processor",
fileRE: regexp.MustCompile(`\.darwin-amd64(-osx10\.8)?\.pkg$`),
},
{
Platform: "Linux",
Requirements: "Linux 2.6.23 or later, Intel 64-bit processor",
fileRE: regexp.MustCompile(`\.linux-amd64\.tar\.gz$`),
},
{
Platform: "Source",
fileRE: regexp.MustCompile(`\.src\.tar\.gz$`),
},
}
// data to send to the template; increment cacheKey if you change this.
type listTemplateData struct {
Featured []Feature
Stable, Unstable, Archive []Release
}
var (
listTemplate = template.Must(template.New("").Funcs(templateFuncs).Parse(templateHTML))
templateFuncs = template.FuncMap{"pretty": pretty}
)
func listHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var (
c = appengine.NewContext(r)
d listTemplateData
)
if _, err := memcache.Gob.Get(c, cacheKey, &d); err != nil {
if err == memcache.ErrCacheMiss {
log.Debugf(c, "cache miss")
} else {
log.Errorf(c, "cache get error: %v", err)
}
var fs []File
_, err := datastore.NewQuery("File").Ancestor(rootKey(c)).GetAll(c, &fs)
if err != nil {
log.Errorf(c, "error listing: %v", err)
return
}
d.Stable, d.Unstable, d.Archive = filesToReleases(fs)
if len(d.Stable) > 0 {
d.Featured = filesToFeatured(d.Stable[0].Files)
}
item := &memcache.Item{Key: cacheKey, Object: &d, Expiration: cacheDuration}
if err := memcache.Gob.Set(c, item); err != nil {
log.Errorf(c, "cache set error: %v", err)
}
}
if r.URL.Query().Get("mode") == "json" {
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
if err := enc.Encode(d.Stable); err != nil {
log.Errorf(c, "failed rendering JSON for releases: %v", err)
}
return
}
if err := listTemplate.ExecuteTemplate(w, "root", d); err != nil {
log.Errorf(c, "error executing template: %v", err)
}
}
func filesToFeatured(fs []File) (featured []Feature) {
for _, feature := range featuredFiles {
for _, file := range fs {
if feature.fileRE.MatchString(file.Filename) {
feature.File = file
featured = append(featured, feature)
break
}
}
}
return
}
func filesToReleases(fs []File) (stable, unstable, archive []Release) {
sort.Sort(fileOrder(fs))
var r *Release
var stableMaj, stableMin int
add := func() {
if r == nil {
return
}
if !r.Stable {
if len(unstable) != 0 {
// Only show one (latest) unstable version.
return
}
maj, min, _ := parseVersion(r.Version)
if maj < stableMaj || maj == stableMaj && min <= stableMin {
// Display unstable version only if newer than the
// latest stable release.
return
}
unstable = append(unstable, *r)
}
// Reports whether the release is the most recent minor version of the
// two most recent major versions.
shouldAddStable := func() bool {
if len(stable) >= 2 {
// Show up to two stable versions.
return false
}
if len(stable) == 0 {
// Most recent stable version.
stableMaj, stableMin, _ = parseVersion(r.Version)
return true
}
if maj, _, _ := parseVersion(r.Version); maj == stableMaj {
// Older minor version of most recent major version.
return false
}
// Second most recent stable version.
return true
}
if !shouldAddStable() {
archive = append(archive, *r)
return
}
// Split the file list into primary/other ports for the stable releases.
// NOTE(cbro): This is only done for stable releases because maintaining the historical
// nature of primary/other ports for older versions is infeasible.
// If freebsd is considered primary some time in the future, we'd not want to
// mark all of the older freebsd binaries as "primary".
// It might be better if we set that as a flag when uploading.
r.SplitPortTable = true
r.Visible = true // Toggle open all stable releases.
stable = append(stable, *r)
}
for _, f := range fs {
if r == nil || f.Version != r.Version {
add()
r = &Release{
Version: f.Version,
Stable: isStable(f.Version),
}
}
r.Files = append(r.Files, f)
}
add()
return
}
// isStable reports whether the version string v is a stable version.
func isStable(v string) bool {
return !strings.Contains(v, "beta") && !strings.Contains(v, "rc")
}
type fileOrder []File
func (s fileOrder) Len() int { return len(s) }
func (s fileOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s fileOrder) Less(i, j int) bool {
a, b := s[i], s[j]
if av, bv := a.Version, b.Version; av != bv {
return versionLess(av, bv)
}
if a.OS != b.OS {
return a.OS < b.OS
}
if a.Arch != b.Arch {
return a.Arch < b.Arch
}
if a.Kind != b.Kind {
return a.Kind < b.Kind
}
return a.Filename < b.Filename
}
func versionLess(a, b string) bool {
// Put stable releases first.
if isStable(a) != isStable(b) {
return isStable(a)
}
maja, mina, ta := parseVersion(a)
majb, minb, tb := parseVersion(b)
if maja == majb {
if mina == minb {
return ta >= tb
}
return mina >= minb
}
return maja >= majb
}
func parseVersion(v string) (maj, min int, tail string) {
if i := strings.Index(v, "beta"); i > 0 {
tail = v[i:]
v = v[:i]
}
if i := strings.Index(v, "rc"); i > 0 {
tail = v[i:]
v = v[:i]
}
p := strings.Split(strings.TrimPrefix(v, "go1."), ".")
maj, _ = strconv.Atoi(p[0])
if len(p) < 2 {
return
}
min, _ = strconv.Atoi(p[1])
return
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
c := appengine.NewContext(r)
// Authenticate using a user token (same as gomote).
user := r.FormValue("user")
if !validUser(user) {
http.Error(w, "bad user", http.StatusForbidden)
return
}
if r.FormValue("key") != userKey(c, user) {
http.Error(w, "bad key", http.StatusForbidden)
return
}
var f File
defer r.Body.Close()
if err := json.NewDecoder(r.Body).Decode(&f); err != nil {
log.Errorf(c, "error decoding upload JSON: %v", err)
http.Error(w, "Something broke", http.StatusInternalServerError)
return
}
if f.Filename == "" {
http.Error(w, "Must provide Filename", http.StatusBadRequest)
return
}
if f.Uploaded.IsZero() {
f.Uploaded = time.Now()
}
k := datastore.NewKey(c, "File", f.Filename, 0, rootKey(c))
if _, err := datastore.Put(c, k, &f); err != nil {
log.Errorf(c, "putting File entity: %v", err)
http.Error(w, "could not put File entity", http.StatusInternalServerError)
return
}
if err := memcache.Delete(c, cacheKey); err != nil {
log.Errorf(c, "cache delete error: %v", err)
}
io.WriteString(w, "OK")
}
func getHandler(w http.ResponseWriter, r *http.Request) {
name := strings.TrimPrefix(r.URL.Path, "/dl/")
if name == "" {
listHandler(w, r)
return
}
if !fileRe.MatchString(name) {
http.NotFound(w, r)
return
}
http.Redirect(w, r, downloadBaseURL+name, http.StatusFound)
}
func validUser(user string) bool {
switch user {
case "adg", "bradfitz", "cbro", "andybons", "valsorda":
return true
}
return false
}
func userKey(c context.Context, user string) string {
h := hmac.New(md5.New, []byte(secret(c)))
h.Write([]byte("user-" + user))
return fmt.Sprintf("%x", h.Sum(nil))
}
var fileRe = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+\.(tar\.gz|pkg|msi|zip)$`)
func initHandler(w http.ResponseWriter, r *http.Request) {
var fileRoot struct {
Root string
}
c := appengine.NewContext(r)
k := rootKey(c)
err := datastore.RunInTransaction(c, func(c context.Context) error {
err := datastore.Get(c, k, &fileRoot)
if err != nil && err != datastore.ErrNoSuchEntity {
return err
}
_, err = datastore.Put(c, k, &fileRoot)
return err
}, nil)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
io.WriteString(w, "OK")
}
// rootKey is the ancestor of all File entities.
func rootKey(c context.Context) *datastore.Key {
return datastore.NewKey(c, "FileRoot", "root", 0, nil)
}
// pretty returns a human-readable version of the given OS, Arch, or Kind.
func pretty(s string) string {
t, ok := prettyStrings[s]
if !ok {
return s
}
return t
}
var prettyStrings = map[string]string{
"darwin": "macOS",
"freebsd": "FreeBSD",
"linux": "Linux",
"windows": "Windows",
"386": "x86",
"amd64": "x86-64",
"armv6l": "ARMv6",
"arm64": "ARMv8",
"archive": "Archive",
"installer": "Installer",
"source": "Source",
}
// Code below copied from x/build/app/key
var theKey struct {
sync.RWMutex
builderKey
}
type builderKey struct {
Secret string
}
func (k *builderKey) Key(c context.Context) *datastore.Key {
return datastore.NewKey(c, "BuilderKey", "root", 0, nil)
}
func secret(c context.Context) string {
// check with rlock
theKey.RLock()
k := theKey.Secret
theKey.RUnlock()
if k != "" {
return k
}
// prepare to fill; check with lock and keep lock
theKey.Lock()
defer theKey.Unlock()
if theKey.Secret != "" {
return theKey.Secret
}
// fill
if err := datastore.Get(c, theKey.Key(c), &theKey.builderKey); err != nil {
if err == datastore.ErrNoSuchEntity {
// If the key is not stored in datastore, write it.
// This only happens at the beginning of a new deployment.
// The code is left here for SDK use and in case a fresh
// deployment is ever needed. "gophers rule" is not the
// real key.
if !appengine.IsDevAppServer() {
panic("lost key from datastore")
}
theKey.Secret = "gophers rule"
datastore.Put(c, theKey.Key(c), &theKey.builderKey)
return theKey.Secret
}
panic("cannot load builder key: " + err.Error())
}
return theKey.Secret
}

137
vendor/golang.org/x/tools/godoc/dl/dl_test.go generated vendored Normal file
View File

@@ -0,0 +1,137 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build appengine
package dl
import (
"sort"
"strings"
"testing"
)
func TestParseVersion(t *testing.T) {
for _, c := range []struct {
in string
maj, min int
tail string
}{
{"go1.5", 5, 0, ""},
{"go1.5beta1", 5, 0, "beta1"},
{"go1.5.1", 5, 1, ""},
{"go1.5.1rc1", 5, 1, "rc1"},
} {
maj, min, tail := parseVersion(c.in)
if maj != c.maj || min != c.min || tail != c.tail {
t.Errorf("parseVersion(%q) = %v, %v, %q; want %v, %v, %q",
c.in, maj, min, tail, c.maj, c.min, c.tail)
}
}
}
func TestFileOrder(t *testing.T) {
fs := []File{
{Filename: "go1.3.src.tar.gz", Version: "go1.3", OS: "", Arch: "", Kind: "source"},
{Filename: "go1.3.1.src.tar.gz", Version: "go1.3.1", OS: "", Arch: "", Kind: "source"},
{Filename: "go1.3.linux-amd64.tar.gz", Version: "go1.3", OS: "linux", Arch: "amd64", Kind: "archive"},
{Filename: "go1.3.1.linux-amd64.tar.gz", Version: "go1.3.1", OS: "linux", Arch: "amd64", Kind: "archive"},
{Filename: "go1.3.darwin-amd64.tar.gz", Version: "go1.3", OS: "darwin", Arch: "amd64", Kind: "archive"},
{Filename: "go1.3.darwin-amd64.pkg", Version: "go1.3", OS: "darwin", Arch: "amd64", Kind: "installer"},
{Filename: "go1.3.darwin-386.tar.gz", Version: "go1.3", OS: "darwin", Arch: "386", Kind: "archive"},
{Filename: "go1.3beta1.linux-amd64.tar.gz", Version: "go1.3beta1", OS: "linux", Arch: "amd64", Kind: "archive"},
{Filename: "go1.3beta2.linux-amd64.tar.gz", Version: "go1.3beta2", OS: "linux", Arch: "amd64", Kind: "archive"},
{Filename: "go1.3rc1.linux-amd64.tar.gz", Version: "go1.3rc1", OS: "linux", Arch: "amd64", Kind: "archive"},
{Filename: "go1.2.linux-amd64.tar.gz", Version: "go1.2", OS: "linux", Arch: "amd64", Kind: "archive"},
{Filename: "go1.2.2.linux-amd64.tar.gz", Version: "go1.2.2", OS: "linux", Arch: "amd64", Kind: "archive"},
}
sort.Sort(fileOrder(fs))
var s []string
for _, f := range fs {
s = append(s, f.Filename)
}
got := strings.Join(s, "\n")
want := strings.Join([]string{
"go1.3.1.src.tar.gz",
"go1.3.1.linux-amd64.tar.gz",
"go1.3.src.tar.gz",
"go1.3.darwin-386.tar.gz",
"go1.3.darwin-amd64.tar.gz",
"go1.3.darwin-amd64.pkg",
"go1.3.linux-amd64.tar.gz",
"go1.2.2.linux-amd64.tar.gz",
"go1.2.linux-amd64.tar.gz",
"go1.3rc1.linux-amd64.tar.gz",
"go1.3beta2.linux-amd64.tar.gz",
"go1.3beta1.linux-amd64.tar.gz",
}, "\n")
if got != want {
t.Errorf("sort order is\n%s\nwant:\n%s", got, want)
}
}
func TestFilesToReleases(t *testing.T) {
fs := []File{
{Version: "go1.7.4", OS: "darwin"},
{Version: "go1.7.4", OS: "windows"},
{Version: "go1.7", OS: "darwin"},
{Version: "go1.7", OS: "windows"},
{Version: "go1.6.2", OS: "darwin"},
{Version: "go1.6.2", OS: "windows"},
{Version: "go1.6", OS: "darwin"},
{Version: "go1.6", OS: "windows"},
{Version: "go1.5.2", OS: "darwin"},
{Version: "go1.5.2", OS: "windows"},
{Version: "go1.5", OS: "darwin"},
{Version: "go1.5", OS: "windows"},
{Version: "go1.5beta1", OS: "windows"},
}
stable, unstable, archive := filesToReleases(fs)
if got, want := len(stable), 2; want != got {
t.Errorf("len(stable): got %v, want %v", got, want)
} else {
if got, want := stable[0].Version, "go1.7.4"; want != got {
t.Errorf("stable[0].Version: got %v, want %v", got, want)
}
if got, want := stable[1].Version, "go1.6.2"; want != got {
t.Errorf("stable[1].Version: got %v, want %v", got, want)
}
}
if got, want := len(unstable), 0; want != got {
t.Errorf("len(unstable): got %v, want %v", got, want)
}
if got, want := len(archive), 4; want != got {
t.Errorf("len(archive): got %v, want %v", got, want)
}
}
func TestOldUnstableNotShown(t *testing.T) {
fs := []File{
{Version: "go1.7.4"},
{Version: "go1.7"},
{Version: "go1.7beta1"},
}
_, unstable, _ := filesToReleases(fs)
if len(unstable) != 0 {
t.Errorf("got unstable, want none")
}
}
func TestUnstableShown(t *testing.T) {
fs := []File{
{Version: "go1.8beta2"},
{Version: "go1.8rc1"},
{Version: "go1.7.4"},
{Version: "go1.7"},
{Version: "go1.7beta1"},
}
_, unstable, _ := filesToReleases(fs)
if got, want := len(unstable), 1; got != want {
t.Fatalf("len(unstable): got %v, want %v", got, want)
}
// show rcs ahead of betas.
if got, want := unstable[0].Version, "go1.8rc1"; got != want {
t.Fatalf("unstable[0].Version: got %v, want %v", got, want)
}
}

279
vendor/golang.org/x/tools/godoc/dl/tmpl.go generated vendored Normal file
View File

@@ -0,0 +1,279 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build appengine
package dl
// TODO(adg): refactor this to use the tools/godoc/static template.
const templateHTML = `
{{define "root"}}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Downloads - The Go Programming Language</title>
<link type="text/css" rel="stylesheet" href="/lib/godoc/style.css">
<script type="text/javascript">window.initFuncs = [];</script>
<style>
table.codetable {
margin-left: 20px; margin-right: 20px;
border-collapse: collapse;
}
table.codetable tr {
background-color: #f0f0f0;
}
table.codetable tr:nth-child(2n), table.codetable tr.first {
background-color: white;
}
table.codetable td, table.codetable th {
white-space: nowrap;
padding: 6px 10px;
}
table.codetable tt {
font-size: xx-small;
}
table.codetable tr.highlight td {
font-weight: bold;
}
a.downloadBox {
display: block;
color: #222;
border: 1px solid #375EAB;
border-radius: 5px;
background: #E0EBF5;
width: 280px;
float: left;
margin-left: 10px;
margin-bottom: 10px;
padding: 10px;
}
a.downloadBox:hover {
text-decoration: none;
}
.downloadBox .platform {
font-size: large;
}
.downloadBox .filename {
color: #375EAB;
font-weight: bold;
line-height: 1.5em;
}
a.downloadBox:hover .filename {
text-decoration: underline;
}
.downloadBox .size {
font-size: small;
font-weight: normal;
}
.downloadBox .reqs {
font-size: small;
font-style: italic;
}
.downloadBox .checksum {
font-size: 5pt;
}
</style>
</head>
<body>
<div id="topbar"><div class="container">
<div class="top-heading"><a href="/">The Go Programming Language</a></div>
<form method="GET" action="/search">
<div id="menu">
<a href="/doc/">Documents</a>
<a href="/pkg/">Packages</a>
<a href="/project/">The Project</a>
<a href="/help/">Help</a>
<a href="/blog/">Blog</a>
<span class="search-box"><input type="search" id="search" name="q" placeholder="Search" aria-label="Search" required><button type="submit"><span><!-- magnifying glass: --><svg width="24" height="24" viewBox="0 0 24 24"><title>submit search</title><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/><path d="M0 0h24v24H0z" fill="none"/></svg></span></button></span>
</div>
</form>
</div></div>
<div id="page">
<div class="container">
<h1>Downloads</h1>
<p>
After downloading a binary release suitable for your system,
please follow the <a href="/doc/install">installation instructions</a>.
</p>
<p>
If you are building from source,
follow the <a href="/doc/install/source">source installation instructions</a>.
</p>
<p>
See the <a href="/doc/devel/release.html">release history</a> for more
information about Go releases.
</p>
{{with .Featured}}
<h3 id="featured">Featured downloads</h3>
{{range .}}
{{template "download" .}}
{{end}}
{{end}}
<div style="clear: both;"></div>
{{with .Stable}}
<h3 id="stable">Stable versions</h3>
{{template "releases" .}}
{{end}}
{{with .Unstable}}
<h3 id="unstable">Unstable version</h3>
{{template "releases" .}}
{{end}}
{{with .Archive}}
<div class="toggle" id="archive">
<div class="collapsed">
<h3 class="toggleButton" title="Click to show versions">Archived versions▹</h3>
</div>
<div class="expanded">
<h3 class="toggleButton" title="Click to hide versions">Archived versions▾</h3>
{{template "releases" .}}
</div>
</div>
{{end}}
<div id="footer">
<p>
Except as
<a href="https://developers.google.com/site-policies#restrictions">noted</a>,
the content of this page is licensed under the Creative Commons
Attribution 3.0 License,<br>
and code is licensed under a <a href="http://golang.org/LICENSE">BSD license</a>.<br>
<a href="http://golang.org/doc/tos.html">Terms of Service</a> |
<a href="http://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
</p>
</div><!-- #footer -->
</div><!-- .container -->
</div><!-- #page -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-11222381-2', 'auto');
ga('send', 'pageview');
</script>
</body>
<script src="/lib/godoc/jquery.js"></script>
<script src="/lib/godoc/godocs.js"></script>
<script>
$(document).ready(function() {
$('a.download').click(function(e) {
// Try using the link text as the file name,
// unless there's a child element of class 'filename'.
var filename = $(this).text();
var child = $(this).find('.filename');
if (child.length > 0) {
filename = child.text();
}
// This must be kept in sync with the filenameRE in godocs.js.
var filenameRE = /^go1\.\d+(\.\d+)?([a-z0-9]+)?\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\.[68])?\.([a-z.]+)$/;
var m = filenameRE.exec(filename);
if (!m) {
// Don't redirect to the download page if it won't recognize this file.
// (Should not happen.)
return;
}
var dest = "/doc/install";
if (filename.indexOf(".src.") != -1) {
dest += "/source";
}
dest += "?download=" + filename;
e.preventDefault();
e.stopPropagation();
window.location = dest;
});
});
</script>
</html>
{{end}}
{{define "releases"}}
{{range .}}
<div class="toggle{{if .Visible}}Visible{{end}}" id="{{.Version}}">
<div class="collapsed">
<h2 class="toggleButton" title="Click to show downloads for this version">{{.Version}} ▹</h2>
</div>
<div class="expanded">
<h2 class="toggleButton" title="Click to hide downloads for this version">{{.Version}} ▾</h2>
{{if .Stable}}{{else}}
<p>This is an <b>unstable</b> version of Go. Use with caution.</p>
<p>If you already have Go installed, you can install this version by running:</p>
<pre>
go get golang.org/x/build/version/{{.Version}}
</pre>
<p>Then, use the <code>{{.Version}}</code> command instead of the <code>go</code> command to use {{.Version}}.</p>
{{end}}
{{template "files" .}}
</div>
</div>
{{end}}
{{end}}
{{define "files"}}
<table class="codetable">
<thead>
<tr class="first">
<th>File name</th>
<th>Kind</th>
<th>OS</th>
<th>Arch</th>
<th>Size</th>
{{/* Use the checksum type of the first file for the column heading. */}}
<th>{{(index .Files 0).ChecksumType}} Checksum</th>
</tr>
</thead>
{{if .SplitPortTable}}
{{range .Files}}{{if .PrimaryPort}}{{template "file" .}}{{end}}{{end}}
{{/* TODO(cbro): add a link to an explanatory doc page */}}
<tr class="first"><th colspan="6" class="first">Other Ports</th></tr>
{{range .Files}}{{if not .PrimaryPort}}{{template "file" .}}{{end}}{{end}}
{{else}}
{{range .Files}}{{template "file" .}}{{end}}
{{end}}
</table>
{{end}}
{{define "file"}}
<tr{{if .Highlight}} class="highlight"{{end}}>
<td class="filename"><a class="download" href="{{.URL}}">{{.Filename}}</a></td>
<td>{{pretty .Kind}}</td>
<td>{{.PrettyOS}}</td>
<td>{{pretty .Arch}}</td>
<td>{{.PrettySize}}</td>
<td><tt>{{.PrettyChecksum}}</tt></td>
</tr>
{{end}}
{{define "download"}}
<a class="download downloadBox" href="{{.URL}}">
<div class="platform">{{.Platform}}</div>
{{with .Requirements}}<div class="reqs">{{.}}</div>{{end}}
<div>
<span class="filename">{{.Filename}}</span>
{{if .Size}}<span class="size">({{.PrettySize}})</span>{{end}}
</div>
</a>
{{end}}
`

371
vendor/golang.org/x/tools/godoc/format.go generated vendored Normal file
View File

@@ -0,0 +1,371 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements FormatSelections and FormatText.
// FormatText is used to HTML-format Go and non-Go source
// text with line numbers and highlighted sections. It is
// built on top of FormatSelections, a generic formatter
// for "selected" text.
package godoc
import (
"fmt"
"go/scanner"
"go/token"
"io"
"regexp"
"strconv"
"text/template"
)
// ----------------------------------------------------------------------------
// Implementation of FormatSelections
// A Segment describes a text segment [start, end).
// The zero value of a Segment is a ready-to-use empty segment.
//
type Segment struct {
start, end int
}
func (seg *Segment) isEmpty() bool { return seg.start >= seg.end }
// A Selection is an "iterator" function returning a text segment.
// Repeated calls to a selection return consecutive, non-overlapping,
// non-empty segments, followed by an infinite sequence of empty
// segments. The first empty segment marks the end of the selection.
//
type Selection func() Segment
// A LinkWriter writes some start or end "tag" to w for the text offset offs.
// It is called by FormatSelections at the start or end of each link segment.
//
type LinkWriter func(w io.Writer, offs int, start bool)
// A SegmentWriter formats a text according to selections and writes it to w.
// The selections parameter is a bit set indicating which selections provided
// to FormatSelections overlap with the text segment: If the n'th bit is set
// in selections, the n'th selection provided to FormatSelections is overlapping
// with the text.
//
type SegmentWriter func(w io.Writer, text []byte, selections int)
// FormatSelections takes a text and writes it to w using link and segment
// writers lw and sw as follows: lw is invoked for consecutive segment starts
// and ends as specified through the links selection, and sw is invoked for
// consecutive segments of text overlapped by the same selections as specified
// by selections. The link writer lw may be nil, in which case the links
// Selection is ignored.
//
func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) {
// If we have a link writer, make the links
// selection the last entry in selections
if lw != nil {
selections = append(selections, links)
}
// compute the sequence of consecutive segment changes
changes := newMerger(selections)
// The i'th bit in bitset indicates that the text
// at the current offset is covered by selections[i].
bitset := 0
lastOffs := 0
// Text segments are written in a delayed fashion
// such that consecutive segments belonging to the
// same selection can be combined (peephole optimization).
// last describes the last segment which has not yet been written.
var last struct {
begin, end int // valid if begin < end
bitset int
}
// flush writes the last delayed text segment
flush := func() {
if last.begin < last.end {
sw(w, text[last.begin:last.end], last.bitset)
}
last.begin = last.end // invalidate last
}
// segment runs the segment [lastOffs, end) with the selection
// indicated by bitset through the segment peephole optimizer.
segment := func(end int) {
if lastOffs < end { // ignore empty segments
if last.end != lastOffs || last.bitset != bitset {
// the last segment is not adjacent to or
// differs from the new one
flush()
// start a new segment
last.begin = lastOffs
}
last.end = end
last.bitset = bitset
}
}
for {
// get the next segment change
index, offs, start := changes.next()
if index < 0 || offs > len(text) {
// no more segment changes or the next change
// is past the end of the text - we're done
break
}
// determine the kind of segment change
if lw != nil && index == len(selections)-1 {
// we have a link segment change (see start of this function):
// format the previous selection segment, write the
// link tag and start a new selection segment
segment(offs)
flush()
lastOffs = offs
lw(w, offs, start)
} else {
// we have a selection change:
// format the previous selection segment, determine
// the new selection bitset and start a new segment
segment(offs)
lastOffs = offs
mask := 1 << uint(index)
if start {
bitset |= mask
} else {
bitset &^= mask
}
}
}
segment(len(text))
flush()
}
// A merger merges a slice of Selections and produces a sequence of
// consecutive segment change events through repeated next() calls.
//
type merger struct {
selections []Selection
segments []Segment // segments[i] is the next segment of selections[i]
}
const infinity int = 2e9
func newMerger(selections []Selection) *merger {
segments := make([]Segment, len(selections))
for i, sel := range selections {
segments[i] = Segment{infinity, infinity}
if sel != nil {
if seg := sel(); !seg.isEmpty() {
segments[i] = seg
}
}
}
return &merger{selections, segments}
}
// next returns the next segment change: index specifies the Selection
// to which the segment belongs, offs is the segment start or end offset
// as determined by the start value. If there are no more segment changes,
// next returns an index value < 0.
//
func (m *merger) next() (index, offs int, start bool) {
// find the next smallest offset where a segment starts or ends
offs = infinity
index = -1
for i, seg := range m.segments {
switch {
case seg.start < offs:
offs = seg.start
index = i
start = true
case seg.end < offs:
offs = seg.end
index = i
start = false
}
}
if index < 0 {
// no offset found => all selections merged
return
}
// offset found - it's either the start or end offset but
// either way it is ok to consume the start offset: set it
// to infinity so it won't be considered in the following
// next call
m.segments[index].start = infinity
if start {
return
}
// end offset found - consume it
m.segments[index].end = infinity
// advance to the next segment for that selection
seg := m.selections[index]()
if !seg.isEmpty() {
m.segments[index] = seg
}
return
}
// ----------------------------------------------------------------------------
// Implementation of FormatText
// lineSelection returns the line segments for text as a Selection.
func lineSelection(text []byte) Selection {
i, j := 0, 0
return func() (seg Segment) {
// find next newline, if any
for j < len(text) {
j++
if text[j-1] == '\n' {
break
}
}
if i < j {
// text[i:j] constitutes a line
seg = Segment{i, j}
i = j
}
return
}
}
// tokenSelection returns, as a selection, the sequence of
// consecutive occurrences of token sel in the Go src text.
//
func tokenSelection(src []byte, sel token.Token) Selection {
var s scanner.Scanner
fset := token.NewFileSet()
file := fset.AddFile("", fset.Base(), len(src))
s.Init(file, src, nil, scanner.ScanComments)
return func() (seg Segment) {
for {
pos, tok, lit := s.Scan()
if tok == token.EOF {
break
}
offs := file.Offset(pos)
if tok == sel {
seg = Segment{offs, offs + len(lit)}
break
}
}
return
}
}
// makeSelection is a helper function to make a Selection from a slice of pairs.
// Pairs describing empty segments are ignored.
//
func makeSelection(matches [][]int) Selection {
i := 0
return func() Segment {
for i < len(matches) {
m := matches[i]
i++
if m[0] < m[1] {
// non-empty segment
return Segment{m[0], m[1]}
}
}
return Segment{}
}
}
// regexpSelection computes the Selection for the regular expression expr in text.
func regexpSelection(text []byte, expr string) Selection {
var matches [][]int
if rx, err := regexp.Compile(expr); err == nil {
matches = rx.FindAllIndex(text, -1)
}
return makeSelection(matches)
}
var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`)
// RangeSelection computes the Selection for a text range described
// by the argument str; the range description must match the selRx
// regular expression.
func RangeSelection(str string) Selection {
m := selRx.FindStringSubmatch(str)
if len(m) >= 2 {
from, _ := strconv.Atoi(m[1])
to, _ := strconv.Atoi(m[2])
if from < to {
return makeSelection([][]int{{from, to}})
}
}
return nil
}
// Span tags for all the possible selection combinations that may
// be generated by FormatText. Selections are indicated by a bitset,
// and the value of the bitset specifies the tag to be used.
//
// bit 0: comments
// bit 1: highlights
// bit 2: selections
//
var startTags = [][]byte{
/* 000 */ []byte(``),
/* 001 */ []byte(`<span class="comment">`),
/* 010 */ []byte(`<span class="highlight">`),
/* 011 */ []byte(`<span class="highlight-comment">`),
/* 100 */ []byte(`<span class="selection">`),
/* 101 */ []byte(`<span class="selection-comment">`),
/* 110 */ []byte(`<span class="selection-highlight">`),
/* 111 */ []byte(`<span class="selection-highlight-comment">`),
}
var endTag = []byte(`</span>`)
func selectionTag(w io.Writer, text []byte, selections int) {
if selections < len(startTags) {
if tag := startTags[selections]; len(tag) > 0 {
w.Write(tag)
template.HTMLEscape(w, text)
w.Write(endTag)
return
}
}
template.HTMLEscape(w, text)
}
// FormatText HTML-escapes text and writes it to w.
// Consecutive text segments are wrapped in HTML spans (with tags as
// defined by startTags and endTag) as follows:
//
// - if line >= 0, line number (ln) spans are inserted before each line,
// starting with the value of line
// - if the text is Go source, comments get the "comment" span class
// - each occurrence of the regular expression pattern gets the "highlight"
// span class
// - text segments covered by selection get the "selection" span class
//
// Comments, highlights, and selections may overlap arbitrarily; the respective
// HTML span classes are specified in the startTags variable.
//
func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) {
var comments, highlights Selection
if goSource {
comments = tokenSelection(text, token.COMMENT)
}
if pattern != "" {
highlights = regexpSelection(text, pattern)
}
if line >= 0 || comments != nil || highlights != nil || selection != nil {
var lineTag LinkWriter
if line >= 0 {
lineTag = func(w io.Writer, _ int, start bool) {
if start {
fmt.Fprintf(w, "<span id=\"L%d\" class=\"ln\">%6d</span>\t", line, line)
line++
}
}
}
FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
} else {
template.HTMLEscape(w, text)
}
}

914
vendor/golang.org/x/tools/godoc/godoc.go generated vendored Normal file
View File

@@ -0,0 +1,914 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package godoc is a work-in-progress (2013-07-17) package to
// begin splitting up the godoc binary into multiple pieces.
//
// This package comment will evolve over time as this package splits
// into smaller pieces.
package godoc // import "golang.org/x/tools/godoc"
import (
"bytes"
"fmt"
"go/ast"
"go/doc"
"go/format"
"go/printer"
"go/token"
htmltemplate "html/template"
"io"
"log"
"os"
pathpkg "path"
"regexp"
"strconv"
"strings"
"text/template"
"time"
"unicode"
"unicode/utf8"
)
// Fake relative package path for built-ins. Documentation for all globals
// (not just exported ones) will be shown for packages in this directory.
const builtinPkgPath = "builtin"
// FuncMap defines template functions used in godoc templates.
//
// Convention: template function names ending in "_html" or "_url" produce
// HTML- or URL-escaped strings; all other function results may
// require explicit escaping in the template.
func (p *Presentation) FuncMap() template.FuncMap {
p.initFuncMapOnce.Do(p.initFuncMap)
return p.funcMap
}
func (p *Presentation) TemplateFuncs() template.FuncMap {
p.initFuncMapOnce.Do(p.initFuncMap)
return p.templateFuncs
}
func (p *Presentation) initFuncMap() {
if p.Corpus == nil {
panic("nil Presentation.Corpus")
}
p.templateFuncs = template.FuncMap{
"code": p.code,
}
p.funcMap = template.FuncMap{
// various helpers
"filename": filenameFunc,
"repeat": strings.Repeat,
// access to FileInfos (directory listings)
"fileInfoName": fileInfoNameFunc,
"fileInfoTime": fileInfoTimeFunc,
// access to search result information
"infoKind_html": infoKind_htmlFunc,
"infoLine": p.infoLineFunc,
"infoSnippet_html": p.infoSnippet_htmlFunc,
// formatting of AST nodes
"node": p.nodeFunc,
"node_html": p.node_htmlFunc,
"comment_html": comment_htmlFunc,
"comment_text": comment_textFunc,
"sanitize": sanitizeFunc,
// support for URL attributes
"pkgLink": pkgLinkFunc,
"srcLink": srcLinkFunc,
"posLink_url": newPosLink_urlFunc(srcPosLinkFunc),
"docLink": docLinkFunc,
"queryLink": queryLinkFunc,
"srcBreadcrumb": srcBreadcrumbFunc,
"srcToPkgLink": srcToPkgLinkFunc,
// formatting of Examples
"example_html": p.example_htmlFunc,
"example_text": p.example_textFunc,
"example_name": p.example_nameFunc,
"example_suffix": p.example_suffixFunc,
// formatting of analysis information
"callgraph_html": p.callgraph_htmlFunc,
"implements_html": p.implements_htmlFunc,
"methodset_html": p.methodset_htmlFunc,
// formatting of Notes
"noteTitle": noteTitle,
// Number operation
"multiply": multiply,
// formatting of PageInfoMode query string
"modeQueryString": modeQueryString,
// check whether to display third party section or not
"hasThirdParty": hasThirdParty,
}
if p.URLForSrc != nil {
p.funcMap["srcLink"] = p.URLForSrc
}
if p.URLForSrcPos != nil {
p.funcMap["posLink_url"] = newPosLink_urlFunc(p.URLForSrcPos)
}
if p.URLForSrcQuery != nil {
p.funcMap["queryLink"] = p.URLForSrcQuery
}
}
func multiply(a, b int) int { return a * b }
func filenameFunc(path string) string {
_, localname := pathpkg.Split(path)
return localname
}
func fileInfoNameFunc(fi os.FileInfo) string {
name := fi.Name()
if fi.IsDir() {
name += "/"
}
return name
}
func fileInfoTimeFunc(fi os.FileInfo) string {
if t := fi.ModTime(); t.Unix() != 0 {
return t.Local().String()
}
return "" // don't return epoch if time is obviously not set
}
// The strings in infoKinds must be properly html-escaped.
var infoKinds = [nKinds]string{
PackageClause: "package&nbsp;clause",
ImportDecl: "import&nbsp;decl",
ConstDecl: "const&nbsp;decl",
TypeDecl: "type&nbsp;decl",
VarDecl: "var&nbsp;decl",
FuncDecl: "func&nbsp;decl",
MethodDecl: "method&nbsp;decl",
Use: "use",
}
func infoKind_htmlFunc(info SpotInfo) string {
return infoKinds[info.Kind()] // infoKind entries are html-escaped
}
func (p *Presentation) infoLineFunc(info SpotInfo) int {
line := info.Lori()
if info.IsIndex() {
index, _ := p.Corpus.searchIndex.Get()
if index != nil {
line = index.(*Index).Snippet(line).Line
} else {
// no line information available because
// we don't have an index - this should
// never happen; be conservative and don't
// crash
line = 0
}
}
return line
}
func (p *Presentation) infoSnippet_htmlFunc(info SpotInfo) string {
if info.IsIndex() {
index, _ := p.Corpus.searchIndex.Get()
// Snippet.Text was HTML-escaped when it was generated
return index.(*Index).Snippet(info.Lori()).Text
}
return `<span class="alert">no snippet text available</span>`
}
func (p *Presentation) nodeFunc(info *PageInfo, node interface{}) string {
var buf bytes.Buffer
p.writeNode(&buf, info.FSet, node)
return buf.String()
}
func (p *Presentation) node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string {
var buf1 bytes.Buffer
p.writeNode(&buf1, info.FSet, node)
var buf2 bytes.Buffer
if n, _ := node.(ast.Node); n != nil && linkify && p.DeclLinks {
LinkifyText(&buf2, buf1.Bytes(), n)
if st, name := isStructTypeDecl(n); st != nil {
addStructFieldIDAttributes(&buf2, name, st)
}
} else {
FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
}
return buf2.String()
}
// isStructTypeDecl checks whether n is a struct declaration.
// It either returns a non-nil StructType and its name, or zero values.
func isStructTypeDecl(n ast.Node) (st *ast.StructType, name string) {
gd, ok := n.(*ast.GenDecl)
if !ok || gd.Tok != token.TYPE {
return nil, ""
}
if gd.Lparen > 0 {
// Parenthesized type. Who does that, anyway?
// TODO: Reportedly gri does. Fix this to handle that too.
return nil, ""
}
if len(gd.Specs) != 1 {
return nil, ""
}
ts, ok := gd.Specs[0].(*ast.TypeSpec)
if !ok {
return nil, ""
}
st, ok = ts.Type.(*ast.StructType)
if !ok {
return nil, ""
}
return st, ts.Name.Name
}
// addStructFieldIDAttributes modifies the contents of buf such that
// all struct fields of the named struct have <span id='name.Field'>
// in them, so people can link to /#Struct.Field.
func addStructFieldIDAttributes(buf *bytes.Buffer, name string, st *ast.StructType) {
if st.Fields == nil {
return
}
// needsLink is a set of identifiers that still need to be
// linked, where value == key, to avoid an allocation in func
// linkedField.
needsLink := make(map[string]string)
for _, f := range st.Fields.List {
if len(f.Names) == 0 {
continue
}
fieldName := f.Names[0].Name
needsLink[fieldName] = fieldName
}
var newBuf bytes.Buffer
foreachLine(buf.Bytes(), func(line []byte) {
if fieldName := linkedField(line, needsLink); fieldName != "" {
fmt.Fprintf(&newBuf, `<span id="%s.%s"></span>`, name, fieldName)
delete(needsLink, fieldName)
}
newBuf.Write(line)
})
buf.Reset()
buf.Write(newBuf.Bytes())
}
// foreachLine calls fn for each line of in, where a line includes
// the trailing "\n", except on the last line, if it doesn't exist.
func foreachLine(in []byte, fn func(line []byte)) {
for len(in) > 0 {
nl := bytes.IndexByte(in, '\n')
if nl == -1 {
fn(in)
return
}
fn(in[:nl+1])
in = in[nl+1:]
}
}
// commentPrefix is the line prefix for comments after they've been HTMLified.
var commentPrefix = []byte(`<span class="comment">// `)
// linkedField determines whether the given line starts with an
// identifer in the provided ids map (mapping from identifier to the
// same identifier). The line can start with either an identifier or
// an identifier in a comment. If one matches, it returns the
// identifier that matched. Otherwise it returns the empty string.
func linkedField(line []byte, ids map[string]string) string {
line = bytes.TrimSpace(line)
// For fields with a doc string of the
// conventional form, we put the new span into
// the comment instead of the field.
// The "conventional" form is a complete sentence
// per https://golang.org/s/style#comment-sentences like:
//
// // Foo is an optional Fooer to foo the foos.
// Foo Fooer
//
// In this case, we want the #StructName.Foo
// link to make the browser go to the comment
// line "Foo is an optional Fooer" instead of
// the "Foo Fooer" line, which could otherwise
// obscure the docs above the browser's "fold".
//
// TODO: do this better, so it works for all
// comments, including unconventional ones.
if bytes.HasPrefix(line, commentPrefix) {
line = line[len(commentPrefix):]
}
id := scanIdentifier(line)
if len(id) == 0 {
// No leading identifier. Avoid map lookup for
// somewhat common case.
return ""
}
return ids[string(id)]
}
// scanIdentifier scans a valid Go identifier off the front of v and
// either returns a subslice of v if there's a valid identifier, or
// returns a zero-length slice.
func scanIdentifier(v []byte) []byte {
var n int // number of leading bytes of v belonging to an identifier
for {
r, width := utf8.DecodeRune(v[n:])
if !(isLetter(r) || n > 0 && isDigit(r)) {
break
}
n += width
}
return v[:n]
}
func isLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
}
func comment_htmlFunc(comment string) string {
var buf bytes.Buffer
// TODO(gri) Provide list of words (e.g. function parameters)
// to be emphasized by ToHTML.
doc.ToHTML(&buf, comment, nil) // does html-escaping
return buf.String()
}
// punchCardWidth is the number of columns of fixed-width
// characters to assume when wrapping text. Very few people
// use terminals or cards smaller than 80 characters, so 80 it is.
// We do not try to sniff the environment or the tty to adapt to
// the situation; instead, by using a constant we make sure that
// godoc always produces the same output regardless of context,
// a consistency that is lost otherwise. For example, if we sniffed
// the environment or tty, then http://golang.org/pkg/math/?m=text
// would depend on the width of the terminal where godoc started,
// which is clearly bogus. More generally, the Unix tools that behave
// differently when writing to a tty than when writing to a file have
// a history of causing confusion (compare `ls` and `ls | cat`), and we
// want to avoid that mistake here.
const punchCardWidth = 80
func containsOnlySpace(buf []byte) bool {
isNotSpace := func(r rune) bool { return !unicode.IsSpace(r) }
return bytes.IndexFunc(buf, isNotSpace) == -1
}
func comment_textFunc(comment, indent, preIndent string) string {
var buf bytes.Buffer
doc.ToText(&buf, comment, indent, preIndent, punchCardWidth-2*len(indent))
if containsOnlySpace(buf.Bytes()) {
return ""
}
return buf.String()
}
// sanitizeFunc sanitizes the argument src by replacing newlines with
// blanks, removing extra blanks, and by removing trailing whitespace
// and commas before closing parentheses.
func sanitizeFunc(src string) string {
buf := make([]byte, len(src))
j := 0 // buf index
comma := -1 // comma index if >= 0
for i := 0; i < len(src); i++ {
ch := src[i]
switch ch {
case '\t', '\n', ' ':
// ignore whitespace at the beginning, after a blank, or after opening parentheses
if j == 0 {
continue
}
if p := buf[j-1]; p == ' ' || p == '(' || p == '{' || p == '[' {
continue
}
// replace all whitespace with blanks
ch = ' '
case ',':
comma = j
case ')', '}', ']':
// remove any trailing comma
if comma >= 0 {
j = comma
}
// remove any trailing whitespace
if j > 0 && buf[j-1] == ' ' {
j--
}
default:
comma = -1
}
buf[j] = ch
j++
}
// remove trailing blank, if any
if j > 0 && buf[j-1] == ' ' {
j--
}
return string(buf[:j])
}
type PageInfo struct {
Dirname string // directory containing the package
Err error // error or nil
GoogleCN bool // page is being served from golang.google.cn
Mode PageInfoMode // display metadata from query string
// package info
FSet *token.FileSet // nil if no package documentation
PDoc *doc.Package // nil if no package documentation
Examples []*doc.Example // nil if no example code
Notes map[string][]*doc.Note // nil if no package Notes
PAst map[string]*ast.File // nil if no AST with package exports
IsMain bool // true for package main
IsFiltered bool // true if results were filtered
// analysis info
TypeInfoIndex map[string]int // index of JSON datum for type T (if -analysis=type)
AnalysisData htmltemplate.JS // array of TypeInfoJSON values
CallGraph htmltemplate.JS // array of PCGNodeJSON values (if -analysis=pointer)
CallGraphIndex map[string]int // maps func name to index in CallGraph
// directory info
Dirs *DirList // nil if no directory information
DirTime time.Time // directory time stamp
DirFlat bool // if set, show directory in a flat (non-indented) manner
}
func (info *PageInfo) IsEmpty() bool {
return info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil
}
func pkgLinkFunc(path string) string {
// because of the irregular mapping under goroot
// we need to correct certain relative paths
path = strings.TrimPrefix(path, "/")
path = strings.TrimPrefix(path, "src/")
path = strings.TrimPrefix(path, "pkg/")
return "pkg/" + path
}
// srcToPkgLinkFunc builds an <a> tag linking to the package
// documentation of relpath.
func srcToPkgLinkFunc(relpath string) string {
relpath = pkgLinkFunc(relpath)
relpath = pathpkg.Dir(relpath)
if relpath == "pkg" {
return `<a href="/pkg">Index</a>`
}
return fmt.Sprintf(`<a href="/%s">%s</a>`, relpath, relpath[len("pkg/"):])
}
// srcBreadcrumbFun converts each segment of relpath to a HTML <a>.
// Each segment links to its corresponding src directories.
func srcBreadcrumbFunc(relpath string) string {
segments := strings.Split(relpath, "/")
var buf bytes.Buffer
var selectedSegment string
var selectedIndex int
if strings.HasSuffix(relpath, "/") {
// relpath is a directory ending with a "/".
// Selected segment is the segment before the last slash.
selectedIndex = len(segments) - 2
selectedSegment = segments[selectedIndex] + "/"
} else {
selectedIndex = len(segments) - 1
selectedSegment = segments[selectedIndex]
}
for i := range segments[:selectedIndex] {
buf.WriteString(fmt.Sprintf(`<a href="/%s">%s</a>/`,
strings.Join(segments[:i+1], "/"),
segments[i],
))
}
buf.WriteString(`<span class="text-muted">`)
buf.WriteString(selectedSegment)
buf.WriteString(`</span>`)
return buf.String()
}
func newPosLink_urlFunc(srcPosLinkFunc func(s string, line, low, high int) string) func(info *PageInfo, n interface{}) string {
// n must be an ast.Node or a *doc.Note
return func(info *PageInfo, n interface{}) string {
var pos, end token.Pos
switch n := n.(type) {
case ast.Node:
pos = n.Pos()
end = n.End()
case *doc.Note:
pos = n.Pos
end = n.End
default:
panic(fmt.Sprintf("wrong type for posLink_url template formatter: %T", n))
}
var relpath string
var line int
var low, high int // selection offset range
if pos.IsValid() {
p := info.FSet.Position(pos)
relpath = p.Filename
line = p.Line
low = p.Offset
}
if end.IsValid() {
high = info.FSet.Position(end).Offset
}
return srcPosLinkFunc(relpath, line, low, high)
}
}
func srcPosLinkFunc(s string, line, low, high int) string {
s = srcLinkFunc(s)
var buf bytes.Buffer
template.HTMLEscape(&buf, []byte(s))
// selection ranges are of form "s=low:high"
if low < high {
fmt.Fprintf(&buf, "?s=%d:%d", low, high) // no need for URL escaping
// if we have a selection, position the page
// such that the selection is a bit below the top
line -= 10
if line < 1 {
line = 1
}
}
// line id's in html-printed source are of the
// form "L%d" where %d stands for the line number
if line > 0 {
fmt.Fprintf(&buf, "#L%d", line) // no need for URL escaping
}
return buf.String()
}
func srcLinkFunc(s string) string {
s = pathpkg.Clean("/" + s)
if !strings.HasPrefix(s, "/src/") {
s = "/src" + s
}
return s
}
// queryLinkFunc returns a URL for a line in a source file with a highlighted
// query term.
// s is expected to be a path to a source file.
// query is expected to be a string that has already been appropriately escaped
// for use in a URL query.
func queryLinkFunc(s, query string, line int) string {
url := pathpkg.Clean("/"+s) + "?h=" + query
if line > 0 {
url += "#L" + strconv.Itoa(line)
}
return url
}
func docLinkFunc(s string, ident string) string {
return pathpkg.Clean("/pkg/"+s) + "/#" + ident
}
func (p *Presentation) example_textFunc(info *PageInfo, funcName, indent string) string {
if !p.ShowExamples {
return ""
}
var buf bytes.Buffer
first := true
for _, eg := range info.Examples {
name := stripExampleSuffix(eg.Name)
if name != funcName {
continue
}
if !first {
buf.WriteString("\n")
}
first = false
// print code
cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: p.TabWidth}
var buf1 bytes.Buffer
config.Fprint(&buf1, info.FSet, cnode)
code := buf1.String()
// Additional formatting if this is a function body. Unfortunately, we
// can't print statements individually because we would lose comments
// on later statements.
if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
// remove surrounding braces
code = code[1 : n-1]
// unindent
code = replaceLeadingIndentation(code, strings.Repeat(" ", p.TabWidth), indent)
}
code = strings.Trim(code, "\n")
buf.WriteString(indent)
buf.WriteString("Example:\n")
buf.WriteString(code)
buf.WriteString("\n\n")
}
return buf.String()
}
func (p *Presentation) example_htmlFunc(info *PageInfo, funcName string) string {
var buf bytes.Buffer
for _, eg := range info.Examples {
name := stripExampleSuffix(eg.Name)
if name != funcName {
continue
}
// print code
cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
code := p.node_htmlFunc(info, cnode, true)
out := eg.Output
wholeFile := true
// Additional formatting if this is a function body.
if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
wholeFile = false
// remove surrounding braces
code = code[1 : n-1]
// unindent
code = replaceLeadingIndentation(code, strings.Repeat(" ", p.TabWidth), "")
// remove output comment
if loc := exampleOutputRx.FindStringIndex(code); loc != nil {
code = strings.TrimSpace(code[:loc[0]])
}
}
// Write out the playground code in standard Go style
// (use tabs, no comment highlight, etc).
play := ""
if eg.Play != nil && p.ShowPlayground {
var buf bytes.Buffer
if err := format.Node(&buf, info.FSet, eg.Play); err != nil {
log.Print(err)
} else {
play = buf.String()
}
}
// Drop output, as the output comment will appear in the code.
if wholeFile && play == "" {
out = ""
}
if p.ExampleHTML == nil {
out = ""
return ""
}
err := p.ExampleHTML.Execute(&buf, struct {
Name, Doc, Code, Play, Output string
GoogleCN bool
}{eg.Name, eg.Doc, code, play, out, info.GoogleCN})
if err != nil {
log.Print(err)
}
}
return buf.String()
}
// example_nameFunc takes an example function name and returns its display
// name. For example, "Foo_Bar_quux" becomes "Foo.Bar (Quux)".
func (p *Presentation) example_nameFunc(s string) string {
name, suffix := splitExampleName(s)
// replace _ with . for method names
name = strings.Replace(name, "_", ".", 1)
// use "Package" if no name provided
if name == "" {
name = "Package"
}
return name + suffix
}
// example_suffixFunc takes an example function name and returns its suffix in
// parenthesized form. For example, "Foo_Bar_quux" becomes " (Quux)".
func (p *Presentation) example_suffixFunc(name string) string {
_, suffix := splitExampleName(name)
return suffix
}
// implements_html returns the "> Implements" toggle for a package-level named type.
// Its contents are populated from JSON data by client-side JS at load time.
func (p *Presentation) implements_htmlFunc(info *PageInfo, typeName string) string {
if p.ImplementsHTML == nil {
return ""
}
index, ok := info.TypeInfoIndex[typeName]
if !ok {
return ""
}
var buf bytes.Buffer
err := p.ImplementsHTML.Execute(&buf, struct{ Index int }{index})
if err != nil {
log.Print(err)
}
return buf.String()
}
// methodset_html returns the "> Method set" toggle for a package-level named type.
// Its contents are populated from JSON data by client-side JS at load time.
func (p *Presentation) methodset_htmlFunc(info *PageInfo, typeName string) string {
if p.MethodSetHTML == nil {
return ""
}
index, ok := info.TypeInfoIndex[typeName]
if !ok {
return ""
}
var buf bytes.Buffer
err := p.MethodSetHTML.Execute(&buf, struct{ Index int }{index})
if err != nil {
log.Print(err)
}
return buf.String()
}
// callgraph_html returns the "> Call graph" toggle for a package-level func.
// Its contents are populated from JSON data by client-side JS at load time.
func (p *Presentation) callgraph_htmlFunc(info *PageInfo, recv, name string) string {
if p.CallGraphHTML == nil {
return ""
}
if recv != "" {
// Format must match (*ssa.Function).RelString().
name = fmt.Sprintf("(%s).%s", recv, name)
}
index, ok := info.CallGraphIndex[name]
if !ok {
return ""
}
var buf bytes.Buffer
err := p.CallGraphHTML.Execute(&buf, struct{ Index int }{index})
if err != nil {
log.Print(err)
}
return buf.String()
}
func noteTitle(note string) string {
return strings.Title(strings.ToLower(note))
}
func startsWithUppercase(s string) bool {
r, _ := utf8.DecodeRuneInString(s)
return unicode.IsUpper(r)
}
var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*(unordered )?output:`)
// stripExampleSuffix strips lowercase braz in Foo_braz or Foo_Bar_braz from name
// while keeping uppercase Braz in Foo_Braz.
func stripExampleSuffix(name string) string {
if i := strings.LastIndex(name, "_"); i != -1 {
if i < len(name)-1 && !startsWithUppercase(name[i+1:]) {
name = name[:i]
}
}
return name
}
func splitExampleName(s string) (name, suffix string) {
i := strings.LastIndex(s, "_")
if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) {
name = s[:i]
suffix = " (" + strings.Title(s[i+1:]) + ")"
return
}
name = s
return
}
// replaceLeadingIndentation replaces oldIndent at the beginning of each line
// with newIndent. This is used for formatting examples. Raw strings that
// span multiple lines are handled specially: oldIndent is not removed (since
// go/printer will not add any indentation there), but newIndent is added
// (since we may still want leading indentation).
func replaceLeadingIndentation(body, oldIndent, newIndent string) string {
// Handle indent at the beginning of the first line. After this, we handle
// indentation only after a newline.
var buf bytes.Buffer
if strings.HasPrefix(body, oldIndent) {
buf.WriteString(newIndent)
body = body[len(oldIndent):]
}
// Use a state machine to keep track of whether we're in a string or
// rune literal while we process the rest of the code.
const (
codeState = iota
runeState
interpretedStringState
rawStringState
)
searchChars := []string{
"'\"`\n", // codeState
`\'`, // runeState
`\"`, // interpretedStringState
"`\n", // rawStringState
// newlineState does not need to search
}
state := codeState
for {
i := strings.IndexAny(body, searchChars[state])
if i < 0 {
buf.WriteString(body)
break
}
c := body[i]
buf.WriteString(body[:i+1])
body = body[i+1:]
switch state {
case codeState:
switch c {
case '\'':
state = runeState
case '"':
state = interpretedStringState
case '`':
state = rawStringState
case '\n':
if strings.HasPrefix(body, oldIndent) {
buf.WriteString(newIndent)
body = body[len(oldIndent):]
}
}
case runeState:
switch c {
case '\\':
r, size := utf8.DecodeRuneInString(body)
buf.WriteRune(r)
body = body[size:]
case '\'':
state = codeState
}
case interpretedStringState:
switch c {
case '\\':
r, size := utf8.DecodeRuneInString(body)
buf.WriteRune(r)
body = body[size:]
case '"':
state = codeState
}
case rawStringState:
switch c {
case '`':
state = codeState
case '\n':
buf.WriteString(newIndent)
}
}
}
return buf.String()
}
// Write an AST node to w.
func (p *Presentation) writeNode(w io.Writer, fset *token.FileSet, x interface{}) {
// 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)
//
// 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)
mode := printer.TabIndent | printer.UseSpaces
err := (&printer.Config{Mode: mode, Tabwidth: p.TabWidth}).Fprint(&tconv{p: p, output: w}, fset, x)
if err != nil {
log.Print(err)
}
}
// WriteNode writes x to w.
// TODO(bgarcia) Is this method needed? It's just a wrapper for p.writeNode.
func (p *Presentation) WriteNode(w io.Writer, fset *token.FileSet, x interface{}) {
p.writeNode(w, fset, x)
}

35
vendor/golang.org/x/tools/godoc/godoc17_test.go generated vendored Normal file
View File

@@ -0,0 +1,35 @@
// Copyright 2017 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.
// +build go1.7
package godoc
import (
"bytes"
"fmt"
"testing"
)
// Verify that scanIdentifier isn't quadratic.
// This doesn't actually measure and fail on its own, but it was previously
// very obvious when running by hand.
//
// TODO: if there's a reliable and non-flaky way to test this, do so.
// Maybe count user CPU time instead of wall time? But that's not easy
// to do portably in Go.
func TestStructField(t *testing.T) {
for _, n := range []int{10, 100, 1000, 10000} {
n := n
t.Run(fmt.Sprint(n), func(t *testing.T) {
var buf bytes.Buffer
fmt.Fprintf(&buf, "package foo\n\ntype T struct {\n")
for i := 0; i < n; i++ {
fmt.Fprintf(&buf, "\t// Field%d is foo.\n\tField%d int\n\n", i, i)
}
fmt.Fprintf(&buf, "}\n")
linkifySource(t, buf.Bytes())
})
}
}

323
vendor/golang.org/x/tools/godoc/godoc_test.go generated vendored Normal file
View File

@@ -0,0 +1,323 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
import (
"bytes"
"go/parser"
"go/token"
"strings"
"testing"
)
func TestPkgLinkFunc(t *testing.T) {
for _, tc := range []struct {
path string
want string
}{
{"/src/fmt", "pkg/fmt"},
{"src/fmt", "pkg/fmt"},
{"/fmt", "pkg/fmt"},
{"fmt", "pkg/fmt"},
} {
if got := pkgLinkFunc(tc.path); got != tc.want {
t.Errorf("pkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
}
}
}
func TestSrcPosLinkFunc(t *testing.T) {
for _, tc := range []struct {
src string
line int
low int
high int
want string
}{
{"/src/fmt/print.go", 42, 30, 50, "/src/fmt/print.go?s=30:50#L32"},
{"/src/fmt/print.go", 2, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
{"/src/fmt/print.go", 2, 0, 0, "/src/fmt/print.go#L2"},
{"/src/fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
{"/src/fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
{"fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
{"fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
} {
if got := srcPosLinkFunc(tc.src, tc.line, tc.low, tc.high); got != tc.want {
t.Errorf("srcLinkFunc(%v, %v, %v, %v) = %v; want %v", tc.src, tc.line, tc.low, tc.high, got, tc.want)
}
}
}
func TestSrcLinkFunc(t *testing.T) {
for _, tc := range []struct {
src string
want string
}{
{"/src/fmt/print.go", "/src/fmt/print.go"},
{"src/fmt/print.go", "/src/fmt/print.go"},
{"/fmt/print.go", "/src/fmt/print.go"},
{"fmt/print.go", "/src/fmt/print.go"},
} {
if got := srcLinkFunc(tc.src); got != tc.want {
t.Errorf("srcLinkFunc(%v) = %v; want %v", tc.src, got, tc.want)
}
}
}
func TestQueryLinkFunc(t *testing.T) {
for _, tc := range []struct {
src string
query string
line int
want string
}{
{"/src/fmt/print.go", "Sprintf", 33, "/src/fmt/print.go?h=Sprintf#L33"},
{"/src/fmt/print.go", "Sprintf", 0, "/src/fmt/print.go?h=Sprintf"},
{"src/fmt/print.go", "EOF", 33, "/src/fmt/print.go?h=EOF#L33"},
{"src/fmt/print.go", "a%3f+%26b", 1, "/src/fmt/print.go?h=a%3f+%26b#L1"},
} {
if got := queryLinkFunc(tc.src, tc.query, tc.line); got != tc.want {
t.Errorf("queryLinkFunc(%v, %v, %v) = %v; want %v", tc.src, tc.query, tc.line, got, tc.want)
}
}
}
func TestDocLinkFunc(t *testing.T) {
for _, tc := range []struct {
src string
ident string
want string
}{
{"fmt", "Sprintf", "/pkg/fmt/#Sprintf"},
{"fmt", "EOF", "/pkg/fmt/#EOF"},
} {
if got := docLinkFunc(tc.src, tc.ident); got != tc.want {
t.Errorf("docLinkFunc(%v, %v) = %v; want %v", tc.src, tc.ident, got, tc.want)
}
}
}
func TestSanitizeFunc(t *testing.T) {
for _, tc := range []struct {
src string
want string
}{
{},
{"foo", "foo"},
{"func f()", "func f()"},
{"func f(a int,)", "func f(a int)"},
{"func f(a int,\n)", "func f(a int)"},
{"func f(\n\ta int,\n\tb int,\n\tc int,\n)", "func f(a int, b int, c int)"},
{" ( a, b, c ) ", "(a, b, c)"},
{"( a, b, c int, foo bar , )", "(a, b, c int, foo bar)"},
{"{ a, b}", "{a, b}"},
{"[ a, b]", "[a, b]"},
} {
if got := sanitizeFunc(tc.src); got != tc.want {
t.Errorf("sanitizeFunc(%v) = %v; want %v", tc.src, got, tc.want)
}
}
}
// Test that we add <span id="StructName.FieldName"> elements
// to the HTML of struct fields.
func TestStructFieldsIDAttributes(t *testing.T) {
got := linkifySource(t, []byte(`
package foo
type T struct {
NoDoc string
// Doc has a comment.
Doc string
// Opt, if non-nil, is an option.
Opt *int
// Опция - другое поле.
Опция bool
}
`))
want := `type T struct {
<span id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a>
<span id="T.Doc"></span><span class="comment">// Doc has a comment.</span>
Doc <a href="/pkg/builtin/#string">string</a>
<span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span>
Opt *<a href="/pkg/builtin/#int">int</a>
<span id="T.Опция"></span><span class="comment">// Опция - другое поле.</span>
Опция <a href="/pkg/builtin/#bool">bool</a>
}`
if got != want {
t.Errorf("got: %s\n\nwant: %s\n", got, want)
}
}
// Test that we add <span id="ConstName"> elements to the HTML
// of definitions in const and var specs.
func TestValueSpecIDAttributes(t *testing.T) {
got := linkifySource(t, []byte(`
package foo
const (
NoDoc string = "NoDoc"
// Doc has a comment
Doc = "Doc"
NoVal
)`))
want := `const (
<span id="NoDoc">NoDoc</span> <a href="/pkg/builtin/#string">string</a> = &#34;NoDoc&#34;
<span class="comment">// Doc has a comment</span>
<span id="Doc">Doc</span> = &#34;Doc&#34;
<span id="NoVal">NoVal</span>
)`
if got != want {
t.Errorf("got: %s\n\nwant: %s\n", got, want)
}
}
func TestCompositeLitLinkFields(t *testing.T) {
got := linkifySource(t, []byte(`
package foo
type T struct {
X int
}
var S T = T{X: 12}`))
want := `type T struct {
<span id="T.X"></span>X <a href="/pkg/builtin/#int">int</a>
}
var <span id="S">S</span> <a href="#T">T</a> = <a href="#T">T</a>{<a href="#T.X">X</a>: 12}`
if got != want {
t.Errorf("got: %s\n\nwant: %s\n", got, want)
}
}
func TestFuncDeclNotLink(t *testing.T) {
// Function.
got := linkifySource(t, []byte(`
package http
func Get(url string) (resp *Response, err error)`))
want := `func Get(url <a href="/pkg/builtin/#string">string</a>) (resp *<a href="#Response">Response</a>, err <a href="/pkg/builtin/#error">error</a>)`
if got != want {
t.Errorf("got: %s\n\nwant: %s\n", got, want)
}
// Method.
got = linkifySource(t, []byte(`
package http
func (h Header) Get(key string) string`))
want = `func (h <a href="#Header">Header</a>) Get(key <a href="/pkg/builtin/#string">string</a>) <a href="/pkg/builtin/#string">string</a>`
if got != want {
t.Errorf("got: %s\n\nwant: %s\n", got, want)
}
}
func linkifySource(t *testing.T, src []byte) string {
p := &Presentation{
DeclLinks: true,
}
fset := token.NewFileSet()
af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
pi := &PageInfo{
FSet: fset,
}
sep := ""
for _, decl := range af.Decls {
buf.WriteString(sep)
sep = "\n"
buf.WriteString(p.node_htmlFunc(pi, decl, true))
}
return buf.String()
}
func TestScanIdentifier(t *testing.T) {
tests := []struct {
in, want string
}{
{"foo bar", "foo"},
{"foo/bar", "foo"},
{" foo", ""},
{"фоо", "фоо"},
{"f123", "f123"},
{"123f", ""},
}
for _, tt := range tests {
got := scanIdentifier([]byte(tt.in))
if string(got) != tt.want {
t.Errorf("scanIdentifier(%q) = %q; want %q", tt.in, got, tt.want)
}
}
}
func TestReplaceLeadingIndentation(t *testing.T) {
oldIndent := strings.Repeat(" ", 2)
newIndent := strings.Repeat(" ", 4)
tests := []struct {
src, want string
}{
{" foo\n bar\n baz", " foo\n bar\n baz"},
{" '`'\n '`'\n", " '`'\n '`'\n"},
{" '\\''\n '`'\n", " '\\''\n '`'\n"},
{" \"`\"\n \"`\"\n", " \"`\"\n \"`\"\n"},
{" `foo\n bar`", " `foo\n bar`"},
{" `foo\\`\n bar", " `foo\\`\n bar"},
{" '\\`'`foo\n bar", " '\\`'`foo\n bar"},
{
" if true {\n foo := `One\n \tTwo\nThree`\n }\n",
" if true {\n foo := `One\n \tTwo\n Three`\n }\n",
},
}
for _, tc := range tests {
if got := replaceLeadingIndentation(tc.src, oldIndent, newIndent); got != tc.want {
t.Errorf("replaceLeadingIndentation:\n%v\n---\nhave:\n%v\n---\nwant:\n%v\n",
tc.src, got, tc.want)
}
}
}
func TestSrcBreadcrumbFunc(t *testing.T) {
for _, tc := range []struct {
path string
want string
}{
{"src/", `<span class="text-muted">src/</span>`},
{"src/fmt/", `<a href="/src">src</a>/<span class="text-muted">fmt/</span>`},
{"src/fmt/print.go", `<a href="/src">src</a>/<a href="/src/fmt">fmt</a>/<span class="text-muted">print.go</span>`},
} {
if got := srcBreadcrumbFunc(tc.path); got != tc.want {
t.Errorf("srcBreadcrumbFunc(%v) = %v; want %v", tc.path, got, tc.want)
}
}
}
func TestSrcToPkgLinkFunc(t *testing.T) {
for _, tc := range []struct {
path string
want string
}{
{"src/", `<a href="/pkg">Index</a>`},
{"src/fmt/", `<a href="/pkg/fmt">fmt</a>`},
{"pkg/", `<a href="/pkg">Index</a>`},
{"pkg/LICENSE", `<a href="/pkg">Index</a>`},
} {
if got := srcToPkgLinkFunc(tc.path); got != tc.want {
t.Errorf("srcToPkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
}
}
}

1580
vendor/golang.org/x/tools/godoc/index.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

323
vendor/golang.org/x/tools/godoc/index_test.go generated vendored Normal file
View File

@@ -0,0 +1,323 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
import (
"bytes"
"reflect"
"sort"
"strings"
"testing"
"golang.org/x/tools/godoc/vfs/mapfs"
)
func newCorpus(t *testing.T) *Corpus {
c := NewCorpus(mapfs.New(map[string]string{
"src/foo/foo.go": `// Package foo is an example.
package foo
import "bar"
const Pi = 3.1415
var Foos []Foo
// Foo is stuff.
type Foo struct{}
func New() *Foo {
return new(Foo)
}
`,
"src/bar/bar.go": `// Package bar is another example to test races.
package bar
`,
"src/other/bar/bar.go": `// Package bar is another bar package.
package bar
func X() {}
`,
"src/skip/skip.go": `// Package skip should be skipped.
package skip
func Skip() {}
`,
"src/bar/readme.txt": `Whitelisted text file.
`,
"src/bar/baz.zzz": `Text file not whitelisted.
`,
}))
c.IndexEnabled = true
c.IndexDirectory = func(dir string) bool {
return !strings.Contains(dir, "skip")
}
if err := c.Init(); err != nil {
t.Fatal(err)
}
return c
}
func TestIndex(t *testing.T) {
for _, docs := range []bool{true, false} {
for _, goCode := range []bool{true, false} {
for _, fullText := range []bool{true, false} {
c := newCorpus(t)
c.IndexDocs = docs
c.IndexGoCode = goCode
c.IndexFullText = fullText
c.UpdateIndex()
ix, _ := c.CurrentIndex()
if ix == nil {
t.Fatal("no index")
}
t.Logf("docs, goCode, fullText = %v,%v,%v", docs, goCode, fullText)
testIndex(t, c, ix)
}
}
}
}
func TestIndexWriteRead(t *testing.T) {
type key struct {
docs, goCode, fullText bool
}
type val struct {
buf *bytes.Buffer
c *Corpus
}
m := map[key]val{}
for _, docs := range []bool{true, false} {
for _, goCode := range []bool{true, false} {
for _, fullText := range []bool{true, false} {
k := key{docs, goCode, fullText}
c := newCorpus(t)
c.IndexDocs = docs
c.IndexGoCode = goCode
c.IndexFullText = fullText
c.UpdateIndex()
ix, _ := c.CurrentIndex()
if ix == nil {
t.Fatal("no index")
}
var buf bytes.Buffer
nw, err := ix.WriteTo(&buf)
if err != nil {
t.Fatalf("Index.WriteTo: %v", err)
}
m[k] = val{bytes.NewBuffer(buf.Bytes()), c}
ix2 := new(Index)
nr, err := ix2.ReadFrom(&buf)
if err != nil {
t.Fatalf("Index.ReadFrom: %v", err)
}
if nr != nw {
t.Errorf("Wrote %d bytes to index but read %d", nw, nr)
}
testIndex(t, c, ix)
}
}
}
// Test CompatibleWith
for k1, v1 := range m {
ix := new(Index)
if _, err := ix.ReadFrom(v1.buf); err != nil {
t.Fatalf("Index.ReadFrom: %v", err)
}
for k2, v2 := range m {
if got, want := ix.CompatibleWith(v2.c), k1 == k2; got != want {
t.Errorf("CompatibleWith = %v; want %v for %v, %v", got, want, k1, k2)
}
}
}
}
func testIndex(t *testing.T, c *Corpus, ix *Index) {
if _, ok := ix.words["Skip"]; ok {
t.Errorf("the word Skip was found; expected it to be skipped")
}
checkStats(t, c, ix)
checkImportCount(t, c, ix)
checkPackagePath(t, c, ix)
checkExports(t, c, ix)
checkIdents(t, c, ix)
}
// checkStats checks the Index's statistics.
// Some statistics are only set when we're indexing Go code.
func checkStats(t *testing.T, c *Corpus, ix *Index) {
want := Statistics{}
if c.IndexFullText {
want.Bytes = 314
want.Files = 4
want.Lines = 21
} else if c.IndexDocs || c.IndexGoCode {
want.Bytes = 291
want.Files = 3
want.Lines = 20
}
if c.IndexGoCode {
want.Words = 8
want.Spots = 12
}
if got := ix.Stats(); !reflect.DeepEqual(got, want) {
t.Errorf("Stats = %#v; want %#v", got, want)
}
}
// checkImportCount checks the Index's import count map.
// It is only set when we're indexing Go code.
func checkImportCount(t *testing.T, c *Corpus, ix *Index) {
want := map[string]int{}
if c.IndexGoCode {
want = map[string]int{
"bar": 1,
}
}
if got := ix.ImportCount(); !reflect.DeepEqual(got, want) {
t.Errorf("ImportCount = %v; want %v", got, want)
}
}
// checkPackagePath checks the Index's package path map.
// It is set if at least one of the indexing options is enabled.
func checkPackagePath(t *testing.T, c *Corpus, ix *Index) {
want := map[string]map[string]bool{}
if c.IndexDocs || c.IndexGoCode || c.IndexFullText {
want = map[string]map[string]bool{
"foo": {
"foo": true,
},
"bar": {
"bar": true,
"other/bar": true,
},
}
}
if got := ix.PackagePath(); !reflect.DeepEqual(got, want) {
t.Errorf("PackagePath = %v; want %v", got, want)
}
}
// checkExports checks the Index's exports map.
// It is only set when we're indexing Go code.
func checkExports(t *testing.T, c *Corpus, ix *Index) {
want := map[string]map[string]SpotKind{}
if c.IndexGoCode {
want = map[string]map[string]SpotKind{
"foo": {
"Pi": ConstDecl,
"Foos": VarDecl,
"Foo": TypeDecl,
"New": FuncDecl,
},
"other/bar": {
"X": FuncDecl,
},
}
}
if got := ix.Exports(); !reflect.DeepEqual(got, want) {
t.Errorf("Exports = %v; want %v", got, want)
}
}
// checkIdents checks the Index's indents map.
// It is only set when we're indexing documentation.
func checkIdents(t *testing.T, c *Corpus, ix *Index) {
want := map[SpotKind]map[string][]Ident{}
if c.IndexDocs {
want = map[SpotKind]map[string][]Ident{
PackageClause: {
"bar": {
{"bar", "bar", "bar", "Package bar is another example to test races."},
{"other/bar", "bar", "bar", "Package bar is another bar package."},
},
"foo": {{"foo", "foo", "foo", "Package foo is an example."}},
"other": {{"other/bar", "bar", "bar", "Package bar is another bar package."}},
},
ConstDecl: {
"Pi": {{"foo", "foo", "Pi", ""}},
},
VarDecl: {
"Foos": {{"foo", "foo", "Foos", ""}},
},
TypeDecl: {
"Foo": {{"foo", "foo", "Foo", "Foo is stuff."}},
},
FuncDecl: {
"New": {{"foo", "foo", "New", ""}},
"X": {{"other/bar", "bar", "X", ""}},
},
}
}
if got := ix.Idents(); !reflect.DeepEqual(got, want) {
t.Errorf("Idents = %v; want %v", got, want)
}
}
func TestIdentResultSort(t *testing.T) {
ic := map[string]int{
"/a/b/pkg1": 10,
"/a/b/pkg2": 2,
"/b/d/pkg3": 20,
}
for _, tc := range []struct {
ir []Ident
exp []Ident
}{
{
ir: []Ident{
{"/a/b/pkg2", "pkg2", "MyFunc2", ""},
{"/b/d/pkg3", "pkg3", "MyFunc3", ""},
{"/a/b/pkg1", "pkg1", "MyFunc1", ""},
},
exp: []Ident{
{"/b/d/pkg3", "pkg3", "MyFunc3", ""},
{"/a/b/pkg1", "pkg1", "MyFunc1", ""},
{"/a/b/pkg2", "pkg2", "MyFunc2", ""},
},
},
{
ir: []Ident{
{"/a/a/pkg1", "pkg1", "MyFunc1", ""},
{"/a/b/pkg1", "pkg1", "MyFunc1", ""},
},
exp: []Ident{
{"/a/b/pkg1", "pkg1", "MyFunc1", ""},
{"/a/a/pkg1", "pkg1", "MyFunc1", ""},
},
},
} {
if sort.Sort(byImportCount{tc.ir, ic}); !reflect.DeepEqual(tc.ir, tc.exp) {
t.Errorf("got: %v, want %v", tc.ir, tc.exp)
}
}
}
func TestIdentFilter(t *testing.T) {
ic := map[string]int{}
for _, tc := range []struct {
ir []Ident
pak string
exp []Ident
}{
{
ir: []Ident{
{"/a/b/pkg2", "pkg2", "MyFunc2", ""},
{"/b/d/pkg3", "pkg3", "MyFunc3", ""},
{"/a/b/pkg1", "pkg1", "MyFunc1", ""},
},
pak: "pkg2",
exp: []Ident{
{"/a/b/pkg2", "pkg2", "MyFunc2", ""},
},
},
} {
res := byImportCount{tc.ir, ic}.filter(tc.pak)
if !reflect.DeepEqual(res, tc.exp) {
t.Errorf("got: %v, want %v", res, tc.exp)
}
}
}

195
vendor/golang.org/x/tools/godoc/linkify.go generated vendored Normal file
View File

@@ -0,0 +1,195 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements LinkifyText which introduces
// links for identifiers pointing to their declarations.
// The approach does not cover all cases because godoc
// doesn't have complete type information, but it's
// reasonably good for browsing.
package godoc
import (
"fmt"
"go/ast"
"go/doc"
"go/token"
"io"
"strconv"
)
// LinkifyText HTML-escapes source text and writes it to w.
// Identifiers that are in a "use" position (i.e., that are
// not being declared), are wrapped with HTML links pointing
// to the respective declaration, if possible. Comments are
// formatted the same way as with FormatText.
//
func LinkifyText(w io.Writer, text []byte, n ast.Node) {
links := linksFor(n)
i := 0 // links index
prev := "" // prev HTML tag
linkWriter := func(w io.Writer, _ int, start bool) {
// end tag
if !start {
if prev != "" {
fmt.Fprintf(w, `</%s>`, prev)
prev = ""
}
return
}
// start tag
prev = ""
if i < len(links) {
switch info := links[i]; {
case info.path != "" && info.name == "":
// package path
fmt.Fprintf(w, `<a href="/pkg/%s/">`, info.path)
prev = "a"
case info.path != "" && info.name != "":
// qualified identifier
fmt.Fprintf(w, `<a href="/pkg/%s/#%s">`, info.path, info.name)
prev = "a"
case info.path == "" && info.name != "":
// local identifier
if info.isVal {
fmt.Fprintf(w, `<span id="%s">`, info.name)
prev = "span"
} else if ast.IsExported(info.name) {
fmt.Fprintf(w, `<a href="#%s">`, info.name)
prev = "a"
}
}
i++
}
}
idents := tokenSelection(text, token.IDENT)
comments := tokenSelection(text, token.COMMENT)
FormatSelections(w, text, linkWriter, idents, selectionTag, comments)
}
// A link describes the (HTML) link information for an identifier.
// The zero value of a link represents "no link".
//
type link struct {
path, name string // package path, identifier name
isVal bool // identifier is defined in a const or var declaration
}
// linksFor returns the list of links for the identifiers used
// by node in the same order as they appear in the source.
//
func linksFor(node ast.Node) (links []link) {
// linkMap tracks link information for each ast.Ident node. Entries may
// be created out of source order (for example, when we visit a parent
// definition node). These links are appended to the returned slice when
// their ast.Ident nodes are visited.
linkMap := make(map[*ast.Ident]link)
ast.Inspect(node, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.Field:
for _, n := range n.Names {
linkMap[n] = link{}
}
case *ast.ImportSpec:
if name := n.Name; name != nil {
linkMap[name] = link{}
}
case *ast.ValueSpec:
for _, n := range n.Names {
linkMap[n] = link{name: n.Name, isVal: true}
}
case *ast.FuncDecl:
linkMap[n.Name] = link{}
case *ast.TypeSpec:
linkMap[n.Name] = link{}
case *ast.AssignStmt:
// Short variable declarations only show up if we apply
// this code to all source code (as opposed to exported
// declarations only).
if n.Tok == token.DEFINE {
// Some of the lhs variables may be re-declared,
// so technically they are not defs. We don't
// care for now.
for _, x := range n.Lhs {
// Each lhs expression should be an
// ident, but we are conservative and check.
if n, _ := x.(*ast.Ident); n != nil {
linkMap[n] = link{isVal: true}
}
}
}
case *ast.SelectorExpr:
// Detect qualified identifiers of the form pkg.ident.
// If anything fails we return true and collect individual
// identifiers instead.
if x, _ := n.X.(*ast.Ident); x != nil {
// Create links only if x is a qualified identifier.
if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
// spec.Path.Value is the import path
if path, err := strconv.Unquote(spec.Path.Value); err == nil {
// Register two links, one for the package
// and one for the qualified identifier.
linkMap[x] = link{path: path}
linkMap[n.Sel] = link{path: path, name: n.Sel.Name}
}
}
}
}
case *ast.CompositeLit:
// Detect field names within composite literals. These links should
// be prefixed by the type name.
fieldPath := ""
prefix := ""
switch typ := n.Type.(type) {
case *ast.Ident:
prefix = typ.Name + "."
case *ast.SelectorExpr:
if x, _ := typ.X.(*ast.Ident); x != nil {
// Create links only if x is a qualified identifier.
if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
// spec.Path.Value is the import path
if path, err := strconv.Unquote(spec.Path.Value); err == nil {
// Register two links, one for the package
// and one for the qualified identifier.
linkMap[x] = link{path: path}
linkMap[typ.Sel] = link{path: path, name: typ.Sel.Name}
fieldPath = path
prefix = typ.Sel.Name + "."
}
}
}
}
}
for _, e := range n.Elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
if k, ok := kv.Key.(*ast.Ident); ok {
// Note: there is some syntactic ambiguity here. We cannot determine
// if this is a struct literal or a map literal without type
// information. We assume struct literal.
name := prefix + k.Name
linkMap[k] = link{path: fieldPath, name: name}
}
}
}
case *ast.Ident:
if l, ok := linkMap[n]; ok {
links = append(links, l)
} else {
l := link{name: n.Name}
if n.Obj == nil && doc.IsPredeclared(n.Name) {
l.path = builtinPkgPath
}
links = append(links, l)
}
}
return true
})
return
}

144
vendor/golang.org/x/tools/godoc/meta.go generated vendored Normal file
View File

@@ -0,0 +1,144 @@
// 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 godoc
import (
"bytes"
"encoding/json"
"log"
pathpkg "path"
"strings"
"time"
"golang.org/x/tools/godoc/vfs"
)
var (
doctype = []byte("<!DOCTYPE ")
jsonStart = []byte("<!--{")
jsonEnd = []byte("}-->")
)
// ----------------------------------------------------------------------------
// Documentation Metadata
// TODO(adg): why are some exported and some aren't? -brad
type Metadata struct {
Title string
Subtitle string
Template bool // execute as template
Path string // canonical path for this page
filePath string // filesystem path relative to goroot
}
func (m *Metadata) FilePath() string { return m.filePath }
// extractMetadata extracts the Metadata from a byte slice.
// It returns the Metadata value and the remaining data.
// If no metadata is present the original byte slice is returned.
//
func extractMetadata(b []byte) (meta Metadata, tail []byte, err error) {
tail = b
if !bytes.HasPrefix(b, jsonStart) {
return
}
end := bytes.Index(b, jsonEnd)
if end < 0 {
return
}
b = b[len(jsonStart)-1 : end+1] // drop leading <!-- and include trailing }
if err = json.Unmarshal(b, &meta); err != nil {
return
}
tail = tail[end+len(jsonEnd):]
return
}
// UpdateMetadata scans $GOROOT/doc for HTML files, reads their metadata,
// and updates the DocMetadata map.
func (c *Corpus) updateMetadata() {
metadata := make(map[string]*Metadata)
var scan func(string) // scan is recursive
scan = func(dir string) {
fis, err := c.fs.ReadDir(dir)
if err != nil {
log.Println("updateMetadata:", err)
return
}
for _, fi := range fis {
name := pathpkg.Join(dir, fi.Name())
if fi.IsDir() {
scan(name) // recurse
continue
}
if !strings.HasSuffix(name, ".html") {
continue
}
// Extract metadata from the file.
b, err := vfs.ReadFile(c.fs, name)
if err != nil {
log.Printf("updateMetadata %s: %v", name, err)
continue
}
meta, _, err := extractMetadata(b)
if err != nil {
log.Printf("updateMetadata: %s: %v", name, err)
continue
}
// Store relative filesystem path in Metadata.
meta.filePath = name
if meta.Path == "" {
// If no Path, canonical path is actual path.
meta.Path = meta.filePath
}
// Store under both paths.
metadata[meta.Path] = &meta
metadata[meta.filePath] = &meta
}
}
scan("/doc")
c.docMetadata.Set(metadata)
}
// MetadataFor returns the *Metadata for a given relative path or nil if none
// exists.
//
func (c *Corpus) MetadataFor(relpath string) *Metadata {
if m, _ := c.docMetadata.Get(); m != nil {
meta := m.(map[string]*Metadata)
// If metadata for this relpath exists, return it.
if p := meta[relpath]; p != nil {
return p
}
// Try with or without trailing slash.
if strings.HasSuffix(relpath, "/") {
relpath = relpath[:len(relpath)-1]
} else {
relpath = relpath + "/"
}
return meta[relpath]
}
return nil
}
// refreshMetadata sends a signal to update DocMetadata. If a refresh is in
// progress the metadata will be refreshed again afterward.
//
func (c *Corpus) refreshMetadata() {
select {
case c.refreshMetadataSignal <- true:
default:
}
}
// RefreshMetadataLoop runs forever, updating DocMetadata when the underlying
// file system changes. It should be launched in a goroutine.
func (c *Corpus) refreshMetadataLoop() {
for {
<-c.refreshMetadataSignal
c.updateMetadata()
time.Sleep(10 * time.Second) // at most once every 10 seconds
}
}

76
vendor/golang.org/x/tools/godoc/page.go generated vendored Normal file
View File

@@ -0,0 +1,76 @@
// 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 godoc
import (
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
)
// Page describes the contents of the top-level godoc webpage.
type Page struct {
Title string
Tabtitle string
Subtitle string
SrcPath string
Query string
Body []byte
GoogleCN bool // page is being served from golang.google.cn
// filled in by servePage
SearchBox bool
Playground bool
Version string
}
func (p *Presentation) ServePage(w http.ResponseWriter, page Page) {
if page.Tabtitle == "" {
page.Tabtitle = page.Title
}
page.SearchBox = p.Corpus.IndexEnabled
page.Playground = p.ShowPlayground
page.Version = runtime.Version()
applyTemplateToResponseWriter(w, p.GodocHTML, page)
}
func (p *Presentation) ServeError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
w.WriteHeader(http.StatusNotFound)
if perr, ok := err.(*os.PathError); ok {
rel, err := filepath.Rel(runtime.GOROOT(), perr.Path)
if err != nil {
perr.Path = "REDACTED"
} else {
perr.Path = filepath.Join("$GOROOT", rel)
}
}
p.ServePage(w, Page{
Title: "File " + relpath,
Subtitle: relpath,
Body: applyTemplate(p.ErrorHTML, "errorHTML", err),
GoogleCN: googleCN(r),
})
}
var onAppengine = false // overridden in appengine.go when on app engine
func googleCN(r *http.Request) bool {
if r.FormValue("googlecn") != "" {
return true
}
if !onAppengine {
return false
}
if strings.HasSuffix(r.Host, ".cn") {
return true
}
switch r.Header.Get("X-AppEngine-Country") {
case "", "ZZ", "CN":
return true
}
return false
}

74
vendor/golang.org/x/tools/godoc/parser.go generated vendored Normal file
View File

@@ -0,0 +1,74 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains support functions for parsing .go files
// accessed via godoc's file system fs.
package godoc
import (
"bytes"
"go/ast"
"go/parser"
"go/token"
pathpkg "path"
"golang.org/x/tools/godoc/vfs"
)
var linePrefix = []byte("//line ")
// This function replaces source lines starting with "//line " with a blank line.
// It does this irrespective of whether the line is truly a line comment or not;
// e.g., the line may be inside a string, or a /*-style comment; however that is
// rather unlikely (proper testing would require a full Go scan which we want to
// avoid for performance).
func replaceLinePrefixCommentsWithBlankLine(src []byte) {
for {
i := bytes.Index(src, linePrefix)
if i < 0 {
break // we're done
}
// 0 <= i && i+len(linePrefix) <= len(src)
if i == 0 || src[i-1] == '\n' {
// at beginning of line: blank out line
for i < len(src) && src[i] != '\n' {
src[i] = ' '
i++
}
} else {
// not at beginning of line: skip over prefix
i += len(linePrefix)
}
// i <= len(src)
src = src[i:]
}
}
func (c *Corpus) parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) {
src, err := vfs.ReadFile(c.fs, filename)
if err != nil {
return nil, err
}
// Temporary ad-hoc fix for issue 5247.
// TODO(gri) Remove this in favor of a better fix, eventually (see issue 7702).
replaceLinePrefixCommentsWithBlankLine(src)
return parser.ParseFile(fset, filename, src, mode)
}
func (c *Corpus) parseFiles(fset *token.FileSet, relpath string, abspath string, localnames []string) (map[string]*ast.File, error) {
files := make(map[string]*ast.File)
for _, f := range localnames {
absname := pathpkg.Join(abspath, f)
file, err := c.parseFile(fset, absname, parser.ParseComments)
if err != nil {
return nil, err
}
files[pathpkg.Join(relpath, f)] = file
}
return files, nil
}

169
vendor/golang.org/x/tools/godoc/pres.go generated vendored Normal file
View File

@@ -0,0 +1,169 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
import (
"net/http"
"regexp"
"sync"
"text/template"
"golang.org/x/tools/godoc/vfs/httpfs"
)
// SearchResultFunc functions return an HTML body for displaying search results.
type SearchResultFunc func(p *Presentation, result SearchResult) []byte
// Presentation generates output from a corpus.
type Presentation struct {
Corpus *Corpus
mux *http.ServeMux
fileServer http.Handler
cmdHandler handlerServer
pkgHandler handlerServer
CallGraphHTML,
DirlistHTML,
ErrorHTML,
ExampleHTML,
GodocHTML,
ImplementsHTML,
MethodSetHTML,
PackageHTML,
PackageRootHTML,
PackageText,
SearchHTML,
SearchDocHTML,
SearchCodeHTML,
SearchTxtHTML,
SearchText,
SearchDescXML *template.Template
// TabWidth optionally specifies the tab width.
TabWidth int
ShowTimestamps bool
ShowPlayground bool
ShowExamples bool
DeclLinks bool
// SrcMode outputs source code instead of documentation in command-line mode.
SrcMode bool
// HTMLMode outputs HTML instead of plain text in command-line mode.
HTMLMode bool
// AllMode includes unexported identifiers in the output in command-line mode.
AllMode bool
// NotesRx optionally specifies a regexp to match
// notes to render in the output.
NotesRx *regexp.Regexp
// AdjustPageInfoMode optionally specifies a function to
// modify the PageInfoMode of a request. The default chosen
// value is provided.
AdjustPageInfoMode func(req *http.Request, mode PageInfoMode) PageInfoMode
// URLForSrc optionally specifies a function that takes a source file and
// returns a URL for it.
// The source file argument has the form /src/<path>/<filename>.
URLForSrc func(src string) string
// URLForSrcPos optionally specifies a function to create a URL given a
// source file, a line from the source file (1-based), and low & high offset
// positions (0-based, bytes from beginning of file). Ideally, the returned
// URL will be for the specified line of the file, while the high & low
// positions will be used to highlight a section of the file.
// The source file argument has the form /src/<path>/<filename>.
URLForSrcPos func(src string, line, low, high int) string
// URLForSrcQuery optionally specifies a function to create a URL given a
// source file, a query string, and a line from the source file (1-based).
// The source file argument has the form /src/<path>/<filename>.
// The query argument will be escaped for the purposes of embedding in a URL
// query parameter.
// Ideally, the returned URL will be for the specified line of the file with
// the query string highlighted.
URLForSrcQuery func(src, query string, line int) string
// SearchResults optionally specifies a list of functions returning an HTML
// body for displaying search results.
SearchResults []SearchResultFunc
initFuncMapOnce sync.Once
funcMap template.FuncMap
templateFuncs template.FuncMap
}
// NewPresentation returns a new Presentation from a corpus.
// It sets SearchResults to:
// [SearchResultDoc SearchResultCode SearchResultTxt].
func NewPresentation(c *Corpus) *Presentation {
if c == nil {
panic("nil Corpus")
}
p := &Presentation{
Corpus: c,
mux: http.NewServeMux(),
fileServer: http.FileServer(httpfs.New(c.fs)),
TabWidth: 4,
ShowExamples: true,
DeclLinks: true,
SearchResults: []SearchResultFunc{
(*Presentation).SearchResultDoc,
(*Presentation).SearchResultCode,
(*Presentation).SearchResultTxt,
},
}
p.cmdHandler = handlerServer{
p: p,
c: c,
pattern: "/cmd/",
fsRoot: "/src",
}
p.pkgHandler = handlerServer{
p: p,
c: c,
pattern: "/pkg/",
stripPrefix: "pkg/",
fsRoot: "/src",
exclude: []string{"/src/cmd"},
}
p.cmdHandler.registerWithMux(p.mux)
p.pkgHandler.registerWithMux(p.mux)
p.mux.HandleFunc("/", p.ServeFile)
p.mux.HandleFunc("/search", p.HandleSearch)
p.mux.HandleFunc("/opensearch.xml", p.serveSearchDesc)
return p
}
func (p *Presentation) FileServer() http.Handler {
return p.fileServer
}
func (p *Presentation) ServeHTTP(w http.ResponseWriter, r *http.Request) {
p.mux.ServeHTTP(w, r)
}
func (p *Presentation) PkgFSRoot() string {
return p.pkgHandler.fsRoot
}
func (p *Presentation) CmdFSRoot() string {
return p.cmdHandler.fsRoot
}
// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
// but this doesn't feel right.
func (p *Presentation) GetPkgPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
return p.pkgHandler.GetPageInfo(abspath, relpath, mode, "", "")
}
// TODO(bradfitz): move this to be a method on Corpus. Just moving code around for now,
// but this doesn't feel right.
func (p *Presentation) GetCmdPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
return p.cmdHandler.GetPageInfo(abspath, relpath, mode, "", "")
}

148
vendor/golang.org/x/tools/godoc/proxy/proxy.go generated vendored Normal file
View File

@@ -0,0 +1,148 @@
// Copyright 2015 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.
// +build appengine
// Package proxy proxies requests to the playground's compile and share handlers.
// It is designed to run only on the instance of godoc that serves golang.org.
package proxy
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
"golang.org/x/net/context"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
"google.golang.org/appengine/urlfetch"
)
type Request struct {
Body string
}
type Response struct {
Errors string
Events []Event
}
type Event struct {
Message string
Kind string // "stdout" or "stderr"
Delay time.Duration // time to wait before printing Message
}
const playgroundURL = "https://play.golang.org"
const expires = 7 * 24 * time.Hour // 1 week
var cacheControlHeader = fmt.Sprintf("public, max-age=%d", int(expires.Seconds()))
func RegisterHandlers(mux *http.ServeMux) {
mux.HandleFunc("/compile", compile)
mux.HandleFunc("/share", share)
}
func compile(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "I only answer to POST requests.", http.StatusMethodNotAllowed)
return
}
ctx := appengine.NewContext(r)
body := r.FormValue("body")
res := &Response{}
req := &Request{Body: body}
if err := makeCompileRequest(ctx, req, res); err != nil {
log.Errorf(ctx, "compile error: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
expiresTime := time.Now().Add(expires).UTC()
w.Header().Set("Expires", expiresTime.Format(time.RFC1123))
w.Header().Set("Cache-Control", cacheControlHeader)
var out interface{}
switch r.FormValue("version") {
case "2":
out = res
default: // "1"
out = struct {
CompileErrors string `json:"compile_errors"`
Output string `json:"output"`
}{res.Errors, flatten(res.Events)}
}
if err := json.NewEncoder(w).Encode(out); err != nil {
log.Errorf(ctx, "encoding response: %v", err)
}
}
// makePlaygroundRequest sends the given Request to the playground compile
// endpoint and stores the response in the given Response.
func makeCompileRequest(ctx context.Context, req *Request, res *Response) error {
reqJ, err := json.Marshal(req)
if err != nil {
return fmt.Errorf("marshalling request: %v", err)
}
r, err := urlfetch.Client(ctx).Post(playgroundURL+"/compile", "application/json", bytes.NewReader(reqJ))
if err != nil {
return fmt.Errorf("making request: %v", err)
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
b, _ := ioutil.ReadAll(r.Body)
return fmt.Errorf("bad status: %v body:\n%s", r.Status, b)
}
err = json.NewDecoder(r.Body).Decode(res)
if err != nil {
return fmt.Errorf("unmarshalling response: %v", err)
}
return nil
}
// flatten takes a sequence of Events and returns their contents, concatenated.
func flatten(seq []Event) string {
var buf bytes.Buffer
for _, e := range seq {
buf.WriteString(e.Message)
}
return buf.String()
}
func share(w http.ResponseWriter, r *http.Request) {
if googleCN(r) {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
}
target, _ := url.Parse(playgroundURL)
p := httputil.NewSingleHostReverseProxy(target)
p.Transport = &urlfetch.Transport{Context: appengine.NewContext(r)}
p.ServeHTTP(w, r)
}
func googleCN(r *http.Request) bool {
if r.FormValue("googlecn") != "" {
return true
}
if appengine.IsDevAppServer() {
return false
}
if strings.HasSuffix(r.Host, ".cn") {
return true
}
switch r.Header.Get("X-AppEngine-Country") {
case "", "ZZ", "CN":
return true
}
return false
}

138
vendor/golang.org/x/tools/godoc/redirect/hash.go generated vendored Normal file
View File

@@ -0,0 +1,138 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file provides a compact encoding of
// a map of Mercurial hashes to Git hashes.
package redirect
import (
"encoding/binary"
"fmt"
"io"
"os"
"sort"
"strconv"
"strings"
)
// hashMap is a map of Mercurial hashes to Git hashes.
type hashMap struct {
file *os.File
entries int
}
// newHashMap takes a file handle that contains a map of Mercurial to Git
// hashes. The file should be a sequence of pairs of little-endian encoded
// uint32s, representing a hgHash and a gitHash respectively.
// The sequence must be sorted by hgHash.
// The file must remain open for as long as the returned hashMap is used.
func newHashMap(f *os.File) (*hashMap, error) {
fi, err := f.Stat()
if err != nil {
return nil, err
}
return &hashMap{file: f, entries: int(fi.Size() / 8)}, nil
}
// Lookup finds an hgHash in the map that matches the given prefix, and returns
// its corresponding gitHash. The prefix must be at least 8 characters long.
func (m *hashMap) Lookup(s string) gitHash {
if m == nil {
return 0
}
hg, err := hgHashFromString(s)
if err != nil {
return 0
}
var git gitHash
b := make([]byte, 8)
sort.Search(m.entries, func(i int) bool {
n, err := m.file.ReadAt(b, int64(i*8))
if err != nil {
panic(err)
}
if n != 8 {
panic(io.ErrUnexpectedEOF)
}
v := hgHash(binary.LittleEndian.Uint32(b[:4]))
if v == hg {
git = gitHash(binary.LittleEndian.Uint32(b[4:]))
}
return v >= hg
})
return git
}
// hgHash represents the lower (leftmost) 32 bits of a Mercurial hash.
type hgHash uint32
func (h hgHash) String() string {
return intToHash(int64(h))
}
func hgHashFromString(s string) (hgHash, error) {
if len(s) < 8 {
return 0, fmt.Errorf("string too small: len(s) = %d", len(s))
}
hash := s[:8]
i, err := strconv.ParseInt(hash, 16, 64)
if err != nil {
return 0, err
}
return hgHash(i), nil
}
// gitHash represents the leftmost 28 bits of a Git hash in its upper 28 bits,
// and it encodes hash's repository in the lower 4 bits.
type gitHash uint32
func (h gitHash) Hash() string {
return intToHash(int64(h))[:7]
}
func (h gitHash) Repo() string {
return repo(h & 0xF).String()
}
func intToHash(i int64) string {
s := strconv.FormatInt(i, 16)
if len(s) < 8 {
s = strings.Repeat("0", 8-len(s)) + s
}
return s
}
// repo represents a Go Git repository.
type repo byte
const (
repoGo repo = iota
repoBlog
repoCrypto
repoExp
repoImage
repoMobile
repoNet
repoSys
repoTalks
repoText
repoTools
)
func (r repo) String() string {
return map[repo]string{
repoGo: "go",
repoBlog: "blog",
repoCrypto: "crypto",
repoExp: "exp",
repoImage: "image",
repoMobile: "mobile",
repoNet: "net",
repoSys: "sys",
repoTalks: "talks",
repoText: "text",
repoTools: "tools",
}[r]
}

250
vendor/golang.org/x/tools/godoc/redirect/redirect.go generated vendored Normal file
View File

@@ -0,0 +1,250 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package redirect provides hooks to register HTTP handlers that redirect old
// godoc paths to their new equivalents and assist in accessing the issue
// tracker, wiki, code review system, etc.
package redirect // import "golang.org/x/tools/godoc/redirect"
import (
"fmt"
"net/http"
"os"
"regexp"
"strconv"
"strings"
)
// Register registers HTTP handlers that redirect old godoc paths to their new
// equivalents and assist in accessing the issue tracker, wiki, code review
// system, etc. If mux is nil it uses http.DefaultServeMux.
func Register(mux *http.ServeMux) {
if mux == nil {
mux = http.DefaultServeMux
}
handlePathRedirects(mux, pkgRedirects, "/pkg/")
handlePathRedirects(mux, cmdRedirects, "/cmd/")
for prefix, redirect := range prefixHelpers {
p := "/" + prefix + "/"
mux.Handle(p, PrefixHandler(p, redirect))
}
for path, redirect := range redirects {
mux.Handle(path, Handler(redirect))
}
// NB: /src/pkg (sans trailing slash) is the index of packages.
mux.HandleFunc("/src/pkg/", srcPkgHandler)
mux.HandleFunc("/cl/", clHandler)
mux.HandleFunc("/change/", changeHandler)
mux.HandleFunc("/design/", designHandler)
}
func handlePathRedirects(mux *http.ServeMux, redirects map[string]string, prefix string) {
for source, target := range redirects {
h := Handler(prefix + target + "/")
p := prefix + source
mux.Handle(p, h)
mux.Handle(p+"/", h)
}
}
// Packages that were renamed between r60 and go1.
var pkgRedirects = map[string]string{
"asn1": "encoding/asn1",
"big": "math/big",
"cmath": "math/cmplx",
"csv": "encoding/csv",
"exec": "os/exec",
"exp/template/html": "html/template",
"gob": "encoding/gob",
"http": "net/http",
"http/cgi": "net/http/cgi",
"http/fcgi": "net/http/fcgi",
"http/httptest": "net/http/httptest",
"http/pprof": "net/http/pprof",
"json": "encoding/json",
"mail": "net/mail",
"rand": "math/rand",
"rpc": "net/rpc",
"rpc/jsonrpc": "net/rpc/jsonrpc",
"scanner": "text/scanner",
"smtp": "net/smtp",
"tabwriter": "text/tabwriter",
"template": "text/template",
"template/parse": "text/template/parse",
"url": "net/url",
"utf16": "unicode/utf16",
"utf8": "unicode/utf8",
"xml": "encoding/xml",
}
// Commands that were renamed between r60 and go1.
var cmdRedirects = map[string]string{
"gofix": "fix",
"goinstall": "go",
"gopack": "pack",
"gotest": "go",
"govet": "vet",
"goyacc": "yacc",
}
var redirects = map[string]string{
"/blog": "/blog/",
"/build": "http://build.golang.org",
"/change": "https://go.googlesource.com/go",
"/cl": "https://go-review.googlesource.com",
"/cmd/godoc/": "http://godoc.org/golang.org/x/tools/cmd/godoc/",
"/issue": "https://github.com/golang/go/issues",
"/issue/new": "https://github.com/golang/go/issues/new",
"/issues": "https://github.com/golang/go/issues",
"/issues/new": "https://github.com/golang/go/issues/new",
"/play": "http://play.golang.org",
"/design": "https://github.com/golang/proposal/tree/master/design",
// In Go 1.2 the references page is part of /doc/.
"/ref": "/doc/#references",
// This next rule clobbers /ref/spec and /ref/mem.
// TODO(adg): figure out what to do here, if anything.
// "/ref/": "/doc/#references",
// Be nice to people who are looking in the wrong place.
"/doc/mem": "/ref/mem",
"/doc/spec": "/ref/spec",
"/talks": "http://talks.golang.org",
"/tour": "http://tour.golang.org",
"/wiki": "https://github.com/golang/go/wiki",
"/doc/articles/c_go_cgo.html": "/blog/c-go-cgo",
"/doc/articles/concurrency_patterns.html": "/blog/go-concurrency-patterns-timing-out-and",
"/doc/articles/defer_panic_recover.html": "/blog/defer-panic-and-recover",
"/doc/articles/error_handling.html": "/blog/error-handling-and-go",
"/doc/articles/gobs_of_data.html": "/blog/gobs-of-data",
"/doc/articles/godoc_documenting_go_code.html": "/blog/godoc-documenting-go-code",
"/doc/articles/gos_declaration_syntax.html": "/blog/gos-declaration-syntax",
"/doc/articles/image_draw.html": "/blog/go-imagedraw-package",
"/doc/articles/image_package.html": "/blog/go-image-package",
"/doc/articles/json_and_go.html": "/blog/json-and-go",
"/doc/articles/json_rpc_tale_of_interfaces.html": "/blog/json-rpc-tale-of-interfaces",
"/doc/articles/laws_of_reflection.html": "/blog/laws-of-reflection",
"/doc/articles/slices_usage_and_internals.html": "/blog/go-slices-usage-and-internals",
"/doc/go_for_cpp_programmers.html": "/wiki/GoForCPPProgrammers",
"/doc/go_tutorial.html": "http://tour.golang.org/",
}
var prefixHelpers = map[string]string{
"issue": "https://github.com/golang/go/issues/",
"issues": "https://github.com/golang/go/issues/",
"play": "http://play.golang.org/",
"talks": "http://talks.golang.org/",
"wiki": "https://github.com/golang/go/wiki/",
}
func Handler(target string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
url := target
if qs := r.URL.RawQuery; qs != "" {
url += "?" + qs
}
http.Redirect(w, r, url, http.StatusMovedPermanently)
})
}
var validId = regexp.MustCompile(`^[A-Za-z0-9-]*/?$`)
func PrefixHandler(prefix, baseURL string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if p := r.URL.Path; p == prefix {
// redirect /prefix/ to /prefix
http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
return
}
id := r.URL.Path[len(prefix):]
if !validId.MatchString(id) {
http.Error(w, "Not found", http.StatusNotFound)
return
}
target := baseURL + id
http.Redirect(w, r, target, http.StatusFound)
})
}
// Redirect requests from the old "/src/pkg/foo" to the new "/src/foo".
// See http://golang.org/s/go14nopkg
func srcPkgHandler(w http.ResponseWriter, r *http.Request) {
r.URL.Path = "/src/" + r.URL.Path[len("/src/pkg/"):]
http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
}
func clHandler(w http.ResponseWriter, r *http.Request) {
const prefix = "/cl/"
if p := r.URL.Path; p == prefix {
// redirect /prefix/ to /prefix
http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
return
}
id := r.URL.Path[len(prefix):]
// support /cl/152700045/, which is used in commit 0edafefc36.
id = strings.TrimSuffix(id, "/")
if !validId.MatchString(id) {
http.Error(w, "Not found", http.StatusNotFound)
return
}
target := ""
// the first CL in rietveld is about 152046, so only treat the id as
// a rietveld CL if it is larger than 150000.
if n, err := strconv.Atoi(id); err == nil && n > 150000 {
target = "https://codereview.appspot.com/" + id
} else {
target = "https://go-review.googlesource.com/" + id
}
http.Redirect(w, r, target, http.StatusFound)
}
var changeMap *hashMap
// LoadChangeMap loads the specified map of Mercurial to Git revisions,
// which is used by the /change/ handler to intelligently map old hg
// revisions to their new git equivalents.
// It should be called before calling Register.
// The file should remain open as long as the process is running.
// See the implementation of this package for details.
func LoadChangeMap(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
m, err := newHashMap(f)
if err != nil {
return err
}
changeMap = m
return nil
}
func changeHandler(w http.ResponseWriter, r *http.Request) {
const prefix = "/change/"
if p := r.URL.Path; p == prefix {
// redirect /prefix/ to /prefix
http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
return
}
hash := r.URL.Path[len(prefix):]
target := "https://go.googlesource.com/go/+/" + hash
if git := changeMap.Lookup(hash); git > 0 {
target = fmt.Sprintf("https://go.googlesource.com/%v/+/%v", git.Repo(), git.Hash())
}
http.Redirect(w, r, target, http.StatusFound)
}
func designHandler(w http.ResponseWriter, r *http.Request) {
const prefix = "/design/"
if p := r.URL.Path; p == prefix {
// redirect /prefix/ to /prefix
http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
return
}
name := r.URL.Path[len(prefix):]
target := "https://github.com/golang/proposal/blob/master/design/" + name + ".md"
http.Redirect(w, r, target, http.StatusFound)
}

View File

@@ -0,0 +1,104 @@
// Copyright 2015 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 redirect
import (
"net/http"
"net/http/httptest"
"testing"
)
type redirectResult struct {
status int
path string
}
func errorResult(status int) redirectResult {
return redirectResult{status, ""}
}
func TestRedirects(t *testing.T) {
var tests = map[string]redirectResult{
"/build": {301, "http://build.golang.org"},
"/ref": {301, "/doc/#references"},
"/doc/mem": {301, "/ref/mem"},
"/doc/spec": {301, "/ref/spec"},
"/tour": {301, "http://tour.golang.org"},
"/foo": errorResult(404),
"/pkg/asn1": {301, "/pkg/encoding/asn1/"},
"/pkg/template/parse": {301, "/pkg/text/template/parse/"},
"/src/pkg/foo": {301, "/src/foo"},
"/cmd/gofix": {301, "/cmd/fix/"},
// git commits (/change)
// TODO: mercurial tags and LoadChangeMap.
"/change": {301, "https://go.googlesource.com/go"},
"/change/a": {302, "https://go.googlesource.com/go/+/a"},
"/issue": {301, "https://github.com/golang/go/issues"},
"/issue?": {301, "https://github.com/golang/go/issues"},
"/issue/1": {302, "https://github.com/golang/go/issues/1"},
"/issue/new": {301, "https://github.com/golang/go/issues/new"},
"/issue/new?a=b&c=d%20&e=f": {301, "https://github.com/golang/go/issues/new?a=b&c=d%20&e=f"},
"/issues": {301, "https://github.com/golang/go/issues"},
"/issues/1": {302, "https://github.com/golang/go/issues/1"},
"/issues/new": {301, "https://github.com/golang/go/issues/new"},
"/issues/1/2/3": errorResult(404),
"/wiki/foo": {302, "https://github.com/golang/go/wiki/foo"},
"/wiki/foo/": {302, "https://github.com/golang/go/wiki/foo/"},
"/design": {301, "https://github.com/golang/proposal/tree/master/design"},
"/design/": {302, "/design"},
"/design/123-foo": {302, "https://github.com/golang/proposal/blob/master/design/123-foo.md"},
"/design/text/123-foo": {302, "https://github.com/golang/proposal/blob/master/design/text/123-foo.md"},
"/cl/1": {302, "https://go-review.googlesource.com/1"},
"/cl/1/": {302, "https://go-review.googlesource.com/1"},
"/cl/267120043": {302, "https://codereview.appspot.com/267120043"},
"/cl/267120043/": {302, "https://codereview.appspot.com/267120043"},
}
mux := http.NewServeMux()
Register(mux)
ts := httptest.NewServer(mux)
defer ts.Close()
for path, want := range tests {
if want.path != "" && want.path[0] == '/' {
// All redirects are absolute.
want.path = ts.URL + want.path
}
req, err := http.NewRequest("GET", ts.URL+path, nil)
if err != nil {
t.Errorf("(path: %q) unexpected error: %v", path, err)
continue
}
resp, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
t.Errorf("(path: %q) unexpected error: %v", path, err)
continue
}
if resp.StatusCode != want.status {
t.Errorf("(path: %q) got status %d, want %d", path, resp.StatusCode, want.status)
}
if want.status != 301 && want.status != 302 {
// Not a redirect. Just check status.
continue
}
out, _ := resp.Location()
if got := out.String(); got != want.path {
t.Errorf("(path: %q) got %s, want %s", path, got, want.path)
}
}
}

139
vendor/golang.org/x/tools/godoc/search.go generated vendored Normal file
View File

@@ -0,0 +1,139 @@
// 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 godoc
import (
"bytes"
"fmt"
"net/http"
"regexp"
"strings"
)
type SearchResult struct {
Query string
Alert string // error or warning message
// identifier matches
Pak HitList // packages matching Query
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
Idents map[SpotKind][]Ident
}
func (c *Corpus) Lookup(query string) SearchResult {
result := &SearchResult{Query: query}
index, timestamp := c.CurrentIndex()
if index != nil {
// identifier search
if r, err := index.Lookup(query); err == nil {
result = r
} else if err != nil && !c.IndexFullText {
// ignore the error if full text search is enabled
// since the query may be a valid regular expression
result.Alert = "Error in query string: " + err.Error()
return *result
}
// full text search
if c.IndexFullText && query != "" {
rx, err := regexp.Compile(query)
if err != nil {
result.Alert = "Error in query regular expression: " + err.Error()
return *result
}
// 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, c.MaxResults+1)
result.Complete = result.Found <= c.MaxResults
if !result.Complete {
result.Found-- // since we looked for maxResults+1
}
}
}
// is the result accurate?
if c.IndexEnabled {
if ts := c.FSModifiedTime(); timestamp.Before(ts) {
// The index is older than the latest file system change under godoc's observation.
result.Alert = "Indexing in progress: result may be inaccurate"
}
} else {
result.Alert = "Search index disabled: no results available"
}
return *result
}
// SearchResultDoc optionally specifies a function returning an HTML body
// displaying search results matching godoc documentation.
func (p *Presentation) SearchResultDoc(result SearchResult) []byte {
return applyTemplate(p.SearchDocHTML, "searchDocHTML", result)
}
// SearchResultCode optionally specifies a function returning an HTML body
// displaying search results matching source code.
func (p *Presentation) SearchResultCode(result SearchResult) []byte {
return applyTemplate(p.SearchCodeHTML, "searchCodeHTML", result)
}
// SearchResultTxt optionally specifies a function returning an HTML body
// displaying search results of textual matches.
func (p *Presentation) SearchResultTxt(result SearchResult) []byte {
return applyTemplate(p.SearchTxtHTML, "searchTxtHTML", result)
}
// HandleSearch obtains results for the requested search and returns a page
// to display them.
func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
query := strings.TrimSpace(r.FormValue("q"))
result := p.Corpus.Lookup(query)
if p.GetPageInfoMode(r)&NoHTML != 0 {
p.ServeText(w, applyTemplate(p.SearchText, "searchText", result))
return
}
contents := bytes.Buffer{}
for _, f := range p.SearchResults {
contents.Write(f(p, result))
}
var title string
if haveResults := contents.Len() > 0; haveResults {
title = fmt.Sprintf(`Results for query: %v`, query)
if !p.Corpus.IndexEnabled {
result.Alert = ""
}
} else {
title = fmt.Sprintf(`No results found for query %q`, query)
}
body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result))
body.Write(contents.Bytes())
p.ServePage(w, Page{
Title: title,
Tabtitle: query,
Query: query,
Body: body.Bytes(),
GoogleCN: googleCN(r),
})
}
func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/opensearchdescription+xml")
data := map[string]interface{}{
"BaseURL": fmt.Sprintf("http://%s", r.Host),
}
applyTemplateToResponseWriter(w, p.SearchDescXML, &data)
}

830
vendor/golang.org/x/tools/godoc/server.go generated vendored Normal file
View File

@@ -0,0 +1,830 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"go/ast"
"go/build"
"go/doc"
"go/token"
htmlpkg "html"
htmltemplate "html/template"
"io"
"io/ioutil"
"log"
"net/http"
"os"
pathpkg "path"
"path/filepath"
"sort"
"strings"
"text/template"
"time"
"golang.org/x/tools/godoc/analysis"
"golang.org/x/tools/godoc/util"
"golang.org/x/tools/godoc/vfs"
)
// handlerServer is a migration from an old godoc http Handler type.
// This should probably merge into something else.
type handlerServer struct {
p *Presentation
c *Corpus // copy of p.Corpus
pattern string // url pattern; e.g. "/pkg/"
stripPrefix string // prefix to strip from import path; e.g. "pkg/"
fsRoot string // file system root to which the pattern is mapped; e.g. "/src"
exclude []string // file system paths to exclude; e.g. "/src/cmd"
}
func (s *handlerServer) registerWithMux(mux *http.ServeMux) {
mux.Handle(s.pattern, s)
}
// getPageInfo returns the PageInfo for a package directory abspath. If the
// parameter genAST is set, an AST containing only the package exports is
// computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
// 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 an error occurred, PageInfo.Err is
// set to the respective error but the error is not logged.
//
func (h *handlerServer) GetPageInfo(abspath, relpath string, mode PageInfoMode, goos, goarch string) *PageInfo {
info := &PageInfo{Dirname: abspath, Mode: mode}
// Restrict to the package files that would be used when building
// the package on this system. This makes sure that if there are
// separate implementations for, say, Windows vs Unix, we don't
// jumble them all together.
// Note: If goos/goarch aren't set, the current binary's GOOS/GOARCH
// are used.
ctxt := build.Default
ctxt.IsAbsPath = pathpkg.IsAbs
ctxt.IsDir = func(path string) bool {
fi, err := h.c.fs.Stat(filepath.ToSlash(path))
return err == nil && fi.IsDir()
}
ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
f, err := h.c.fs.ReadDir(filepath.ToSlash(dir))
filtered := make([]os.FileInfo, 0, len(f))
for _, i := range f {
if mode&NoFiltering != 0 || i.Name() != "internal" {
filtered = append(filtered, i)
}
}
return filtered, err
}
ctxt.OpenFile = func(name string) (r io.ReadCloser, err error) {
data, err := vfs.ReadFile(h.c.fs, filepath.ToSlash(name))
if err != nil {
return nil, err
}
return ioutil.NopCloser(bytes.NewReader(data)), nil
}
// Make the syscall/js package always visible by default.
// It defaults to the host's GOOS/GOARCH, and golang.org's
// linux/amd64 means the wasm syscall/js package was blank.
// And you can't run godoc on js/wasm anyway, so host defaults
// don't make sense here.
if goos == "" && goarch == "" && relpath == "syscall/js" {
goos, goarch = "js", "wasm"
}
if goos != "" {
ctxt.GOOS = goos
}
if goarch != "" {
ctxt.GOARCH = goarch
}
pkginfo, err := ctxt.ImportDir(abspath, 0)
// continue if there are no Go source files; we still want the directory info
if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
info.Err = err
return info
}
// collect package files
pkgname := pkginfo.Name
pkgfiles := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
if len(pkgfiles) == 0 {
// Commands written in C have no .go files in the build.
// Instead, documentation may be found in an ignored file.
// The file may be ignored via an explicit +build ignore
// constraint (recommended), or by defining the package
// documentation (historic).
pkgname = "main" // assume package main since pkginfo.Name == ""
pkgfiles = pkginfo.IgnoredGoFiles
}
// get package information, if any
if len(pkgfiles) > 0 {
// build package AST
fset := token.NewFileSet()
files, err := h.c.parseFiles(fset, relpath, abspath, pkgfiles)
if err != nil {
info.Err = err
return info
}
// ignore any errors - they are due to unresolved identifiers
pkg, _ := ast.NewPackage(fset, files, poorMansImporter, nil)
// extract package documentation
info.FSet = fset
if mode&ShowSource == 0 {
// show extracted documentation
var m doc.Mode
if mode&NoFiltering != 0 {
m |= doc.AllDecls
}
if mode&AllMethods != 0 {
m |= doc.AllMethods
}
info.PDoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath
if mode&NoTypeAssoc != 0 {
for _, t := range info.PDoc.Types {
info.PDoc.Consts = append(info.PDoc.Consts, t.Consts...)
info.PDoc.Vars = append(info.PDoc.Vars, t.Vars...)
info.PDoc.Funcs = append(info.PDoc.Funcs, t.Funcs...)
t.Consts = nil
t.Vars = nil
t.Funcs = nil
}
// for now we cannot easily sort consts and vars since
// go/doc.Value doesn't export the order information
sort.Sort(funcsByName(info.PDoc.Funcs))
}
// collect examples
testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...)
files, err = h.c.parseFiles(fset, relpath, abspath, testfiles)
if err != nil {
log.Println("parsing examples:", err)
}
info.Examples = collectExamples(h.c, pkg, files)
// collect any notes that we want to show
if info.PDoc.Notes != nil {
// could regexp.Compile only once per godoc, but probably not worth it
if rx := h.p.NotesRx; rx != nil {
for m, n := range info.PDoc.Notes {
if rx.MatchString(m) {
if info.Notes == nil {
info.Notes = make(map[string][]*doc.Note)
}
info.Notes[m] = n
}
}
}
}
} else {
// show source code
// TODO(gri) Consider eliminating export filtering in this mode,
// or perhaps eliminating the mode altogether.
if mode&NoFiltering == 0 {
packageExports(fset, pkg)
}
info.PAst = files
}
info.IsMain = pkgname == "main"
}
// get directory information, if any
var dir *Directory
var timestamp time.Time
if tree, ts := h.c.fsTree.Get(); tree != nil && tree.(*Directory) != nil {
// directory tree is present; lookup respective directory
// (may still fail if the file system was updated and the
// new directory tree has not yet been computed)
dir = tree.(*Directory).lookup(abspath)
timestamp = ts
}
if dir == nil {
// no directory tree present (happens in command-line mode);
// compute 2 levels for this page. The second level is to
// get the synopses of sub-directories.
// note: cannot use path filter here because in general
// it doesn't contain the FSTree path
dir = h.c.newDirectory(abspath, 2)
timestamp = time.Now()
}
info.Dirs = dir.listing(true, func(path string) bool { return h.includePath(path, mode) })
info.DirTime = timestamp
info.DirFlat = mode&FlatDir != 0
return info
}
func (h *handlerServer) includePath(path string, mode PageInfoMode) (r bool) {
// if the path is under one of the exclusion paths, don't list.
for _, e := range h.exclude {
if strings.HasPrefix(path, e) {
return false
}
}
// if the path includes 'internal', don't list unless we are in the NoFiltering mode.
if mode&NoFiltering != 0 {
return true
}
if strings.Contains(path, "internal") || strings.Contains(path, "vendor") {
for _, c := range strings.Split(filepath.Clean(path), string(os.PathSeparator)) {
if c == "internal" || c == "vendor" {
return false
}
}
}
return true
}
type funcsByName []*doc.Func
func (s funcsByName) Len() int { return len(s) }
func (s funcsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s funcsByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
func (h *handlerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if redirect(w, r) {
return
}
relpath := pathpkg.Clean(r.URL.Path[len(h.stripPrefix)+1:])
if !h.corpusInitialized() {
h.p.ServeError(w, r, relpath, errors.New("Scan is not yet complete. Please retry after a few moments"))
return
}
abspath := pathpkg.Join(h.fsRoot, relpath)
mode := h.p.GetPageInfoMode(r)
if relpath == builtinPkgPath {
mode = NoFiltering | NoTypeAssoc
}
info := h.GetPageInfo(abspath, relpath, mode, r.FormValue("GOOS"), r.FormValue("GOARCH"))
if info.Err != nil {
log.Print(info.Err)
h.p.ServeError(w, r, relpath, info.Err)
return
}
if mode&NoHTML != 0 {
h.p.ServeText(w, applyTemplate(h.p.PackageText, "packageText", info))
return
}
var tabtitle, title, subtitle string
switch {
case info.PAst != nil:
for _, ast := range info.PAst {
tabtitle = ast.Name.Name
break
}
case info.PDoc != nil:
tabtitle = info.PDoc.Name
default:
tabtitle = info.Dirname
title = "Directory "
if h.p.ShowTimestamps {
subtitle = "Last update: " + info.DirTime.String()
}
}
if title == "" {
if info.IsMain {
// assume that the directory name is the command name
_, tabtitle = pathpkg.Split(relpath)
title = "Command "
} else {
title = "Package "
}
}
title += tabtitle
// special cases for top-level package/command directories
switch tabtitle {
case "/src":
title = "Packages"
tabtitle = "Packages"
case "/src/cmd":
title = "Commands"
tabtitle = "Commands"
}
// Emit JSON array for type information.
pi := h.c.Analysis.PackageInfo(relpath)
info.CallGraphIndex = pi.CallGraphIndex
info.CallGraph = htmltemplate.JS(marshalJSON(pi.CallGraph))
info.AnalysisData = htmltemplate.JS(marshalJSON(pi.Types))
info.TypeInfoIndex = make(map[string]int)
for i, ti := range pi.Types {
info.TypeInfoIndex[ti.Name] = i
}
info.GoogleCN = googleCN(r)
var body []byte
if info.Dirname == "/src" {
body = applyTemplate(h.p.PackageRootHTML, "packageRootHTML", info)
} else {
body = applyTemplate(h.p.PackageHTML, "packageHTML", info)
}
h.p.ServePage(w, Page{
Title: title,
Tabtitle: tabtitle,
Subtitle: subtitle,
Body: body,
GoogleCN: info.GoogleCN,
})
}
func (h *handlerServer) corpusInitialized() bool {
h.c.initMu.RLock()
defer h.c.initMu.RUnlock()
return h.c.initDone
}
type PageInfoMode uint
const (
PageInfoModeQueryString = "m" // query string where PageInfoMode is stored
NoFiltering PageInfoMode = 1 << iota // do not filter exports
AllMethods // show all embedded methods
ShowSource // show source code, do not extract documentation
NoHTML // show result in textual form, do not generate HTML
FlatDir // show directory in a flat (non-indented) manner
NoTypeAssoc // don't associate consts, vars, and factory functions with types
)
// modeNames defines names for each PageInfoMode flag.
var modeNames = map[string]PageInfoMode{
"all": NoFiltering,
"methods": AllMethods,
"src": ShowSource,
"text": NoHTML,
"flat": FlatDir,
}
// generate a query string for persisting PageInfoMode between pages.
func modeQueryString(mode PageInfoMode) string {
if modeNames := mode.names(); len(modeNames) > 0 {
return "?m=" + strings.Join(modeNames, ",")
}
return ""
}
// alphabetically sorted names of active flags for a PageInfoMode.
func (m PageInfoMode) names() []string {
var names []string
for name, mode := range modeNames {
if m&mode != 0 {
names = append(names, name)
}
}
sort.Strings(names)
return names
}
// GetPageInfoMode computes the PageInfoMode flags by analyzing the request
// URL form value "m". It is value is a comma-separated list of mode names
// as defined by modeNames (e.g.: m=src,text).
func (p *Presentation) GetPageInfoMode(r *http.Request) PageInfoMode {
var mode PageInfoMode
for _, k := range strings.Split(r.FormValue(PageInfoModeQueryString), ",") {
if m, found := modeNames[strings.TrimSpace(k)]; found {
mode |= m
}
}
if p.AdjustPageInfoMode != nil {
mode = p.AdjustPageInfoMode(r, mode)
}
return mode
}
// poorMansImporter returns a (dummy) package object named
// by the last path component of the provided package path
// (as is the convention for packages). This is sufficient
// to resolve package identifiers without doing an actual
// import. It never returns an error.
//
func poorMansImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
pkg := imports[path]
if pkg == nil {
// note that strings.LastIndex returns -1 if there is no "/"
pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:])
pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import
imports[path] = pkg
}
return pkg, nil
}
// globalNames returns a set of the names declared by all package-level
// declarations. Method names are returned in the form Receiver_Method.
func globalNames(pkg *ast.Package) map[string]bool {
names := make(map[string]bool)
for _, file := range pkg.Files {
for _, decl := range file.Decls {
addNames(names, decl)
}
}
return names
}
// collectExamples collects examples for pkg from testfiles.
func collectExamples(c *Corpus, pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Example {
var files []*ast.File
for _, f := range testfiles {
files = append(files, f)
}
var examples []*doc.Example
globals := globalNames(pkg)
for _, e := range doc.Examples(files...) {
name := stripExampleSuffix(e.Name)
if name == "" || globals[name] {
examples = append(examples, e)
} else if c.Verbose {
log.Printf("skipping example 'Example%s' because '%s' is not a known function or type", e.Name, e.Name)
}
}
return examples
}
// addNames adds the names declared by decl to the names set.
// Method names are added in the form ReceiverTypeName_Method.
func addNames(names map[string]bool, decl ast.Decl) {
switch d := decl.(type) {
case *ast.FuncDecl:
name := d.Name.Name
if d.Recv != nil {
var typeName string
switch r := d.Recv.List[0].Type.(type) {
case *ast.StarExpr:
typeName = r.X.(*ast.Ident).Name
case *ast.Ident:
typeName = r.Name
}
name = typeName + "_" + name
}
names[name] = true
case *ast.GenDecl:
for _, spec := range d.Specs {
switch s := spec.(type) {
case *ast.TypeSpec:
names[s.Name.Name] = true
case *ast.ValueSpec:
for _, id := range s.Names {
names[id.Name] = true
}
}
}
}
}
// packageExports is a local implementation of ast.PackageExports
// which correctly updates each package file's comment list.
// (The ast.PackageExports signature is frozen, hence the local
// implementation).
//
func packageExports(fset *token.FileSet, pkg *ast.Package) {
for _, src := range pkg.Files {
cmap := ast.NewCommentMap(fset, src, src.Comments)
ast.FileExports(src)
src.Comments = cmap.Filter(src).Comments()
}
}
func applyTemplate(t *template.Template, name string, data interface{}) []byte {
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
log.Printf("%s.Execute: %s", name, err)
}
return buf.Bytes()
}
type writerCapturesErr struct {
w io.Writer
err error
}
func (w *writerCapturesErr) Write(p []byte) (int, error) {
n, err := w.w.Write(p)
if err != nil {
w.err = err
}
return n, err
}
// applyTemplateToResponseWriter uses an http.ResponseWriter as the io.Writer
// for the call to template.Execute. It uses an io.Writer wrapper to capture
// errors from the underlying http.ResponseWriter. Errors are logged only when
// they come from the template processing and not the Writer; this avoid
// polluting log files with error messages due to networking issues, such as
// client disconnects and http HEAD protocol violations.
func applyTemplateToResponseWriter(rw http.ResponseWriter, t *template.Template, data interface{}) {
w := &writerCapturesErr{w: rw}
err := t.Execute(w, data)
// There are some cases where template.Execute does not return an error when
// rw returns an error, and some where it does. So check w.err first.
if w.err == nil && err != nil {
// Log template errors.
log.Printf("%s.Execute: %s", t.Name(), err)
}
}
func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
canonical := pathpkg.Clean(r.URL.Path)
if !strings.HasSuffix(canonical, "/") {
canonical += "/"
}
if r.URL.Path != canonical {
url := *r.URL
url.Path = canonical
http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
redirected = true
}
return
}
func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) {
c := pathpkg.Clean(r.URL.Path)
c = strings.TrimRight(c, "/")
if r.URL.Path != c {
url := *r.URL
url.Path = c
http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
redirected = true
}
return
}
func (p *Presentation) serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
src, err := vfs.ReadFile(p.Corpus.fs, abspath)
if err != nil {
log.Printf("ReadFile: %s", err)
p.ServeError(w, r, relpath, err)
return
}
if r.FormValue(PageInfoModeQueryString) == "text" {
p.ServeText(w, src)
return
}
h := r.FormValue("h")
s := RangeSelection(r.FormValue("s"))
var buf bytes.Buffer
if pathpkg.Ext(abspath) == ".go" {
// Find markup links for this file (e.g. "/src/fmt/print.go").
fi := p.Corpus.Analysis.FileInfo(abspath)
buf.WriteString("<script type='text/javascript'>document.ANALYSIS_DATA = ")
buf.Write(marshalJSON(fi.Data))
buf.WriteString(";</script>\n")
if status := p.Corpus.Analysis.Status(); status != "" {
buf.WriteString("<a href='/lib/godoc/analysis/help.html'>Static analysis features</a> ")
// TODO(adonovan): show analysis status at per-file granularity.
fmt.Fprintf(&buf, "<span style='color: grey'>[%s]</span><br/>", htmlpkg.EscapeString(status))
}
buf.WriteString("<pre>")
formatGoSource(&buf, src, fi.Links, h, s)
buf.WriteString("</pre>")
} else {
buf.WriteString("<pre>")
FormatText(&buf, src, 1, false, h, s)
buf.WriteString("</pre>")
}
fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath))
p.ServePage(w, Page{
Title: title,
SrcPath: relpath,
Tabtitle: relpath,
Body: buf.Bytes(),
GoogleCN: googleCN(r),
})
}
// formatGoSource HTML-escapes Go source text and writes it to w,
// decorating it with the specified analysis links.
//
func formatGoSource(buf *bytes.Buffer, text []byte, links []analysis.Link, pattern string, selection Selection) {
// Emit to a temp buffer so that we can add line anchors at the end.
saved, buf := buf, new(bytes.Buffer)
var i int
var link analysis.Link // shared state of the two funcs below
segmentIter := func() (seg Segment) {
if i < len(links) {
link = links[i]
i++
seg = Segment{link.Start(), link.End()}
}
return
}
linkWriter := func(w io.Writer, offs int, start bool) {
link.Write(w, offs, start)
}
comments := tokenSelection(text, token.COMMENT)
var highlights Selection
if pattern != "" {
highlights = regexpSelection(text, pattern)
}
FormatSelections(buf, text, linkWriter, segmentIter, selectionTag, comments, highlights, selection)
// Now copy buf to saved, adding line anchors.
// The lineSelection mechanism can't be composed with our
// linkWriter, so we have to add line spans as another pass.
n := 1
for _, line := range bytes.Split(buf.Bytes(), []byte("\n")) {
// The line numbers are inserted into the document via a CSS ::before
// pseudo-element. This prevents them from being copied when users
// highlight and copy text.
// ::before is supported in 98% of browsers: https://caniuse.com/#feat=css-gencontent
// This is also the trick Github uses to hide line numbers.
//
// The first tab for the code snippet needs to start in column 9, so
// it indents a full 8 spaces, hence the two nbsp's. Otherwise the tab
// character only indents about two spaces.
fmt.Fprintf(saved, `<span id="L%d" class="ln">%6d&nbsp;&nbsp;</span>`, n, n)
n++
saved.Write(line)
saved.WriteByte('\n')
}
}
func (p *Presentation) serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
if redirect(w, r) {
return
}
list, err := p.Corpus.fs.ReadDir(abspath)
if err != nil {
p.ServeError(w, r, relpath, err)
return
}
p.ServePage(w, Page{
Title: "Directory",
SrcPath: relpath,
Tabtitle: relpath,
Body: applyTemplate(p.DirlistHTML, "dirlistHTML", list),
GoogleCN: googleCN(r),
})
}
func (p *Presentation) ServeHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
// get HTML body contents
src, err := vfs.ReadFile(p.Corpus.fs, abspath)
if err != nil {
log.Printf("ReadFile: %s", err)
p.ServeError(w, r, relpath, err)
return
}
// if it begins with "<!DOCTYPE " assume it is standalone
// html that doesn't need the template wrapping.
if bytes.HasPrefix(src, doctype) {
w.Write(src)
return
}
// if it begins with a JSON blob, read in the metadata.
meta, src, err := extractMetadata(src)
if err != nil {
log.Printf("decoding metadata %s: %v", relpath, err)
}
page := Page{
Title: meta.Title,
Subtitle: meta.Subtitle,
GoogleCN: googleCN(r),
}
// evaluate as template if indicated
if meta.Template {
tmpl, err := template.New("main").Funcs(p.TemplateFuncs()).Parse(string(src))
if err != nil {
log.Printf("parsing template %s: %v", relpath, err)
p.ServeError(w, r, relpath, err)
return
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, page); err != nil {
log.Printf("executing template %s: %v", relpath, err)
p.ServeError(w, r, relpath, err)
return
}
src = buf.Bytes()
}
// if it's the language spec, add tags to EBNF productions
if strings.HasSuffix(abspath, "go_spec.html") {
var buf bytes.Buffer
Linkify(&buf, src)
src = buf.Bytes()
}
page.Body = src
p.ServePage(w, page)
}
func (p *Presentation) ServeFile(w http.ResponseWriter, r *http.Request) {
p.serveFile(w, r)
}
func (p *Presentation) serveFile(w http.ResponseWriter, r *http.Request) {
relpath := r.URL.Path
// Check to see if we need to redirect or serve another file.
if m := p.Corpus.MetadataFor(relpath); m != nil {
if m.Path != relpath {
// Redirect to canonical path.
http.Redirect(w, r, m.Path, http.StatusMovedPermanently)
return
}
// Serve from the actual filesystem path.
relpath = m.filePath
}
abspath := relpath
relpath = relpath[1:] // strip leading slash
switch pathpkg.Ext(relpath) {
case ".html":
if strings.HasSuffix(relpath, "/index.html") {
// We'll show index.html for the directory.
// Use the dir/ version as canonical instead of dir/index.html.
http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently)
return
}
p.ServeHTMLDoc(w, r, abspath, relpath)
return
case ".go":
p.serveTextFile(w, r, abspath, relpath, "Source file")
return
}
dir, err := p.Corpus.fs.Lstat(abspath)
if err != nil {
log.Print(err)
p.ServeError(w, r, relpath, err)
return
}
if dir != nil && dir.IsDir() {
if redirect(w, r) {
return
}
if index := pathpkg.Join(abspath, "index.html"); util.IsTextFile(p.Corpus.fs, index) {
p.ServeHTMLDoc(w, r, index, index)
return
}
p.serveDirectory(w, r, abspath, relpath)
return
}
if util.IsTextFile(p.Corpus.fs, abspath) {
if redirectFile(w, r) {
return
}
p.serveTextFile(w, r, abspath, relpath, "Text file")
return
}
p.fileServer.ServeHTTP(w, r)
}
func (p *Presentation) ServeText(w http.ResponseWriter, text []byte) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Write(text)
}
func marshalJSON(x interface{}) []byte {
var data []byte
var err error
const indentJSON = false // for easier debugging
if indentJSON {
data, err = json.MarshalIndent(x, "", " ")
} else {
data, err = json.Marshal(x)
}
if err != nil {
panic(fmt.Sprintf("json.Marshal failed: %s", err))
}
return data
}

48
vendor/golang.org/x/tools/godoc/server_test.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
// Copyright 2018 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 godoc
import (
"testing"
"golang.org/x/tools/godoc/vfs/mapfs"
)
// TestIgnoredGoFiles tests the scenario where a folder has no .go or .c files,
// but has an ignored go file.
func TestIgnoredGoFiles(t *testing.T) {
packagePath := "github.com/package"
packageComment := "main is documented in an ignored .go file"
c := NewCorpus(mapfs.New(map[string]string{
"src/" + packagePath + "/ignored.go": `// +build ignore
// ` + packageComment + `
package main`}))
srv := &handlerServer{
p: &Presentation{
Corpus: c,
},
c: c,
}
pInfo := srv.GetPageInfo("/src/"+packagePath, packagePath, NoFiltering, "linux", "amd64")
if pInfo.PDoc == nil {
t.Error("pInfo.PDoc = nil; want non-nil.")
} else {
if got, want := pInfo.PDoc.Doc, packageComment+"\n"; got != want {
t.Errorf("pInfo.PDoc.Doc = %q; want %q.", got, want)
}
if got, want := pInfo.PDoc.Name, "main"; got != want {
t.Errorf("pInfo.PDoc.Name = %q; want %q.", got, want)
}
if got, want := pInfo.PDoc.ImportPath, packagePath; got != want {
t.Errorf("pInfo.PDoc.ImportPath = %q; want %q.", got, want)
}
}
if pInfo.FSet == nil {
t.Error("pInfo.FSet = nil; want non-nil.")
}
}

173
vendor/golang.org/x/tools/godoc/short/short.go generated vendored Normal file
View File

@@ -0,0 +1,173 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build appengine
// Package short implements a simple URL shortener, serving an administrative
// interface at /s and shortened urls from /s/key.
// It is designed to run only on the instance of godoc that serves golang.org.
package short
// TODO(adg): collect statistics on URL visits
import (
"errors"
"fmt"
"html/template"
"net/http"
"net/url"
"regexp"
"golang.org/x/net/context"
"google.golang.org/appengine"
"google.golang.org/appengine/datastore"
"google.golang.org/appengine/log"
"google.golang.org/appengine/memcache"
"google.golang.org/appengine/user"
)
const (
prefix = "/s"
kind = "Link"
baseURL = "https://golang.org" + prefix
)
// Link represents a short link.
type Link struct {
Key, Target string
}
var validKey = regexp.MustCompile(`^[a-zA-Z0-9-_.]+$`)
func RegisterHandlers(mux *http.ServeMux) {
mux.HandleFunc(prefix, adminHandler)
mux.HandleFunc(prefix+"/", linkHandler)
}
// linkHandler services requests to short URLs.
// http://golang.org/s/key
// It consults memcache and datastore for the Link for key.
// It then sends a redirects or an error message.
func linkHandler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
key := r.URL.Path[len(prefix)+1:]
if !validKey.MatchString(key) {
http.Error(w, "not found", http.StatusNotFound)
return
}
var link Link
_, err := memcache.JSON.Get(c, cacheKey(key), &link)
if err != nil {
k := datastore.NewKey(c, kind, key, 0, nil)
err = datastore.Get(c, k, &link)
switch err {
case datastore.ErrNoSuchEntity:
http.Error(w, "not found", http.StatusNotFound)
return
default: // != nil
log.Errorf(c, "%q: %v", key, err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
case nil:
item := &memcache.Item{
Key: cacheKey(key),
Object: &link,
}
if err := memcache.JSON.Set(c, item); err != nil {
log.Warningf(c, "%q: %v", key, err)
}
}
}
http.Redirect(w, r, link.Target, http.StatusFound)
}
var adminTemplate = template.Must(template.New("admin").Parse(templateHTML))
// adminHandler serves an administrative interface.
func adminHandler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
if !user.IsAdmin(c) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
var newLink *Link
var doErr error
if r.Method == "POST" {
key := r.FormValue("key")
switch r.FormValue("do") {
case "Add":
newLink = &Link{key, r.FormValue("target")}
doErr = putLink(c, newLink)
case "Delete":
k := datastore.NewKey(c, kind, key, 0, nil)
doErr = datastore.Delete(c, k)
default:
http.Error(w, "unknown action", http.StatusBadRequest)
}
err := memcache.Delete(c, cacheKey(key))
if err != nil && err != memcache.ErrCacheMiss {
log.Warningf(c, "%q: %v", key, err)
}
}
var links []*Link
_, err := datastore.NewQuery(kind).Order("Key").GetAll(c, &links)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.Errorf(c, "%v", err)
return
}
// Put the new link in the list if it's not there already.
// (Eventual consistency means that it might not show up
// immediately, which might be confusing for the user.)
if newLink != nil && doErr == nil {
found := false
for i := range links {
if links[i].Key == newLink.Key {
found = true
break
}
}
if !found {
links = append([]*Link{newLink}, links...)
}
newLink = nil
}
var data = struct {
BaseURL string
Prefix string
Links []*Link
New *Link
Error error
}{baseURL, prefix, links, newLink, doErr}
if err := adminTemplate.Execute(w, &data); err != nil {
log.Criticalf(c, "adminTemplate: %v", err)
}
}
// putLink validates the provided link and puts it into the datastore.
func putLink(c context.Context, link *Link) error {
if !validKey.MatchString(link.Key) {
return errors.New("invalid key; must match " + validKey.String())
}
if _, err := url.Parse(link.Target); err != nil {
return fmt.Errorf("bad target: %v", err)
}
k := datastore.NewKey(c, kind, link.Key, 0, nil)
_, err := datastore.Put(c, k, link)
return err
}
// cacheKey returns a short URL key as a memcache key.
func cacheKey(key string) string {
return "link-" + key
}

121
vendor/golang.org/x/tools/godoc/short/tmpl.go generated vendored Normal file
View File

@@ -0,0 +1,121 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build appengine
package short
const templateHTML = `
<!doctype HTML>
<html>
<head>
<title>golang.org URL shortener</title>
<style>
body {
background: white;
}
input {
border: 1px solid #ccc;
}
input[type=text] {
width: 400px;
}
input, td, th {
color: #333;
font-family: Georgia, Times New Roman, serif;
}
input, td {
font-size: 14pt;
}
th {
font-size: 16pt;
text-align: left;
padding-top: 10px;
}
.autoselect {
border: none;
}
.error {
color: #900;
}
table {
margin-left: auto;
margin-right: auto;
}
</style>
</head>
<body>
<table>
{{with .Error}}
<tr>
<th colspan="3">Error</th>
</tr>
<tr>
<td class="error" colspan="3">{{.}}</td>
</tr>
{{end}}
<tr>
<th>Key</th>
<th>Target</th>
<th></th>
</tr>
<form method="POST" action="{{.Prefix}}">
<tr>
<td><input type="text" name="key"{{with .New}} value="{{.Key}}"{{end}}></td>
<td><input type="text" name="target"{{with .New}} value="{{.Target}}"{{end}}></td>
<td><input type="submit" name="do" value="Add">
</tr>
</form>
{{with .Links}}
<tr>
<th>Short Link</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
</tr>
{{range .}}
<tr>
<td><input class="autoselect" type="text" orig="{{$.BaseURL}}/{{.Key}}" value="{{$.BaseURL}}/{{.Key}}"></td>
<td><input class="autoselect" type="text" orig="{{.Target}}" value="{{.Target}}"></td>
<td>
<form method="POST" action="{{$.Prefix}}">
<input type="hidden" name="key" value="{{.Key}}">
<input type="submit" name="do" value="Delete" class="delete">
</form>
</td>
</tr>
{{end}}
{{end}}
</table>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript">window.jQuery || document.write(unescape("%3Cscript src='/doc/jquery.js' type='text/javascript'%3E%3C/script%3E"));</script>
<script>
$(document).ready(function() {
$('.autoselect').each(function() {
$(this).click(function() {
$(this).select();
});
$(this).change(function() {
$(this).val($(this).attr('orig'));
});
});
$('.delete').click(function(e) {
var link = $(this).closest('tr').find('input').first().val();
var ok = confirm('Delete this link?\n' + link);
if (!ok) {
e.preventDefault();
return false;
}
});
});
</script>
</html>
`

123
vendor/golang.org/x/tools/godoc/snippet.go generated vendored Normal file
View File

@@ -0,0 +1,123 @@
// 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.
// This file contains the infrastructure to create a code
// snippet for search results.
//
// Note: At the moment, this only creates HTML snippets.
package godoc
import (
"bytes"
"fmt"
"go/ast"
"go/token"
)
type Snippet struct {
Line int
Text string // HTML-escaped
}
func (p *Presentation) newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
// TODO instead of pretty-printing the node, should use the original source instead
var buf1 bytes.Buffer
p.writeNode(&buf1, fset, decl)
// wrap text with <pre> tag
var buf2 bytes.Buffer
buf2.WriteString("<pre>")
FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil)
buf2.WriteString("</pre>")
return &Snippet{fset.Position(id.Pos()).Line, buf2.String()}
}
func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec {
for _, spec := range list {
switch s := spec.(type) {
case *ast.ImportSpec:
if s.Name == id {
return s
}
case *ast.ValueSpec:
for _, n := range s.Names {
if n == id {
return s
}
}
case *ast.TypeSpec:
if s.Name == id {
return s
}
}
}
return nil
}
func (p *Presentation) genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet {
s := findSpec(d.Specs, id)
if s == nil {
return nil // declaration doesn't contain id - exit gracefully
}
// only use the spec containing the id for the snippet
dd := &ast.GenDecl{
Doc: d.Doc,
TokPos: d.Pos(),
Tok: d.Tok,
Lparen: d.Lparen,
Specs: []ast.Spec{s},
Rparen: d.Rparen,
}
return p.newSnippet(fset, dd, id)
}
func (p *Presentation) funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet {
if d.Name != id {
return nil // declaration doesn't contain id - exit gracefully
}
// only use the function signature for the snippet
dd := &ast.FuncDecl{
Doc: d.Doc,
Recv: d.Recv,
Name: d.Name,
Type: d.Type,
}
return p.newSnippet(fset, dd, id)
}
// NewSnippet creates a text snippet from a declaration decl containing an
// identifier id. Parts of the declaration not containing the identifier
// may be removed for a more compact snippet.
func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
// TODO(bradfitz, adg): remove this function. But it's used by indexer, which
// doesn't have a *Presentation, and NewSnippet needs a TabWidth.
var p Presentation
p.TabWidth = 4
return p.NewSnippet(fset, decl, id)
}
// NewSnippet creates a text snippet from a declaration decl containing an
// identifier id. Parts of the declaration not containing the identifier
// may be removed for a more compact snippet.
func (p *Presentation) NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
var s *Snippet
switch d := decl.(type) {
case *ast.GenDecl:
s = p.genSnippet(fset, d, id)
case *ast.FuncDecl:
s = p.funcSnippet(fset, d, id)
}
// handle failure gracefully
if s == nil {
var buf bytes.Buffer
fmt.Fprintf(&buf, `<span class="alert">could not generate a snippet for <span class="highlight">%s</span></span>`, id.Name)
s = &Snippet{fset.Position(id.Pos()).Line, buf.String()}
}
return s
}

179
vendor/golang.org/x/tools/godoc/spec.go generated vendored Normal file
View File

@@ -0,0 +1,179 @@
// 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 godoc
// This file contains the mechanism to "linkify" html source
// text containing EBNF sections (as found in go_spec.html).
// The result is the input source text with the EBNF sections
// modified such that identifiers are linked to the respective
// definitions.
import (
"bytes"
"fmt"
"io"
"text/scanner"
)
type ebnfParser struct {
out io.Writer // parser output
src []byte // parser input
scanner scanner.Scanner
prev int // offset of previous token
pos int // offset of current token
tok rune // one token look-ahead
lit string // token literal
}
func (p *ebnfParser) flush() {
p.out.Write(p.src[p.prev:p.pos])
p.prev = p.pos
}
func (p *ebnfParser) next() {
p.tok = p.scanner.Scan()
p.pos = p.scanner.Position.Offset
p.lit = p.scanner.TokenText()
}
func (p *ebnfParser) printf(format string, args ...interface{}) {
p.flush()
fmt.Fprintf(p.out, format, args...)
}
func (p *ebnfParser) errorExpected(msg string) {
p.printf(`<span class="highlight">error: expected %s, found %s</span>`, msg, scanner.TokenString(p.tok))
}
func (p *ebnfParser) expect(tok rune) {
if p.tok != tok {
p.errorExpected(scanner.TokenString(tok))
}
p.next() // make progress in any case
}
func (p *ebnfParser) parseIdentifier(def bool) {
if p.tok == scanner.Ident {
name := p.lit
if def {
p.printf(`<a id="%s">%s</a>`, name, name)
} else {
p.printf(`<a href="#%s" class="noline">%s</a>`, name, name)
}
p.prev += len(name) // skip identifier when printing next time
p.next()
} else {
p.expect(scanner.Ident)
}
}
func (p *ebnfParser) parseTerm() bool {
switch p.tok {
case scanner.Ident:
p.parseIdentifier(false)
case scanner.String, scanner.RawString:
p.next()
const ellipsis = '…' // U+2026, the horizontal ellipsis character
if p.tok == ellipsis {
p.next()
p.expect(scanner.String)
}
case '(':
p.next()
p.parseExpression()
p.expect(')')
case '[':
p.next()
p.parseExpression()
p.expect(']')
case '{':
p.next()
p.parseExpression()
p.expect('}')
default:
return false // no term found
}
return true
}
func (p *ebnfParser) parseSequence() {
if !p.parseTerm() {
p.errorExpected("term")
}
for p.parseTerm() {
}
}
func (p *ebnfParser) parseExpression() {
for {
p.parseSequence()
if p.tok != '|' {
break
}
p.next()
}
}
func (p *ebnfParser) parseProduction() {
p.parseIdentifier(true)
p.expect('=')
if p.tok != '.' {
p.parseExpression()
}
p.expect('.')
}
func (p *ebnfParser) parse(out io.Writer, src []byte) {
// initialize ebnfParser
p.out = out
p.src = src
p.scanner.Init(bytes.NewBuffer(src))
p.next() // initializes pos, tok, lit
// process source
for p.tok != scanner.EOF {
p.parseProduction()
}
p.flush()
}
// Markers around EBNF sections
var (
openTag = []byte(`<pre class="ebnf">`)
closeTag = []byte(`</pre>`)
)
func Linkify(out io.Writer, src []byte) {
for len(src) > 0 {
// i: beginning of EBNF text (or end of source)
i := bytes.Index(src, openTag)
if i < 0 {
i = len(src) - len(openTag)
}
i += len(openTag)
// j: end of EBNF text (or end of source)
j := bytes.Index(src[i:], closeTag) // close marker
if j < 0 {
j = len(src) - i
}
j += i
// write text before EBNF
out.Write(src[0:i])
// process EBNF
var p ebnfParser
p.parse(out, src[i:j])
// advance
src = src[j:]
}
}

22
vendor/golang.org/x/tools/godoc/spec_test.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// Copyright 2018 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 godoc
import (
"bytes"
"strings"
"testing"
)
func TestParseEBNFString(t *testing.T) {
var p ebnfParser
var buf bytes.Buffer
src := []byte("octal_byte_value = `\\` octal_digit octal_digit octal_digit .")
p.parse(&buf, src)
if strings.Contains(buf.String(), "error") {
t.Error(buf.String())
}
}

83
vendor/golang.org/x/tools/godoc/spot.go generated vendored Normal file
View File

@@ -0,0 +1,83 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
// ----------------------------------------------------------------------------
// SpotInfo
// A SpotInfo value describes a particular identifier spot in a given file;
// It encodes three values: the SpotKind (declaration or use), a line or
// snippet index "lori", and whether it's a line or index.
//
// The following encoding is used:
//
// bits 32 4 1 0
// value [lori|kind|isIndex]
//
type SpotInfo uint32
// SpotKind describes whether an identifier is declared (and what kind of
// declaration) or used.
type SpotKind uint32
const (
PackageClause SpotKind = iota
ImportDecl
ConstDecl
TypeDecl
VarDecl
FuncDecl
MethodDecl
Use
nKinds
)
var (
// These must match the SpotKind values above.
name = []string{
"Packages",
"Imports",
"Constants",
"Types",
"Variables",
"Functions",
"Methods",
"Uses",
"Unknown",
}
)
func (x SpotKind) Name() string { return name[x] }
func init() {
// sanity check: if nKinds is too large, the SpotInfo
// accessor functions may need to be updated
if nKinds > 8 {
panic("internal error: nKinds > 8")
}
}
// makeSpotInfo makes a SpotInfo.
func makeSpotInfo(kind SpotKind, lori int, isIndex bool) SpotInfo {
// encode lori: bits [4..32)
x := SpotInfo(lori) << 4
if int(x>>4) != lori {
// lori value doesn't fit - since snippet indices are
// most certainly always smaller then 1<<28, this can
// only happen for line numbers; give it no line number (= 0)
x = 0
}
// encode kind: bits [1..4)
x |= SpotInfo(kind) << 1
// encode isIndex: bit 0
if isIndex {
x |= 1
}
return x
}
func (x SpotInfo) Kind() SpotKind { return SpotKind(x >> 1 & 7) }
func (x SpotInfo) Lori() int { return int(x >> 4) }
func (x SpotInfo) IsIndex() bool { return x&1 != 0 }

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,254 @@
<!--{
"Title": "Static analysis features of godoc"
}-->
<style>
span.err { 'font-size:120%; color:darkred; background-color: yellow; }
img.ss { margin-left: 1in; } /* screenshot */
img.dotted { border: thin dotted; }
</style>
<!-- Images were grabbed from Chrome/Linux at 150% zoom, and are
displayed at 66% of natural size. This allows users to zoom a
little before seeing pixels. -->
<p>
When invoked with the <code>-analysis</code> flag, godoc performs
static analysis on the Go packages it indexes and displays the
results in the source and package views. This document provides a
brief tour of these features.
</p>
<h2>Type analysis features</h2>
<p>
<code>godoc -analysis=type</code> performs static checking similar
to that done by a compiler: it detects ill-formed programs, resolves
each identifier to the entity it denotes, computes the type of each
expression and the method set of each type, and determines which
types are assignable to each interface type.
<b>Type analysis</b> is relatively quick, requiring about 10 seconds for
the &gt;200 packages of the standard library, for example.
</p>
<h3>Compiler errors</h3>
<p>
If any source file contains a compilation error, the source view
will highlight the errant location in red. Hovering over it
displays the error message.
</p>
<img class="ss" width='811' src='error1.png'><br/>
<h3>Identifier resolution</h3>
<p>
In the source view, every referring identifier is annotated with
information about the language entity it refers to: a package,
constant, variable, type, function or statement label.
Hovering over the identifier reveals the entity's kind and type
(e.g. <code>var x int</code> or <code>func f
func(int) string</code>).
</p>
<img class="ss" width='652' src='ident-field.png'><br/>
<br/>
<img class="ss" width='652' src='ident-func.png'>
<p>
Clicking the link takes you to the entity's definition.
</p>
<img class="ss" width='652' src='ident-def.png'><br/>
<h3>Type information: size/alignment, method set, interfaces</h3>
<p>
Clicking on the identifier that defines a named type causes a panel
to appear, displaying information about the named type, including
its size and alignment in bytes, its
<a href='http://golang.org/ref/spec#Method_sets'>method set</a>, and its
<i>implements</i> relation: the set of types T that are assignable to
or from this type U where at least one of T or U is an interface.
This example shows information about <code>net/rpc.methodType</code>.
</p>
<img class="ss" width='470' src='typeinfo-src.png'>
<p>
The method set includes not only the declared methods of the type,
but also any methods "promoted" from anonymous fields of structs,
such as <code>sync.Mutex</code> in this example.
In addition, the receiver type is displayed as <code>*T</code> or
<code>T</code> depending on whether it requires the address or just
a copy of the receiver value.
</p>
<p>
The method set and <i>implements</i> relation are also available
via the package view.
</p>
<img class="ss dotted" width='716' src='typeinfo-pkg.png'>
<h2>Pointer analysis features</h2>
<p>
<code>godoc -analysis=pointer</code> additionally performs a precise
whole-program <b>pointer analysis</b>. In other words, it
approximates the set of memory locations to which each
reference&mdash;not just vars of kind <code>*T</code>, but also
<code>[]T</code>, <code>func</code>, <code>map</code>,
<code>chan</code>, and <code>interface</code>&mdash;may refer. This
information reveals the possible destinations of each dynamic call
(via a <code>func</code> variable or interface method), and the
relationship between send and receive operations on the same
channel.
</p>
<p>
Compared to type analysis, pointer analysis requires more time and
memory, and is impractical for code bases exceeding a million lines.
</p>
<h3>Call graph navigation</h3>
<p>
When pointer analysis is complete, the source view annotates the
code with <b>callers</b> and <b>callees</b> information: callers
information is associated with the <code>func</code> keyword that
declares a function, and callees information is associated with the
open paren '<span style="color: dark-blue"><code>(</code></span>' of
a function call.
</p>
<p>
In this example, hovering over the declaration of the
<code>rot13</code> function (defined in strings/strings_test.go)
reveals that it is called in exactly one place.
</p>
<img class="ss" width='612' src='callers1.png'>
<p>
Clicking the link navigates to the sole caller. (If there were
multiple callers, a list of choices would be displayed first.)
</p>
<img class="ss" width='680' src='callers2.png'>
<p>
Notice that hovering over this call reveals that there are 19
possible callees at this site, of which our <code>rot13</code>
function was just one: this is a dynamic call through a variable of
type <code>func(rune) rune</code>.
Clicking on the call brings up the list of all 19 potential callees,
shown truncated. Many of them are anonymous functions.
</p>
<img class="ss" width='564' src='call3.png'>
<p>
Pointer analysis gives a very precise approximation of the call
graph compared to type-based techniques.
As a case in point, the next example shows the dynamic call inside
the <code>testing</code> package responsible for calling all
user-defined functions named <code>Example<i>XYZ</i></code>.
</p>
<img class="ss" width='361' src='call-eg.png'>
<p>
Recall that all such functions have type <code>func()</code>,
i.e. no arguments and no results. A type-based approximation could
only conclude that this call might dispatch to any function matching
that type&mdash;and these are very numerous in most
programs&mdash;but pointer analysis can track the flow of specific
<code>func</code> values through the testing package.
As an indication of its precision, the result contains only
functions whose name starts with <code>Example</code>.
</p>
<h3>Intra-package call graph</h3>
<p>
The same call graph information is presented in a very different way
in the package view. For each package, an interactive tree view
allows exploration of the call graph as it relates to just that
package; all functions from other packages are elided.
The roots of the tree are the external entry points of the package:
not only its exported functions, but also any unexported or
anonymous functions that are called (dynamically) from outside the
package.
</p>
<p>
This example shows the entry points of the
<code>path/filepath</code> package, with the call graph for
<code>Glob</code> expanded several levels
</p>
<img class="ss dotted" width='501' src='ipcg-pkg.png'>
<p>
Notice that the nodes for Glob and Join appear multiple times: the
tree is a partial unrolling of a cyclic graph; the full unrolling
is in general infinite.
</p>
<p>
For each function documented in the package view, another
interactive tree view allows exploration of the same graph starting
at that function.
This is a portion of the internal graph of
<code>net/http.ListenAndServe</code>.
</p>
<img class="ss dotted" width='455' src='ipcg-func.png'>
<h3>Channel peers (send ↔ receive)</h3>
<p>
Because concurrent Go programs use channels to pass not just values
but also control between different goroutines, it is natural when
reading Go code to want to navigate from a channel send to the
corresponding receive so as to understand the sequence of events.
</p>
<p>
Godoc annotates every channel operation&mdash;make, send, range,
receive, close&mdash;with a link to a panel displaying information
about other operations that might alias the same channel.
</p>
<p>
This example, from the tests of <code>net/http</code>, shows a send
operation on a <code>chan bool</code>.
</p>
<img class="ss" width='811' src='chan1.png'>
<p>
Clicking on the <code>&lt;-</code> send operator reveals that this
channel is made at a unique location (line 332) and that there are
three receive operations that might read this value.
It hardly needs pointing out that some channel element types are
very widely used (e.g. struct{}, bool, int, interface{}) and that a
typical Go program might contain dozens of receive operations on a
value of type <code>chan bool</code>; yet the pointer analysis is
able to distinguish operations on channels at a much finer precision
than based on their type alone.
</p>
<p>
Notice also that the send occurs in a different (anonymous) function
from the outer one containing the <code>make</code> and the receive
operations.
</p>
<p>
Here's another example of send on a different <code>chan
bool</code>, also in package <code>net/http</code>:
</p>
<img class="ss" width='774' src='chan2a.png'>
<p>
The analysis finds just one receive operation that might receive
from this channel, in the test for this feature.
</p>
<img class="ss" width='737' src='chan2b.png'>
<h2>Known issues</h2>
<p>
All analysis results pertain to exactly
one configuration (e.g. amd64 linux). Files that are conditionally
compiled based on different platforms or build tags are not visible
to the analysis.
</p>
<p>
Files that <code>import "C"</code> require
preprocessing by the cgo tool. The file offsets after preprocessing
do not align with the unpreprocessed file, so markup is misaligned.
</p>
<p>
Files are not periodically re-analyzed.
If the files change underneath the running server, the displayed
markup is misaligned.
</p>
<p>
Additional issues are listed at
<a href='https://go.googlesource.com/tools/+/master/godoc/analysis/README'>tools/godoc/analysis/README</a>.
</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

15
vendor/golang.org/x/tools/godoc/static/callgraph.html generated vendored Normal file
View File

@@ -0,0 +1,15 @@
<div class="toggle" style="display: none">
<div class="collapsed">
<p class="exampleHeading toggleButton"><span class="text">Internal call graph</span></p>
</div>
<div class="expanded">
<p class="exampleHeading toggleButton"><span class="text">Internal call graph</span></p>
<p>
This viewer shows the portion of the internal call
graph of this package that is reachable from this function.
See the <a href='#pkg-callgraph'>package's call
graph</a> for more information.
</p>
<ul style="margin-left: 0.5in" id="callgraph-{{.Index}}" class="treeview"></ul>
</div>
</div>

56
vendor/golang.org/x/tools/godoc/static/codewalk.html generated vendored Normal file
View File

@@ -0,0 +1,56 @@
<!--
Copyright 2010 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.
-->
<style type='text/css'>@import "/doc/codewalk/codewalk.css";</style>
<script type="text/javascript" src="/doc/codewalk/codewalk.js"></script>
<div id="codewalk-main">
<div class="left" id="code-column">
<div id='sizer'></div>
<div id="code-area">
<div id="code-header" align="center">
<a id="code-popout-link" href="" target="_blank">
<img title="View code in new window" alt="Pop Out Code" src="/doc/codewalk/popout.png" style="display: block; float: right;"/>
</a>
<select id="code-selector">
{{range .File}}
<option value="/doc/codewalk/?fileprint=/{{urlquery .}}">{{html .}}</option>
{{end}}
</select>
</div>
<div id="code">
<iframe class="code-display" name="code-display" id="code-display"></iframe>
</div>
</div>
<div id="code-options" class="setting">
<span>code on <a id="set-code-left" class="selected" href="#">left</a> &bull; <a id="set-code-right" href="#">right</a></span>
<span>code width <span id="code-column-width">70%</span></span>
<span>filepaths <a id="show-filepaths" class="selected" href="#">shown</a> &bull; <a id="hide-filepaths" href="#">hidden</a></span>
</div>
</div>
<div class="right" id="comment-column">
<div id="comment-area">
{{range .Step}}
<div class="comment first last">
<a class="comment-link" href="/doc/codewalk/?fileprint=/{{urlquery .File}}&amp;lo={{urlquery .Lo}}&amp;hi={{urlquery .Hi}}#mark" target="code-display"></a>
<div class="comment-title">{{html .Title}}</div>
<div class="comment-text">
{{with .Err}}
ERROR LOADING FILE: {{html .}}<br/><br/>
{{end}}
{{.XML}}
</div>
<div class="comment-text file-name"><span class="path-file">{{html .}}</span></div>
</div>
{{end}}
</div>
<div id="comment-options" class="setting">
<a id="prev-comment" href="#"><span class="hotkey">p</span>revious step</a>
&bull;
<a id="next-comment" href="#"><span class="hotkey">n</span>ext step</a>
</div>
</div>
</div>

View File

@@ -0,0 +1,16 @@
<!--
Copyright 2010 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.
-->
<table class="layout">
{{range .}}
<tr>
{{$name_html := html .Name}}
<td><a href="{{$name_html}}">{{$name_html}}</a></td>
<td width="25">&nbsp;</td>
<td>{{html .Title}}</td>
</tr>
{{end}}
</table>

31
vendor/golang.org/x/tools/godoc/static/dirlist.html generated vendored Normal file
View File

@@ -0,0 +1,31 @@
<!--
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.
-->
<p>
<table class="layout">
<tr>
<th align="left">File</th>
<td width="25">&nbsp;</td>
<th align="right">Bytes</th>
<td width="25">&nbsp;</td>
<th align="left">Modified</th>
</tr>
<tr>
<td><a href="..">..</a></td>
</tr>
{{range .}}
<tr>
{{$name_html := fileInfoName . | html}}
<td align="left"><a href="{{$name_html}}">{{$name_html}}</a></td>
<td></td>
<td align="right">{{html .Size}}</td>
<td></td>
<td align="left">{{fileInfoTime . | html}}</td>
</tr>
{{end}}
</table>
</p>

8
vendor/golang.org/x/tools/godoc/static/doc.go generated vendored Normal file
View File

@@ -0,0 +1,8 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package static exports a map of static file content that supports the godoc
// user interface. The map should be used with the mapfs package, see
// golang.org/x/tools/godoc/vfs/mapfs.
package static // import "golang.org/x/tools/godoc/static"

9
vendor/golang.org/x/tools/godoc/static/error.html generated vendored Normal file
View File

@@ -0,0 +1,9 @@
<!--
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.
-->
<p>
<span class="alert" style="font-size:120%">{{html .}}</span>
</p>

30
vendor/golang.org/x/tools/godoc/static/example.html generated vendored Normal file
View File

@@ -0,0 +1,30 @@
<div id="example_{{.Name}}" class="toggle">
<div class="collapsed">
<p class="exampleHeading toggleButton"><span class="text">Example{{example_suffix .Name}}</span></p>
</div>
<div class="expanded">
<p class="exampleHeading toggleButton"><span class="text">Example{{example_suffix .Name}}</span></p>
{{with .Doc}}<p>{{html .}}</p>{{end}}
{{$output := .Output}}
{{with .Play}}
<div class="play">
<div class="input"><textarea class="code" spellcheck="false">{{html .}}</textarea></div>
<div class="output"><pre>{{html $output}}</pre></div>
<div class="buttons">
<a class="run" title="Run this code [shift-enter]">Run</a>
<a class="fmt" title="Format this code">Format</a>
{{if not $.GoogleCN}}
<a class="share" title="Share this code">Share</a>
{{end}}
</div>
</div>
{{else}}
<p>Code:</p>
<pre class="code">{{.Code}}</pre>
{{with .Output}}
<p>Output:</p>
<pre class="output">{{html .}}</pre>
{{end}}
{{end}}
</div>
</div>

111
vendor/golang.org/x/tools/godoc/static/gen.go generated vendored Normal file
View File

@@ -0,0 +1,111 @@
// Copyright 2014 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 static
//go:generate go run makestatic.go
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"time"
"unicode"
)
var files = []string{
"analysis/call3.png",
"analysis/call-eg.png",
"analysis/callers1.png",
"analysis/callers2.png",
"analysis/chan1.png",
"analysis/chan2a.png",
"analysis/chan2b.png",
"analysis/error1.png",
"analysis/help.html",
"analysis/ident-def.png",
"analysis/ident-field.png",
"analysis/ident-func.png",
"analysis/ipcg-func.png",
"analysis/ipcg-pkg.png",
"analysis/typeinfo-pkg.png",
"analysis/typeinfo-src.png",
"callgraph.html",
"codewalk.html",
"codewalkdir.html",
"dirlist.html",
"error.html",
"example.html",
"godoc.html",
"godocs.js",
"images/minus.gif",
"images/plus.gif",
"images/treeview-black-line.gif",
"images/treeview-black.gif",
"images/treeview-default-line.gif",
"images/treeview-default.gif",
"images/treeview-gray-line.gif",
"images/treeview-gray.gif",
"implements.html",
"jquery.js",
"jquery.treeview.css",
"jquery.treeview.edit.js",
"jquery.treeview.js",
"methodset.html",
"opensearch.xml",
"package.html",
"packageroot.html",
"package.txt",
"play.js",
"playground.js",
"search.html",
"search.txt",
"searchcode.html",
"searchdoc.html",
"searchtxt.html",
"style.css",
}
// Generate reads a set of files and returns a file buffer that declares
// a map of string constants containing contents of the input files.
func Generate() ([]byte, error) {
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "%v\n\n%v\n\npackage static\n\n", license, warning)
fmt.Fprintf(buf, "var Files = map[string]string{\n")
for _, fn := range files {
b, err := ioutil.ReadFile(fn)
if err != nil {
return b, err
}
fmt.Fprintf(buf, "\t%q: ", fn)
appendQuote(buf, b)
fmt.Fprintf(buf, ",\n\n")
}
fmt.Fprintln(buf, "}")
return format.Source(buf.Bytes())
}
// appendQuote is like strconv.AppendQuote, but we avoid the latter
// because it changes when Unicode evolves, breaking gen_test.go.
func appendQuote(out *bytes.Buffer, data []byte) {
out.WriteByte('"')
for _, b := range data {
if b == '\\' || b == '"' {
out.WriteByte('\\')
out.WriteByte(b)
} else if b <= unicode.MaxASCII && unicode.IsPrint(rune(b)) && !unicode.IsSpace(rune(b)) {
out.WriteByte(b)
} else {
fmt.Fprintf(out, "\\x%02x", b)
}
}
out.WriteByte('"')
}
const warning = `// Code generated by "makestatic"; DO NOT EDIT.`
var license = fmt.Sprintf(`// Copyright %d 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.`, time.Now().UTC().Year())

49
vendor/golang.org/x/tools/godoc/static/gen_test.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2018 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 static
import (
"bytes"
"io/ioutil"
"strconv"
"testing"
"unicode"
)
func TestStaticIsUpToDate(t *testing.T) {
oldBuf, err := ioutil.ReadFile("static.go")
if err != nil {
t.Errorf("error while reading static.go: %v\n", err)
}
newBuf, err := Generate()
if err != nil {
t.Errorf("error while generating static.go: %v\n", err)
}
if bytes.Compare(oldBuf, newBuf) != 0 {
t.Error(`static.go is stale. Run:
$ go generate golang.org/x/tools/godoc/static
$ git diff
to see the differences.`)
}
}
// TestAppendQuote ensures that AppendQuote produces a valid literal.
func TestAppendQuote(t *testing.T) {
var in, out bytes.Buffer
for r := rune(0); r < unicode.MaxRune; r++ {
in.WriteRune(r)
}
appendQuote(&out, in.Bytes())
in2, err := strconv.Unquote(out.String())
if err != nil {
t.Fatalf("AppendQuote produced invalid string literal: %v", err)
}
if got, want := in2, in.String(); got != want {
t.Fatal("AppendQuote modified string") // no point printing got/want: huge
}
}

117
vendor/golang.org/x/tools/godoc/static/godoc.html generated vendored Normal file
View File

@@ -0,0 +1,117 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#375EAB">
{{with .Tabtitle}}
<title>{{html .}} - The Go Programming Language</title>
{{else}}
<title>The Go Programming Language</title>
{{end}}
<link type="text/css" rel="stylesheet" href="/lib/godoc/style.css">
{{if .SearchBox}}
<link rel="search" type="application/opensearchdescription+xml" title="godoc" href="/opensearch.xml" />
{{end}}
<link rel="stylesheet" href="/lib/godoc/jquery.treeview.css">
<script>window.initFuncs = [];</script>
<script src="/lib/godoc/jquery.js" defer></script>
<script src="/lib/godoc/jquery.treeview.js" defer></script>
<script src="/lib/godoc/jquery.treeview.edit.js" defer></script>
{{if .Playground}}
<script src="/lib/godoc/playground.js" defer></script>
{{end}}
{{with .Version}}<script>var goVersion = {{printf "%q" .}};</script>{{end}}
<script src="/lib/godoc/godocs.js" defer></script>
</head>
<body>
<div id='lowframe' style="position: fixed; bottom: 0; left: 0; height: 0; width: 100%; border-top: thin solid grey; background-color: white; overflow: auto;">
...
</div><!-- #lowframe -->
<div id="topbar"{{if .Title}} class="wide"{{end}}><div class="container">
<div class="top-heading" id="heading-wide"><a href="/">The Go Programming Language</a></div>
<div class="top-heading" id="heading-narrow"><a href="/">Go</a></div>
<a href="#" id="menu-button"><span id="menu-button-arrow">&#9661;</span></a>
<form method="GET" action="/search">
<div id="menu">
<a href="/doc/">Documents</a>
<a href="/pkg/">Packages</a>
<a href="/project/">The Project</a>
<a href="/help/">Help</a>
{{if not .GoogleCN}}
<a href="/blog/">Blog</a>
{{end}}
{{if .Playground}}
<a id="playgroundButton" href="http://play.golang.org/" title="Show Go Playground">Play</a>
{{end}}
<span class="search-box"><input type="search" id="search" name="q" placeholder="Search" aria-label="Search" required><button type="submit"><span><!-- magnifying glass: --><svg width="24" height="24" viewBox="0 0 24 24"><title>submit search</title><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/><path d="M0 0h24v24H0z" fill="none"/></svg></span></button></span>
</div>
</form>
</div></div>
{{if .Playground}}
<div id="playground" class="play">
<div class="input"><textarea class="code" spellcheck="false">package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}</textarea></div>
<div class="output"></div>
<div class="buttons">
<a class="run" title="Run this code [shift-enter]">Run</a>
<a class="fmt" title="Format this code">Format</a>
{{if not $.GoogleCN}}
<a class="share" title="Share this code">Share</a>
{{end}}
</div>
</div>
{{end}}
<div id="page"{{if .Title}} class="wide"{{end}}>
<div class="container">
{{if or .Title .SrcPath}}
<h1>
{{html .Title}}
{{html .SrcPath | srcBreadcrumb}}
</h1>
{{end}}
{{with .Subtitle}}
<h2>{{html .}}</h2>
{{end}}
{{with .SrcPath}}
<h2>
Documentation: {{html . | srcToPkgLink}}
</h2>
{{end}}
{{/* The Table of Contents is automatically inserted in this <div>.
Do not delete this <div>. */}}
<div id="nav"></div>
{{/* Body is HTML-escaped elsewhere */}}
{{printf "%s" .Body}}
<div id="footer">
Build version {{html .Version}}.<br>
Except as <a href="https://developers.google.com/site-policies#restrictions">noted</a>,
the content of this page is licensed under the
Creative Commons Attribution 3.0 License,
and code is licensed under a <a href="/LICENSE">BSD license</a>.<br>
<a href="/doc/tos.html">Terms of Service</a> |
<a href="http://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
</div>
</div><!-- .container -->
</div><!-- #page -->
</body>
</html>

626
vendor/golang.org/x/tools/godoc/static/godocs.js generated vendored Normal file
View File

@@ -0,0 +1,626 @@
// Copyright 2012 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.
/* A little code to ease navigation of these documents.
*
* On window load we:
* + Generate a table of contents (generateTOC)
* + Bind foldable sections (bindToggles)
* + Bind links to foldable sections (bindToggleLinks)
*/
(function() {
'use strict';
// Mobile-friendly topbar menu
$(function() {
var menu = $('#menu');
var menuButton = $('#menu-button');
var menuButtonArrow = $('#menu-button-arrow');
menuButton.click(function(event) {
menu.toggleClass('menu-visible');
menuButtonArrow.toggleClass('vertical-flip');
event.preventDefault();
return false;
});
});
/* Generates a table of contents: looks for h2 and h3 elements and generates
* links. "Decorates" the element with id=="nav" with this table of contents.
*/
function generateTOC() {
if ($('#manual-nav').length > 0) {
return;
}
var nav = $('#nav');
if (nav.length === 0) {
return;
}
var toc_items = [];
$(nav).nextAll('h2, h3').each(function() {
var node = this;
if (node.id == '')
node.id = 'tmp_' + toc_items.length;
var link = $('<a/>').attr('href', '#' + node.id).text($(node).text());
var item;
if ($(node).is('h2')) {
item = $('<dt/>');
} else { // h3
item = $('<dd class="indent"/>');
}
item.append(link);
toc_items.push(item);
});
if (toc_items.length <= 1) {
return;
}
var dl1 = $('<dl/>');
var dl2 = $('<dl/>');
var split_index = (toc_items.length / 2) + 1;
if (split_index < 8) {
split_index = toc_items.length;
}
for (var i = 0; i < split_index; i++) {
dl1.append(toc_items[i]);
}
for (/* keep using i */; i < toc_items.length; i++) {
dl2.append(toc_items[i]);
}
var tocTable = $('<table class="unruled"/>').appendTo(nav);
var tocBody = $('<tbody/>').appendTo(tocTable);
var tocRow = $('<tr/>').appendTo(tocBody);
// 1st column
$('<td class="first"/>').appendTo(tocRow).append(dl1);
// 2nd column
$('<td/>').appendTo(tocRow).append(dl2);
}
function bindToggle(el) {
$('.toggleButton', el).click(function() {
if ($(this).closest(".toggle, .toggleVisible")[0] != el) {
// Only trigger the closest toggle header.
return;
}
if ($(el).is('.toggle')) {
$(el).addClass('toggleVisible').removeClass('toggle');
} else {
$(el).addClass('toggle').removeClass('toggleVisible');
}
});
}
function bindToggles(selector) {
$(selector).each(function(i, el) {
bindToggle(el);
});
}
function bindToggleLink(el, prefix) {
$(el).click(function() {
var href = $(el).attr('href');
var i = href.indexOf('#'+prefix);
if (i < 0) {
return;
}
var id = '#' + prefix + href.slice(i+1+prefix.length);
if ($(id).is('.toggle')) {
$(id).find('.toggleButton').first().click();
}
});
}
function bindToggleLinks(selector, prefix) {
$(selector).each(function(i, el) {
bindToggleLink(el, prefix);
});
}
function setupDropdownPlayground() {
if (!$('#page').is('.wide')) {
return; // don't show on front page
}
var button = $('#playgroundButton');
var div = $('#playground');
var setup = false;
button.toggle(function() {
button.addClass('active');
div.show();
if (setup) {
return;
}
setup = true;
playground({
'codeEl': $('.code', div),
'outputEl': $('.output', div),
'runEl': $('.run', div),
'fmtEl': $('.fmt', div),
'shareEl': $('.share', div),
'shareRedirect': '//play.golang.org/p/'
});
},
function() {
button.removeClass('active');
div.hide();
});
button.show();
$('#menu').css('min-width', '+=60');
}
function setupInlinePlayground() {
'use strict';
// Set up playground when each element is toggled.
$('div.play').each(function (i, el) {
// Set up playground for this example.
var setup = function() {
var code = $('.code', el);
playground({
'codeEl': code,
'outputEl': $('.output', el),
'runEl': $('.run', el),
'fmtEl': $('.fmt', el),
'shareEl': $('.share', el),
'shareRedirect': '//play.golang.org/p/'
});
// Make the code textarea resize to fit content.
var resize = function() {
code.height(0);
var h = code[0].scrollHeight;
code.height(h+20); // minimize bouncing.
code.closest('.input').height(h);
};
code.on('keydown', resize);
code.on('keyup', resize);
code.keyup(); // resize now.
};
// If example already visible, set up playground now.
if ($(el).is(':visible')) {
setup();
return;
}
// Otherwise, set up playground when example is expanded.
var built = false;
$(el).closest('.toggle').click(function() {
// Only set up once.
if (!built) {
setup();
built = true;
}
});
});
}
// fixFocus tries to put focus to div#page so that keyboard navigation works.
function fixFocus() {
var page = $('div#page');
var topbar = $('div#topbar');
page.css('outline', 0); // disable outline when focused
page.attr('tabindex', -1); // and set tabindex so that it is focusable
$(window).resize(function (evt) {
// only focus page when the topbar is at fixed position (that is, it's in
// front of page, and keyboard event will go to the former by default.)
// by focusing page, keyboard event will go to page so that up/down arrow,
// space, etc. will work as expected.
if (topbar.css('position') == "fixed")
page.focus();
}).resize();
}
function toggleHash() {
var id = window.location.hash.substring(1);
// Open all of the toggles for a particular hash.
var els = $(
document.getElementById(id),
$('a[name]').filter(function() {
return $(this).attr('name') == id;
})
);
while (els.length) {
for (var i = 0; i < els.length; i++) {
var el = $(els[i]);
if (el.is('.toggle')) {
el.find('.toggleButton').first().click();
}
}
els = el.parent();
}
}
function personalizeInstallInstructions() {
var prefix = '?download=';
var s = window.location.search;
if (s.indexOf(prefix) != 0) {
// No 'download' query string; detect "test" instructions from User Agent.
if (navigator.platform.indexOf('Win') != -1) {
$('.testUnix').hide();
$('.testWindows').show();
} else {
$('.testUnix').show();
$('.testWindows').hide();
}
return;
}
var filename = s.substr(prefix.length);
var filenameRE = /^go1\.\d+(\.\d+)?([a-z0-9]+)?\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\.[68])?\.([a-z.]+)$/;
$('.downloadFilename').text(filename);
$('.hideFromDownload').hide();
var m = filenameRE.exec(filename);
if (!m) {
// Can't interpret file name; bail.
return;
}
var os = m[3];
var ext = m[6];
if (ext != 'tar.gz') {
$('#tarballInstructions').hide();
}
if (os != 'darwin' || ext != 'pkg') {
$('#darwinPackageInstructions').hide();
}
if (os != 'windows') {
$('#windowsInstructions').hide();
$('.testUnix').show();
$('.testWindows').hide();
} else {
if (ext != 'msi') {
$('#windowsInstallerInstructions').hide();
}
if (ext != 'zip') {
$('#windowsZipInstructions').hide();
}
$('.testUnix').hide();
$('.testWindows').show();
}
var download = "https://dl.google.com/go/" + filename;
var message = $('<p class="downloading">'+
'Your download should begin shortly. '+
'If it does not, click <a>this link</a>.</p>');
message.find('a').attr('href', download);
message.insertAfter('#nav');
window.location = download;
}
function updateVersionTags() {
var v = window.goVersion;
if (/^go[0-9.]+$/.test(v)) {
$(".versionTag").empty().text(v);
$(".whereTag").hide();
}
}
function addPermalinks() {
function addPermalink(source, parent) {
var id = source.attr("id");
if (id == "" || id.indexOf("tmp_") === 0) {
// Auto-generated permalink.
return;
}
if (parent.find("> .permalink").length) {
// Already attached.
return;
}
parent.append(" ").append($("<a class='permalink'>&#xb6;</a>").attr("href", "#" + id));
}
$("#page .container").find("h2[id], h3[id]").each(function() {
var el = $(this);
addPermalink(el, el);
});
$("#page .container").find("dl[id]").each(function() {
var el = $(this);
// Add the anchor to the "dt" element.
addPermalink(el, el.find("> dt").first());
});
}
$(".js-expandAll").click(function() {
if ($(this).hasClass("collapsed")) {
toggleExamples('toggle');
$(this).text("(Collapse All)");
} else {
toggleExamples('toggleVisible');
$(this).text("(Expand All)");
}
$(this).toggleClass("collapsed")
});
function toggleExamples(className) {
// We need to explicitly iterate through divs starting with "example_"
// to avoid toggling Overview and Index collapsibles.
$("[id^='example_']").each(function() {
// Check for state and click it only if required.
if ($(this).hasClass(className)) {
$(this).find('.toggleButton').first().click();
}
});
}
$(document).ready(function() {
generateTOC();
addPermalinks();
bindToggles(".toggle");
bindToggles(".toggleVisible");
bindToggleLinks(".exampleLink", "example_");
bindToggleLinks(".overviewLink", "");
bindToggleLinks(".examplesLink", "");
bindToggleLinks(".indexLink", "");
setupDropdownPlayground();
setupInlinePlayground();
fixFocus();
setupTypeInfo();
setupCallgraphs();
toggleHash();
personalizeInstallInstructions();
updateVersionTags();
// godoc.html defines window.initFuncs in the <head> tag, and root.html and
// codewalk.js push their on-page-ready functions to the list.
// We execute those functions here, to avoid loading jQuery until the page
// content is loaded.
for (var i = 0; i < window.initFuncs.length; i++) window.initFuncs[i]();
});
// -- analysis ---------------------------------------------------------
// escapeHTML returns HTML for s, with metacharacters quoted.
// It is safe for use in both elements and attributes
// (unlike the "set innerText, read innerHTML" trick).
function escapeHTML(s) {
return s.replace(/&/g, '&amp;').
replace(/\"/g, '&quot;').
replace(/\'/g, '&#39;').
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
}
// makeAnchor returns HTML for an <a> element, given an anchorJSON object.
function makeAnchor(json) {
var html = escapeHTML(json.Text);
if (json.Href != "") {
html = "<a href='" + escapeHTML(json.Href) + "'>" + html + "</a>";
}
return html;
}
function showLowFrame(html) {
var lowframe = document.getElementById('lowframe');
lowframe.style.height = "200px";
lowframe.innerHTML = "<p style='text-align: left;'>" + html + "</p>\n" +
"<div onclick='hideLowFrame()' style='position: absolute; top: 0; right: 0; cursor: pointer;'>✘</div>"
};
document.hideLowFrame = function() {
var lowframe = document.getElementById('lowframe');
lowframe.style.height = "0px";
}
// onClickCallers is the onclick action for the 'func' tokens of a
// function declaration.
document.onClickCallers = function(index) {
var data = document.ANALYSIS_DATA[index]
if (data.Callers.length == 1 && data.Callers[0].Sites.length == 1) {
document.location = data.Callers[0].Sites[0].Href; // jump to sole caller
return;
}
var html = "Callers of <code>" + escapeHTML(data.Callee) + "</code>:<br/>\n";
for (var i = 0; i < data.Callers.length; i++) {
var caller = data.Callers[i];
html += "<code>" + escapeHTML(caller.Func) + "</code>";
var sites = caller.Sites;
if (sites != null && sites.length > 0) {
html += " at line ";
for (var j = 0; j < sites.length; j++) {
if (j > 0) {
html += ", ";
}
html += "<code>" + makeAnchor(sites[j]) + "</code>";
}
}
html += "<br/>\n";
}
showLowFrame(html);
};
// onClickCallees is the onclick action for the '(' token of a function call.
document.onClickCallees = function(index) {
var data = document.ANALYSIS_DATA[index]
if (data.Callees.length == 1) {
document.location = data.Callees[0].Href; // jump to sole callee
return;
}
var html = "Callees of this " + escapeHTML(data.Descr) + ":<br/>\n";
for (var i = 0; i < data.Callees.length; i++) {
html += "<code>" + makeAnchor(data.Callees[i]) + "</code><br/>\n";
}
showLowFrame(html);
};
// onClickTypeInfo is the onclick action for identifiers declaring a named type.
document.onClickTypeInfo = function(index) {
var data = document.ANALYSIS_DATA[index];
var html = "Type <code>" + data.Name + "</code>: " +
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>(size=" + data.Size + ", align=" + data.Align + ")</small><br/>\n";
html += implementsHTML(data);
html += methodsetHTML(data);
showLowFrame(html);
};
// implementsHTML returns HTML for the implements relation of the
// specified TypeInfoJSON value.
function implementsHTML(info) {
var html = "";
if (info.ImplGroups != null) {
for (var i = 0; i < info.ImplGroups.length; i++) {
var group = info.ImplGroups[i];
var x = "<code>" + escapeHTML(group.Descr) + "</code> ";
for (var j = 0; j < group.Facts.length; j++) {
var fact = group.Facts[j];
var y = "<code>" + makeAnchor(fact.Other) + "</code>";
if (fact.ByKind != null) {
html += escapeHTML(fact.ByKind) + " type " + y + " implements " + x;
} else {
html += x + " implements " + y;
}
html += "<br/>\n";
}
}
}
return html;
}
// methodsetHTML returns HTML for the methodset of the specified
// TypeInfoJSON value.
function methodsetHTML(info) {
var html = "";
if (info.Methods != null) {
for (var i = 0; i < info.Methods.length; i++) {
html += "<code>" + makeAnchor(info.Methods[i]) + "</code><br/>\n";
}
}
return html;
}
// onClickComm is the onclick action for channel "make" and "<-"
// send/receive tokens.
document.onClickComm = function(index) {
var ops = document.ANALYSIS_DATA[index].Ops
if (ops.length == 1) {
document.location = ops[0].Op.Href; // jump to sole element
return;
}
var html = "Operations on this channel:<br/>\n";
for (var i = 0; i < ops.length; i++) {
html += makeAnchor(ops[i].Op) + " by <code>" + escapeHTML(ops[i].Fn) + "</code><br/>\n";
}
if (ops.length == 0) {
html += "(none)<br/>\n";
}
showLowFrame(html);
};
$(window).load(function() {
// Scroll window so that first selection is visible.
// (This means we don't need to emit id='L%d' spans for each line.)
// TODO(adonovan): ideally, scroll it so that it's under the pointer,
// but I don't know how to get the pointer y coordinate.
var elts = document.getElementsByClassName("selection");
if (elts.length > 0) {
elts[0].scrollIntoView()
}
});
// setupTypeInfo populates the "Implements" and "Method set" toggle for
// each type in the package doc.
function setupTypeInfo() {
for (var i in document.ANALYSIS_DATA) {
var data = document.ANALYSIS_DATA[i];
var el = document.getElementById("implements-" + i);
if (el != null) {
// el != null => data is TypeInfoJSON.
if (data.ImplGroups != null) {
el.innerHTML = implementsHTML(data);
el.parentNode.parentNode.style.display = "block";
}
}
var el = document.getElementById("methodset-" + i);
if (el != null) {
// el != null => data is TypeInfoJSON.
if (data.Methods != null) {
el.innerHTML = methodsetHTML(data);
el.parentNode.parentNode.style.display = "block";
}
}
}
}
function setupCallgraphs() {
if (document.CALLGRAPH == null) {
return
}
document.getElementById("pkg-callgraph").style.display = "block";
var treeviews = document.getElementsByClassName("treeview");
for (var i = 0; i < treeviews.length; i++) {
var tree = treeviews[i];
if (tree.id == null || tree.id.indexOf("callgraph-") != 0) {
continue;
}
var id = tree.id.substring("callgraph-".length);
$(tree).treeview({collapsed: true, animated: "fast"});
document.cgAddChildren(tree, tree, [id]);
tree.parentNode.parentNode.style.display = "block";
}
}
document.cgAddChildren = function(tree, ul, indices) {
if (indices != null) {
for (var i = 0; i < indices.length; i++) {
var li = cgAddChild(tree, ul, document.CALLGRAPH[indices[i]]);
if (i == indices.length - 1) {
$(li).addClass("last");
}
}
}
$(tree).treeview({animated: "fast", add: ul});
}
// cgAddChild adds an <li> element for document.CALLGRAPH node cgn to
// the parent <ul> element ul. tree is the tree's root <ul> element.
function cgAddChild(tree, ul, cgn) {
var li = document.createElement("li");
ul.appendChild(li);
li.className = "closed";
var code = document.createElement("code");
if (cgn.Callees != null) {
$(li).addClass("expandable");
// Event handlers and innerHTML updates don't play nicely together,
// hence all this explicit DOM manipulation.
var hitarea = document.createElement("div");
hitarea.className = "hitarea expandable-hitarea";
li.appendChild(hitarea);
li.appendChild(code);
var childUL = document.createElement("ul");
li.appendChild(childUL);
childUL.setAttribute('style', "display: none;");
var onClick = function() {
document.cgAddChildren(tree, childUL, cgn.Callees);
hitarea.removeEventListener('click', onClick)
};
hitarea.addEventListener('click', onClick);
} else {
li.appendChild(code);
}
code.innerHTML += "&nbsp;" + makeAnchor(cgn.Func);
return li
}
})();

BIN
vendor/golang.org/x/tools/godoc/static/images/minus.gif generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

BIN
vendor/golang.org/x/tools/godoc/static/images/plus.gif generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

View File

@@ -0,0 +1,9 @@
<div class="toggle" style="display: none">
<div class="collapsed">
<p class="exampleHeading toggleButton"><span class="text">Implements</span></p>
</div>
<div class="expanded">
<p class="exampleHeading toggleButton"><span class="text">Implements</span></p>
<div style="margin-left: 1in" id='implements-{{.Index}}'>...</div>
</div>
</div>

2
vendor/golang.org/x/tools/godoc/static/jquery.js generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,76 @@
/* https://github.com/jzaefferer/jquery-treeview/blob/master/jquery.treeview.css */
/* License: MIT. */
.treeview, .treeview ul {
padding: 0;
margin: 0;
list-style: none;
}
.treeview ul {
background-color: white;
margin-top: 4px;
}
.treeview .hitarea {
background: url(images/treeview-default.gif) -64px -25px no-repeat;
height: 16px;
width: 16px;
margin-left: -16px;
float: left;
cursor: pointer;
}
/* fix for IE6 */
* html .hitarea {
display: inline;
float:none;
}
.treeview li {
margin: 0;
padding: 3px 0pt 3px 16px;
}
.treeview a.selected {
background-color: #eee;
}
#treecontrol { margin: 1em 0; display: none; }
.treeview .hover { color: red; cursor: pointer; }
.treeview li { background: url(images/treeview-default-line.gif) 0 0 no-repeat; }
.treeview li.collapsable, .treeview li.expandable { background-position: 0 -176px; }
.treeview .expandable-hitarea { background-position: -80px -3px; }
.treeview li.last { background-position: 0 -1766px }
.treeview li.lastCollapsable, .treeview li.lastExpandable { background-image: url(images/treeview-default.gif); }
.treeview li.lastCollapsable { background-position: 0 -111px }
.treeview li.lastExpandable { background-position: -32px -67px }
.treeview div.lastCollapsable-hitarea, .treeview div.lastExpandable-hitarea { background-position: 0; }
.treeview-red li { background-image: url(images/treeview-red-line.gif); }
.treeview-red .hitarea, .treeview-red li.lastCollapsable, .treeview-red li.lastExpandable { background-image: url(images/treeview-red.gif); }
.treeview-black li { background-image: url(images/treeview-black-line.gif); }
.treeview-black .hitarea, .treeview-black li.lastCollapsable, .treeview-black li.lastExpandable { background-image: url(images/treeview-black.gif); }
.treeview-gray li { background-image: url(images/treeview-gray-line.gif); }
.treeview-gray .hitarea, .treeview-gray li.lastCollapsable, .treeview-gray li.lastExpandable { background-image: url(images/treeview-gray.gif); }
.treeview-famfamfam li { background-image: url(images/treeview-famfamfam-line.gif); }
.treeview-famfamfam .hitarea, .treeview-famfamfam li.lastCollapsable, .treeview-famfamfam li.lastExpandable { background-image: url(images/treeview-famfamfam.gif); }
.treeview .placeholder {
background: url(images/ajax-loader.gif) 0 0 no-repeat;
height: 16px;
width: 16px;
display: block;
}
.filetree li { padding: 3px 0 2px 16px; }
.filetree span.folder, .filetree span.file { padding: 1px 0 1px 16px; display: block; }
.filetree span.folder { background: url(images/folder.gif) 0 0 no-repeat; }
.filetree li.expandable span.folder { background: url(images/folder-closed.gif) 0 0 no-repeat; }
.filetree span.file { background: url(images/file.gif) 0 0 no-repeat; }

View File

@@ -0,0 +1,39 @@
/* https://github.com/jzaefferer/jquery-treeview/blob/master/jquery.treeview.edit.js */
/* License: MIT. */
(function($) {
var CLASSES = $.treeview.classes;
var proxied = $.fn.treeview;
$.fn.treeview = function(settings) {
settings = $.extend({}, settings);
if (settings.add) {
return this.trigger("add", [settings.add]);
}
if (settings.remove) {
return this.trigger("remove", [settings.remove]);
}
return proxied.apply(this, arguments).bind("add", function(event, branches) {
$(branches).prev()
.removeClass(CLASSES.last)
.removeClass(CLASSES.lastCollapsable)
.removeClass(CLASSES.lastExpandable)
.find(">.hitarea")
.removeClass(CLASSES.lastCollapsableHitarea)
.removeClass(CLASSES.lastExpandableHitarea);
$(branches).find("li").andSelf().prepareBranches(settings).applyClasses(settings, $(this).data("toggler"));
}).bind("remove", function(event, branches) {
var prev = $(branches).prev();
var parent = $(branches).parent();
$(branches).remove();
prev.filter(":last-child").addClass(CLASSES.last)
.filter("." + CLASSES.expandable).replaceClass(CLASSES.last, CLASSES.lastExpandable).end()
.find(">.hitarea").replaceClass(CLASSES.expandableHitarea, CLASSES.lastExpandableHitarea).end()
.filter("." + CLASSES.collapsable).replaceClass(CLASSES.last, CLASSES.lastCollapsable).end()
.find(">.hitarea").replaceClass(CLASSES.collapsableHitarea, CLASSES.lastCollapsableHitarea);
if (parent.is(":not(:has(>))") && parent[0] != this) {
parent.parent().removeClass(CLASSES.collapsable).removeClass(CLASSES.expandable)
parent.siblings(".hitarea").andSelf().remove();
}
});
};
})(jQuery);

View File

@@ -0,0 +1,256 @@
/*
* Treeview 1.4.1 - jQuery plugin to hide and show branches of a tree
*
* http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
* http://docs.jquery.com/Plugins/Treeview
*
* Copyright (c) 2007 Jörn Zaefferer
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Revision: $Id: jquery.treeview.js 5759 2008-07-01 07:50:28Z joern.zaefferer $
*
*/
;(function($) {
// TODO rewrite as a widget, removing all the extra plugins
$.extend($.fn, {
swapClass: function(c1, c2) {
var c1Elements = this.filter('.' + c1);
this.filter('.' + c2).removeClass(c2).addClass(c1);
c1Elements.removeClass(c1).addClass(c2);
return this;
},
replaceClass: function(c1, c2) {
return this.filter('.' + c1).removeClass(c1).addClass(c2).end();
},
hoverClass: function(className) {
className = className || "hover";
return this.hover(function() {
$(this).addClass(className);
}, function() {
$(this).removeClass(className);
});
},
heightToggle: function(animated, callback) {
animated ?
this.animate({ height: "toggle" }, animated, callback) :
this.each(function(){
jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
if(callback)
callback.apply(this, arguments);
});
},
heightHide: function(animated, callback) {
if (animated) {
this.animate({ height: "hide" }, animated, callback);
} else {
this.hide();
if (callback)
this.each(callback);
}
},
prepareBranches: function(settings) {
if (!settings.prerendered) {
// mark last tree items
this.filter(":last-child:not(ul)").addClass(CLASSES.last);
// collapse whole tree, or only those marked as closed, anyway except those marked as open
this.filter((settings.collapsed ? "" : "." + CLASSES.closed) + ":not(." + CLASSES.open + ")").find(">ul").hide();
}
// return all items with sublists
return this.filter(":has(>ul)");
},
applyClasses: function(settings, toggler) {
// TODO use event delegation
this.filter(":has(>ul):not(:has(>a))").find(">span").unbind("click.treeview").bind("click.treeview", function(event) {
// don't handle click events on children, eg. checkboxes
if ( this == event.target )
toggler.apply($(this).next());
}).add( $("a", this) ).hoverClass();
if (!settings.prerendered) {
// handle closed ones first
this.filter(":has(>ul:hidden)")
.addClass(CLASSES.expandable)
.replaceClass(CLASSES.last, CLASSES.lastExpandable);
// handle open ones
this.not(":has(>ul:hidden)")
.addClass(CLASSES.collapsable)
.replaceClass(CLASSES.last, CLASSES.lastCollapsable);
// create hitarea if not present
var hitarea = this.find("div." + CLASSES.hitarea);
if (!hitarea.length)
hitarea = this.prepend("<div class=\"" + CLASSES.hitarea + "\"/>").find("div." + CLASSES.hitarea);
hitarea.removeClass().addClass(CLASSES.hitarea).each(function() {
var classes = "";
$.each($(this).parent().attr("class").split(" "), function() {
classes += this + "-hitarea ";
});
$(this).addClass( classes );
})
}
// apply event to hitarea
this.find("div." + CLASSES.hitarea).click( toggler );
},
treeview: function(settings) {
settings = $.extend({
cookieId: "treeview"
}, settings);
if ( settings.toggle ) {
var callback = settings.toggle;
settings.toggle = function() {
return callback.apply($(this).parent()[0], arguments);
};
}
// factory for treecontroller
function treeController(tree, control) {
// factory for click handlers
function handler(filter) {
return function() {
// reuse toggle event handler, applying the elements to toggle
// start searching for all hitareas
toggler.apply( $("div." + CLASSES.hitarea, tree).filter(function() {
// for plain toggle, no filter is provided, otherwise we need to check the parent element
return filter ? $(this).parent("." + filter).length : true;
}) );
return false;
};
}
// click on first element to collapse tree
$("a:eq(0)", control).click( handler(CLASSES.collapsable) );
// click on second to expand tree
$("a:eq(1)", control).click( handler(CLASSES.expandable) );
// click on third to toggle tree
$("a:eq(2)", control).click( handler() );
}
// handle toggle event
function toggler() {
$(this)
.parent()
// swap classes for hitarea
.find(">.hitarea")
.swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
.swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea )
.end()
// swap classes for parent li
.swapClass( CLASSES.collapsable, CLASSES.expandable )
.swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
// find child lists
.find( ">ul" )
// toggle them
.heightToggle( settings.animated, settings.toggle );
if ( settings.unique ) {
$(this).parent()
.siblings()
// swap classes for hitarea
.find(">.hitarea")
.replaceClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
.replaceClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea )
.end()
.replaceClass( CLASSES.collapsable, CLASSES.expandable )
.replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
.find( ">ul" )
.heightHide( settings.animated, settings.toggle );
}
}
this.data("toggler", toggler);
function serialize() {
function binary(arg) {
return arg ? 1 : 0;
}
var data = [];
branches.each(function(i, e) {
data[i] = $(e).is(":has(>ul:visible)") ? 1 : 0;
});
$.cookie(settings.cookieId, data.join(""), settings.cookieOptions );
}
function deserialize() {
var stored = $.cookie(settings.cookieId);
if ( stored ) {
var data = stored.split("");
branches.each(function(i, e) {
$(e).find(">ul")[ parseInt(data[i]) ? "show" : "hide" ]();
});
}
}
// add treeview class to activate styles
this.addClass("treeview");
// prepare branches and find all tree items with child lists
var branches = this.find("li").prepareBranches(settings);
switch(settings.persist) {
case "cookie":
var toggleCallback = settings.toggle;
settings.toggle = function() {
serialize();
if (toggleCallback) {
toggleCallback.apply(this, arguments);
}
};
deserialize();
break;
case "location":
var current = this.find("a").filter(function() {
return this.href.toLowerCase() == location.href.toLowerCase();
});
if ( current.length ) {
// TODO update the open/closed classes
var items = current.addClass("selected").parents("ul, li").add( current.next() ).show();
if (settings.prerendered) {
// if prerendered is on, replicate the basic class swapping
items.filter("li")
.swapClass( CLASSES.collapsable, CLASSES.expandable )
.swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
.find(">.hitarea")
.swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
.swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea );
}
}
break;
}
branches.applyClasses(settings, toggler);
// if control option is set, create the treecontroller and show it
if ( settings.control ) {
treeController(this, settings.control);
$(settings.control).show();
}
return this;
}
});
// classes used by the plugin
// need to be styled via external stylesheet, see first example
$.treeview = {};
var CLASSES = ($.treeview.classes = {
open: "open",
closed: "closed",
expandable: "expandable",
expandableHitarea: "expandable-hitarea",
lastExpandableHitarea: "lastExpandable-hitarea",
collapsable: "collapsable",
collapsableHitarea: "collapsable-hitarea",
lastCollapsableHitarea: "lastCollapsable-hitarea",
lastCollapsable: "lastCollapsable",
lastExpandable: "lastExpandable",
last: "last",
hitarea: "hitarea"
});
})(jQuery);

36
vendor/golang.org/x/tools/godoc/static/makestatic.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
// Command makestatic writes the generated file buffer to "static.go".
// It is intended to be invoked via "go generate" (directive in "gen.go").
package main
import (
"fmt"
"io/ioutil"
"os"
"golang.org/x/tools/godoc/static"
)
func main() {
if err := makestatic(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func makestatic() error {
buf, err := static.Generate()
if err != nil {
return fmt.Errorf("error while generating static.go: %v\n", err)
}
err = ioutil.WriteFile("static.go", buf, 0666)
if err != nil {
return fmt.Errorf("error while writing static.go: %v\n", err)
}
return nil
}

View File

@@ -0,0 +1,9 @@
<div class="toggle" style="display: none">
<div class="collapsed">
<p class="exampleHeading toggleButton"><span class="text">Method set</span></p>
</div>
<div class="expanded">
<p class="exampleHeading toggleButton"><span class="text">Method set</span></p>
<div style="margin-left: 1in" id='methodset-{{.Index}}'>...</div>
</div>
</div>

11
vendor/golang.org/x/tools/godoc/static/opensearch.xml generated vendored Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>godoc</ShortName>
<Description>The Go Programming Language</Description>
<Tags>go golang</Tags>
<Contact />
<Url type="text/html" template="{{.BaseURL}}/search?q={searchTerms}" />
<Image height="15" width="16" type="image/x-icon">/favicon.ico</Image>
<OutputEncoding>UTF-8</OutputEncoding>
<InputEncoding>UTF-8</InputEncoding>
</OpenSearchDescription>

282
vendor/golang.org/x/tools/godoc/static/package.html generated vendored Normal file
View File

@@ -0,0 +1,282 @@
<!--
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.
-->
<!--
Note: Static (i.e., not template-generated) href and id
attributes start with "pkg-" to make it impossible for
them to conflict with generated attributes (some of which
correspond to Go identifiers).
-->
{{with .PDoc}}
<script type='text/javascript'>
document.ANALYSIS_DATA = {{$.AnalysisData}};
document.CALLGRAPH = {{$.CallGraph}};
</script>
{{if $.IsMain}}
{{/* command documentation */}}
{{comment_html .Doc}}
{{else}}
{{/* package documentation */}}
<div id="short-nav">
<dl>
<dd><code>import "{{html .ImportPath}}"</code></dd>
</dl>
<dl>
<dd><a href="#pkg-overview" class="overviewLink">Overview</a></dd>
<dd><a href="#pkg-index" class="indexLink">Index</a></dd>
{{if $.Examples}}
<dd><a href="#pkg-examples" class="examplesLink">Examples</a></dd>
{{end}}
{{if $.Dirs}}
<dd><a href="#pkg-subdirectories">Subdirectories</a></dd>
{{end}}
</dl>
</div>
<!-- The package's Name is printed as title by the top-level template -->
<div id="pkg-overview" class="toggleVisible">
<div class="collapsed">
<h2 class="toggleButton" title="Click to show Overview section">Overview ▹</h2>
</div>
<div class="expanded">
<h2 class="toggleButton" title="Click to hide Overview section">Overview ▾</h2>
{{comment_html .Doc}}
{{example_html $ ""}}
</div>
</div>
<div id="pkg-index" class="toggleVisible">
<div class="collapsed">
<h2 class="toggleButton" title="Click to show Index section">Index ▹</h2>
</div>
<div class="expanded">
<h2 class="toggleButton" title="Click to hide Index section">Index ▾</h2>
<!-- Table of contents for API; must be named manual-nav to turn off auto nav. -->
<div id="manual-nav">
<dl>
{{if .Consts}}
<dd><a href="#pkg-constants">Constants</a></dd>
{{end}}
{{if .Vars}}
<dd><a href="#pkg-variables">Variables</a></dd>
{{end}}
{{range .Funcs}}
{{$name_html := html .Name}}
<dd><a href="#{{$name_html}}">{{node_html $ .Decl false | sanitize}}</a></dd>
{{end}}
{{range .Types}}
{{$tname_html := html .Name}}
<dd><a href="#{{$tname_html}}">type {{$tname_html}}</a></dd>
{{range .Funcs}}
{{$name_html := html .Name}}
<dd>&nbsp; &nbsp; <a href="#{{$name_html}}">{{node_html $ .Decl false | sanitize}}</a></dd>
{{end}}
{{range .Methods}}
{{$name_html := html .Name}}
<dd>&nbsp; &nbsp; <a href="#{{$tname_html}}.{{$name_html}}">{{node_html $ .Decl false | sanitize}}</a></dd>
{{end}}
{{end}}
{{if $.Notes}}
{{range $marker, $item := $.Notes}}
<dd><a href="#pkg-note-{{$marker}}">{{noteTitle $marker | html}}s</a></dd>
{{end}}
{{end}}
</dl>
</div><!-- #manual-nav -->
{{if $.Examples}}
<div id="pkg-examples">
<h3>Examples</h3>
<div class="js-expandAll expandAll collapsed">(Expand All)</div>
<dl>
{{range $.Examples}}
<dd><a class="exampleLink" href="#example_{{.Name}}">{{example_name .Name}}</a></dd>
{{end}}
</dl>
</div>
{{end}}
{{with .Filenames}}
<h3>Package files</h3>
<p>
<span style="font-size:90%">
{{range .}}
<a href="{{.|srcLink|html}}">{{.|filename|html}}</a>
{{end}}
</span>
</p>
{{end}}
</div><!-- .expanded -->
</div><!-- #pkg-index -->
<div id="pkg-callgraph" class="toggle" style="display: none">
<div class="collapsed">
<h2 class="toggleButton" title="Click to show Internal Call Graph section">Internal call graph ▹</h2>
</div> <!-- .expanded -->
<div class="expanded">
<h2 class="toggleButton" title="Click to hide Internal Call Graph section">Internal call graph ▾</h2>
<p>
In the call graph viewer below, each node
is a function belonging to this package
and its children are the functions it
calls&mdash;perhaps dynamically.
</p>
<p>
The root nodes are the entry points of the
package: functions that may be called from
outside the package.
There may be non-exported or anonymous
functions among them if they are called
dynamically from another package.
</p>
<p>
Click a node to visit that function's source code.
From there you can visit its callers by
clicking its declaring <code>func</code>
token.
</p>
<p>
Functions may be omitted if they were
determined to be unreachable in the
particular programs or tests that were
analyzed.
</p>
<!-- Zero means show all package entry points. -->
<ul style="margin-left: 0.5in" id="callgraph-0" class="treeview"></ul>
</div>
</div> <!-- #pkg-callgraph -->
{{with .Consts}}
<h2 id="pkg-constants">Constants</h2>
{{range .}}
{{comment_html .Doc}}
<pre>{{node_html $ .Decl true}}</pre>
{{end}}
{{end}}
{{with .Vars}}
<h2 id="pkg-variables">Variables</h2>
{{range .}}
{{comment_html .Doc}}
<pre>{{node_html $ .Decl true}}</pre>
{{end}}
{{end}}
{{range .Funcs}}
{{/* Name is a string - no need for FSet */}}
{{$name_html := html .Name}}
<h2 id="{{$name_html}}">func <a href="{{posLink_url $ .Decl}}">{{$name_html}}</a>
<a class="permalink" href="#{{$name_html}}">&#xb6;</a>
</h2>
<pre>{{node_html $ .Decl true}}</pre>
{{comment_html .Doc}}
{{example_html $ .Name}}
{{callgraph_html $ "" .Name}}
{{end}}
{{range .Types}}
{{$tname := .Name}}
{{$tname_html := html .Name}}
<h2 id="{{$tname_html}}">type <a href="{{posLink_url $ .Decl}}">{{$tname_html}}</a>
<a class="permalink" href="#{{$tname_html}}">&#xb6;</a>
</h2>
{{comment_html .Doc}}
<pre>{{node_html $ .Decl true}}</pre>
{{range .Consts}}
{{comment_html .Doc}}
<pre>{{node_html $ .Decl true}}</pre>
{{end}}
{{range .Vars}}
{{comment_html .Doc}}
<pre>{{node_html $ .Decl true}}</pre>
{{end}}
{{example_html $ $tname}}
{{implements_html $ $tname}}
{{methodset_html $ $tname}}
{{range .Funcs}}
{{$name_html := html .Name}}
<h3 id="{{$name_html}}">func <a href="{{posLink_url $ .Decl}}">{{$name_html}}</a>
<a class="permalink" href="#{{$name_html}}">&#xb6;</a>
</h3>
<pre>{{node_html $ .Decl true}}</pre>
{{comment_html .Doc}}
{{example_html $ .Name}}
{{callgraph_html $ "" .Name}}
{{end}}
{{range .Methods}}
{{$name_html := html .Name}}
<h3 id="{{$tname_html}}.{{$name_html}}">func ({{html .Recv}}) <a href="{{posLink_url $ .Decl}}">{{$name_html}}</a>
<a class="permalink" href="#{{$tname_html}}.{{$name_html}}">&#xb6;</a>
</h3>
<pre>{{node_html $ .Decl true}}</pre>
{{comment_html .Doc}}
{{$name := printf "%s_%s" $tname .Name}}
{{example_html $ $name}}
{{callgraph_html $ .Recv .Name}}
{{end}}
{{end}}
{{end}}
{{with $.Notes}}
{{range $marker, $content := .}}
<h2 id="pkg-note-{{$marker}}">{{noteTitle $marker | html}}s</h2>
<ul style="list-style: none; padding: 0;">
{{range .}}
<li><a href="{{posLink_url $ .}}" style="float: left;">&#x261e;</a> {{comment_html .Body}}</li>
{{end}}
</ul>
{{end}}
{{end}}
{{end}}
{{with .PAst}}
{{range $filename, $ast := .}}
<a href="{{$filename|srcLink|html}}">{{$filename|filename|html}}</a>:<pre>{{node_html $ $ast false}}</pre>
{{end}}
{{end}}
{{with .Dirs}}
{{/* DirList entries are numbers and strings - no need for FSet */}}
{{if $.PDoc}}
<h2 id="pkg-subdirectories">Subdirectories</h2>
{{end}}
<div class="pkg-dir">
<table>
<tr>
<th class="pkg-name">Name</th>
<th class="pkg-synopsis">Synopsis</th>
</tr>
{{if not ((eq $.Dirname "/src/cmd") $.DirFlat)}}
<tr>
<td colspan="2"><a href="..">..</a></td>
</tr>
{{end}}
{{range .List}}
<tr>
{{if $.DirFlat}}
{{if .HasPkg}}
<td class="pkg-name">
<a href="{{html .Path}}/{{modeQueryString $.Mode | html}}">{{html .Path}}</a>
</td>
{{end}}
{{else}}
<td class="pkg-name" style="padding-left: {{multiply .Depth 20}}px;">
<a href="{{html .Path}}/{{modeQueryString $.Mode | html}}">{{html .Name}}</a>
</td>
{{end}}
<td class="pkg-synopsis">
{{html .Synopsis}}
</td>
</tr>
{{end}}
</table>
</div>
{{end}}

116
vendor/golang.org/x/tools/godoc/static/package.txt generated vendored Normal file
View File

@@ -0,0 +1,116 @@
{{$info := .}}{{$filtered := .IsFiltered}}{{/*
---------------------------------------
*/}}{{if $filtered}}{{range .PAst}}{{range .Decls}}{{node $info .}}
{{end}}{{end}}{{else}}{{with .PAst}}{{range $filename, $ast := .}}{{$filename}}:
{{node $ $ast}}{{end}}{{end}}{{end}}{{/*
---------------------------------------
*/}}{{if and $filtered (not (or .PDoc .PAst))}}No match found.
{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND DOCUMENTATION
{{comment_text .Doc " " "\t"}}
{{else}}{{if not $filtered}}PACKAGE DOCUMENTATION
package {{.Name}}
import "{{.ImportPath}}"
{{comment_text .Doc " " "\t"}}
{{example_text $ "" " "}}{{end}}{{/*
---------------------------------------
*/}}{{with .Consts}}{{if not $filtered}}CONSTANTS
{{end}}{{range .}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}
{{end}}{{end}}{{/*
---------------------------------------
*/}}{{with .Vars}}{{if not $filtered}}VARIABLES
{{end}}{{range .}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}
{{end}}{{end}}{{/*
---------------------------------------
*/}}{{with .Funcs}}{{if not $filtered}}FUNCTIONS
{{end}}{{range .}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}
{{example_text $ .Name " "}}{{end}}{{end}}{{/*
---------------------------------------
*/}}{{with .Types}}{{if not $filtered}}TYPES
{{end}}{{range .}}{{$tname := .Name}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}
{{/*
---------------------------------------
*/}}{{if .Consts}}{{range .Consts}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}
{{end}}{{end}}{{/*
---------------------------------------
*/}}{{if .Vars}}{{range .Vars}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}
{{range $name := .Names}}{{example_text $ $name " "}}{{end}}{{end}}{{end}}{{/*
---------------------------------------
*/}}{{if .Funcs}}{{range .Funcs}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}
{{example_text $ .Name " "}}{{end}}{{end}}{{/*
---------------------------------------
*/}}{{if .Methods}}{{range .Methods}}{{node $ .Decl}}
{{comment_text .Doc " " "\t"}}
{{$name := printf "%s_%s" $tname .Name}}{{example_text $ $name " "}}{{end}}{{end}}{{/*
---------------------------------------
*/}}{{end}}{{end}}{{/*
---------------------------------------
*/}}{{if and $filtered (not (or .Consts (or .Vars (or .Funcs .Types))))}}No match found.
{{end}}{{/*
---------------------------------------
*/}}{{end}}{{/*
---------------------------------------
*/}}{{with $.Notes}}
{{range $marker, $content := .}}
{{$marker}}S
{{range $content}}{{comment_text .Body " " "\t"}}
{{end}}{{end}}{{end}}{{end}}{{/*
---------------------------------------
*/}}{{if not $filtered}}{{with .Dirs}}SUBDIRECTORIES
{{if $.DirFlat}}{{range .List}}{{if .HasPkg}}
{{.Path}}{{end}}{{end}}
{{else}}{{range .List}}
{{repeat `. ` .Depth}}{{.Name}}{{end}}
{{end}}{{end}}{{/*
---------------------------------------
*/}}{{end}}{{/*
Make sure there is no newline at the end of this file.
perl -i -pe 'chomp if eof' package.txt
*/}}

150
vendor/golang.org/x/tools/godoc/static/packageroot.html generated vendored Normal file
View File

@@ -0,0 +1,150 @@
<!--
Copyright 2018 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.
-->
<!--
Note: Static (i.e., not template-generated) href and id
attributes start with "pkg-" to make it impossible for
them to conflict with generated attributes (some of which
correspond to Go identifiers).
-->
{{with .PAst}}
{{range $filename, $ast := .}}
<a href="{{$filename|srcLink|html}}">{{$filename|filename|html}}</a>:<pre>{{node_html $ $ast false}}</pre>
{{end}}
{{end}}
{{with .Dirs}}
{{/* DirList entries are numbers and strings - no need for FSet */}}
{{if $.PDoc}}
<h2 id="pkg-subdirectories">Subdirectories</h2>
{{end}}
<div id="manual-nav">
<dl>
<dt><a href="#stdlib">Standard library</a></dt>
{{if hasThirdParty .List }}
<dt><a href="#thirdparty">Third party</a></dt>
{{end}}
<dt><a href="#other">Other packages</a></dt>
<dd><a href="#subrepo">Sub-repositories</a></dd>
<dd><a href="#community">Community</a></dd>
</dl>
</div>
<div id="stdlib" class="toggleVisible">
<div class="collapsed">
<h2 class="toggleButton" title="Click to show Standard library section">Standard library ▹</h2>
</div>
<div class="expanded">
<h2 class="toggleButton" title="Click to hide Standard library section">Standard library ▾</h2>
<img alt="" class="gopher" src="/doc/gopher/pkg.png"/>
<div class="pkg-dir">
<table>
<tr>
<th class="pkg-name">Name</th>
<th class="pkg-synopsis">Synopsis</th>
</tr>
{{range .List}}
<tr>
{{if eq .RootType "GOROOT"}}
{{if $.DirFlat}}
{{if .HasPkg}}
<td class="pkg-name">
<a href="{{html .Path}}/{{modeQueryString $.Mode | html}}">{{html .Path}}</a>
</td>
{{end}}
{{else}}
<td class="pkg-name" style="padding-left: {{multiply .Depth 20}}px;">
<a href="{{html .Path}}/{{modeQueryString $.Mode | html}}">{{html .Name}}</a>
</td>
{{end}}
<td class="pkg-synopsis">
{{html .Synopsis}}
</td>
{{end}}
</tr>
{{end}}
</table>
</div> <!-- .pkg-dir -->
</div> <!-- .expanded -->
</div> <!-- #stdlib .toggleVisible -->
{{if hasThirdParty .List }}
<div id="thirdparty" class="toggleVisible">
<div class="collapsed">
<h2 class="toggleButton" title="Click to show Third party section">Third party ▹</h2>
</div>
<div class="expanded">
<h2 class="toggleButton" title="Click to hide Third party section">Third party ▾</h2>
<div class="pkg-dir">
<table>
<tr>
<th class="pkg-name">Name</th>
<th class="pkg-synopsis">Synopsis</th>
</tr>
{{range .List}}
<tr>
{{if eq .RootType "GOPATH"}}
{{if $.DirFlat}}
{{if .HasPkg}}
<td class="pkg-name">
<a href="{{html .Path}}/{{modeQueryString $.Mode | html}}">{{html .Path}}</a>
</td>
{{end}}
{{else}}
<td class="pkg-name" style="padding-left: {{multiply .Depth 20}}px;">
<a href="{{html .Path}}/{{modeQueryString $.Mode | html}}">{{html .Name}}</a>
</td>
{{end}}
<td class="pkg-synopsis">
{{html .Synopsis}}
</td>
{{end}}
</tr>
{{end}}
</table>
</div> <!-- .pkg-dir -->
</div> <!-- .expanded -->
</div> <!-- #stdlib .toggleVisible -->
{{end}}
<h2 id="other">Other packages</h2>
<h3 id="subrepo">Sub-repositories</h3>
<p>
These packages are part of the Go Project but outside the main Go tree.
They are developed under looser <a href="/doc/go1compat">compatibility requirements</a> than the Go core.
Install them with "<a href="/cmd/go/#hdr-Download_and_install_packages_and_dependencies">go get</a>".
</p>
<ul>
<li><a href="//godoc.org/golang.org/x/benchmarks">benchmarks</a> — benchmarks to measure Go as it is developed.</li>
<li><a href="//godoc.org/golang.org/x/blog">blog</a><a href="//blog.golang.org">blog.golang.org</a>'s implementation.</li>
<li><a href="//godoc.org/golang.org/x/build">build</a><a href="//build.golang.org">build.golang.org</a>'s implementation.</li>
<li><a href="//godoc.org/golang.org/x/crypto">crypto</a> — additional cryptography packages.</li>
<li><a href="//godoc.org/golang.org/x/debug">debug</a> — an experimental debugger for Go.</li>
<li><a href="//godoc.org/golang.org/x/image">image</a> — additional imaging packages.</li>
<li><a href="//godoc.org/golang.org/x/mobile">mobile</a> — experimental support for Go on mobile platforms.</li>
<li><a href="//godoc.org/golang.org/x/net">net</a> — additional networking packages.</li>
<li><a href="//godoc.org/golang.org/x/perf">perf</a> — packages and tools for performance measurement, storage, and analysis.</li>
<li><a href="//godoc.org/golang.org/x/review">review</a> — a tool for working with Gerrit code reviews.</li>
<li><a href="//godoc.org/golang.org/x/sync">sync</a> — additional concurrency primitives.</li>
<li><a href="//godoc.org/golang.org/x/sys">sys</a> — packages for making system calls.</li>
<li><a href="//godoc.org/golang.org/x/text">text</a> — packages for working with text.</li>
<li><a href="//godoc.org/golang.org/x/time">time</a> — additional time packages.</li>
<li><a href="//godoc.org/golang.org/x/tools">tools</a> — godoc, goimports, gorename, and other tools.</li>
<li><a href="//godoc.org/golang.org/x/tour">tour</a><a href="//tour.golang.org">tour.golang.org</a>'s implementation.</li>
<li><a href="//godoc.org/golang.org/x/exp">exp</a> — experimental and deprecated packages (handle with care; may change without warning).</li>
</ul>
<h3 id="community">Community</h3>
<p>
These services can help you find Open Source packages provided by the community.
</p>
<ul>
<li><a href="//godoc.org">GoDoc</a> - a package index and search engine.</li>
<li><a href="http://go-search.org">Go Search</a> - a code search engine.</li>
<li><a href="/wiki/Projects">Projects at the Go Wiki</a> - a curated list of Go projects.</li>
</ul>
{{end}}

114
vendor/golang.org/x/tools/godoc/static/play.js generated vendored Normal file
View File

@@ -0,0 +1,114 @@
// Copyright 2012 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.
function initPlayground(transport) {
'use strict';
function text(node) {
var s = '';
for (var i = 0; i < node.childNodes.length; i++) {
var n = node.childNodes[i];
if (n.nodeType === 1) {
if (n.tagName === 'BUTTON') continue
if (n.tagName === 'SPAN' && n.className === 'number') continue;
if (n.tagName === 'DIV' || n.tagName == 'BR') {
s += "\n";
}
s += text(n);
continue;
}
if (n.nodeType === 3) {
s += n.nodeValue;
}
}
return s.replace('\xA0', ' '); // replace non-breaking spaces
}
// When presenter notes are enabled, the index passed
// here will identify the playground to be synced
function init(code, index) {
var output = document.createElement('div');
var outpre = document.createElement('pre');
var running;
if ($ && $(output).resizable) {
$(output).resizable({
handles: 'n,w,nw',
minHeight: 27,
minWidth: 135,
maxHeight: 608,
maxWidth: 990
});
}
function onKill() {
if (running) running.Kill();
if (window.notesEnabled) updatePlayStorage('onKill', index);
}
function onRun(e) {
var sk = e.shiftKey || localStorage.getItem('play-shiftKey') === 'true';
if (running) running.Kill();
output.style.display = 'block';
outpre.innerHTML = '';
run1.style.display = 'none';
var options = {Race: sk};
running = transport.Run(text(code), PlaygroundOutput(outpre), options);
if (window.notesEnabled) updatePlayStorage('onRun', index, e);
}
function onClose() {
if (running) running.Kill();
output.style.display = 'none';
run1.style.display = 'inline-block';
if (window.notesEnabled) updatePlayStorage('onClose', index);
}
if (window.notesEnabled) {
playgroundHandlers.onRun.push(onRun);
playgroundHandlers.onClose.push(onClose);
playgroundHandlers.onKill.push(onKill);
}
var run1 = document.createElement('button');
run1.innerHTML = 'Run';
run1.className = 'run';
run1.addEventListener("click", onRun, false);
var run2 = document.createElement('button');
run2.className = 'run';
run2.innerHTML = 'Run';
run2.addEventListener("click", onRun, false);
var kill = document.createElement('button');
kill.className = 'kill';
kill.innerHTML = 'Kill';
kill.addEventListener("click", onKill, false);
var close = document.createElement('button');
close.className = 'close';
close.innerHTML = 'Close';
close.addEventListener("click", onClose, false);
var button = document.createElement('div');
button.classList.add('buttons');
button.appendChild(run1);
// Hack to simulate insertAfter
code.parentNode.insertBefore(button, code.nextSibling);
var buttons = document.createElement('div');
buttons.classList.add('buttons');
buttons.appendChild(run2);
buttons.appendChild(kill);
buttons.appendChild(close);
output.classList.add('output');
output.appendChild(buttons);
output.appendChild(outpre);
output.style.display = 'none';
code.parentNode.insertBefore(output, button.nextSibling);
}
var play = document.querySelectorAll('div.playground');
for (var i = 0; i < play.length; i++) {
init(play[i], i);
}
}

499
vendor/golang.org/x/tools/godoc/static/playground.js generated vendored Normal file
View File

@@ -0,0 +1,499 @@
// Copyright 2012 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.
/*
In the absence of any formal way to specify interfaces in JavaScript,
here's a skeleton implementation of a playground transport.
function Transport() {
// Set up any transport state (eg, make a websocket connection).
return {
Run: function(body, output, options) {
// Compile and run the program 'body' with 'options'.
// Call the 'output' callback to display program output.
return {
Kill: function() {
// Kill the running program.
}
};
}
};
}
// The output callback is called multiple times, and each time it is
// passed an object of this form.
var write = {
Kind: 'string', // 'start', 'stdout', 'stderr', 'end'
Body: 'string' // content of write or end status message
}
// The first call must be of Kind 'start' with no body.
// Subsequent calls may be of Kind 'stdout' or 'stderr'
// and must have a non-null Body string.
// The final call should be of Kind 'end' with an optional
// Body string, signifying a failure ("killed", for example).
// The output callback must be of this form.
// See PlaygroundOutput (below) for an implementation.
function outputCallback(write) {
}
*/
// HTTPTransport is the default transport.
// enableVet enables running vet if a program was compiled and ran successfully.
// If vet returned any errors, display them before the output of a program.
function HTTPTransport(enableVet) {
'use strict';
function playback(output, events) {
var timeout;
output({Kind: 'start'});
function next() {
if (!events || events.length === 0) {
output({Kind: 'end'});
return;
}
var e = events.shift();
if (e.Delay === 0) {
output({Kind: e.Kind, Body: e.Message});
next();
return;
}
timeout = setTimeout(function() {
output({Kind: e.Kind, Body: e.Message});
next();
}, e.Delay / 1000000);
}
next();
return {
Stop: function() {
clearTimeout(timeout);
}
};
}
function error(output, msg) {
output({Kind: 'start'});
output({Kind: 'stderr', Body: msg});
output({Kind: 'end'});
}
var seq = 0;
return {
Run: function(body, output, options) {
seq++;
var cur = seq;
var playing;
$.ajax('/compile', {
type: 'POST',
data: {'version': 2, 'body': body},
dataType: 'json',
success: function(data) {
if (seq != cur) return;
if (!data) return;
if (playing != null) playing.Stop();
if (data.Errors) {
error(output, data.Errors);
return;
}
if (!enableVet) {
playing = playback(output, data.Events);
return;
}
$.ajax("/vet", {
data: {"body": body},
type: "POST",
dataType: "json",
success: function(dataVet) {
if (dataVet.Errors) {
if (!data.Events) {
data.Events = [];
}
// inject errors from the vet as the first events in the output
data.Events.unshift({Message: 'Go vet exited.\n\n', Kind: 'system', Delay: 0});
data.Events.unshift({Message: dataVet.Errors, Kind: 'stderr', Delay: 0});
}
playing = playback(output, data.Events);
},
error: function() {
playing = playback(output, data.Events);
}
});
},
error: function() {
error(output, 'Error communicating with remote server.');
}
});
return {
Kill: function() {
if (playing != null) playing.Stop();
output({Kind: 'end', Body: 'killed'});
}
};
}
};
}
function SocketTransport() {
'use strict';
var id = 0;
var outputs = {};
var started = {};
var websocket;
if (window.location.protocol == "http:") {
websocket = new WebSocket('ws://' + window.location.host + '/socket');
} else if (window.location.protocol == "https:") {
websocket = new WebSocket('wss://' + window.location.host + '/socket');
}
websocket.onclose = function() {
console.log('websocket connection closed');
};
websocket.onmessage = function(e) {
var m = JSON.parse(e.data);
var output = outputs[m.Id];
if (output === null)
return;
if (!started[m.Id]) {
output({Kind: 'start'});
started[m.Id] = true;
}
output({Kind: m.Kind, Body: m.Body});
};
function send(m) {
websocket.send(JSON.stringify(m));
}
return {
Run: function(body, output, options) {
var thisID = id+'';
id++;
outputs[thisID] = output;
send({Id: thisID, Kind: 'run', Body: body, Options: options});
return {
Kill: function() {
send({Id: thisID, Kind: 'kill'});
}
};
}
};
}
function PlaygroundOutput(el) {
'use strict';
return function(write) {
if (write.Kind == 'start') {
el.innerHTML = '';
return;
}
var cl = 'system';
if (write.Kind == 'stdout' || write.Kind == 'stderr')
cl = write.Kind;
var m = write.Body;
if (write.Kind == 'end') {
m = '\nProgram exited' + (m?(': '+m):'.');
}
if (m.indexOf('IMAGE:') === 0) {
// TODO(adg): buffer all writes before creating image
var url = 'data:image/png;base64,' + m.substr(6);
var img = document.createElement('img');
img.src = url;
el.appendChild(img);
return;
}
// ^L clears the screen.
var s = m.split('\x0c');
if (s.length > 1) {
el.innerHTML = '';
m = s.pop();
}
m = m.replace(/&/g, '&amp;');
m = m.replace(/</g, '&lt;');
m = m.replace(/>/g, '&gt;');
var needScroll = (el.scrollTop + el.offsetHeight) == el.scrollHeight;
var span = document.createElement('span');
span.className = cl;
span.innerHTML = m;
el.appendChild(span);
if (needScroll)
el.scrollTop = el.scrollHeight - el.offsetHeight;
};
}
(function() {
function lineHighlight(error) {
var regex = /prog.go:([0-9]+)/g;
var r = regex.exec(error);
while (r) {
$(".lines div").eq(r[1]-1).addClass("lineerror");
r = regex.exec(error);
}
}
function highlightOutput(wrappedOutput) {
return function(write) {
if (write.Body) lineHighlight(write.Body);
wrappedOutput(write);
};
}
function lineClear() {
$(".lineerror").removeClass("lineerror");
}
// opts is an object with these keys
// codeEl - code editor element
// outputEl - program output element
// runEl - run button element
// fmtEl - fmt button element (optional)
// fmtImportEl - fmt "imports" checkbox element (optional)
// shareEl - share button element (optional)
// shareURLEl - share URL text input element (optional)
// shareRedirect - base URL to redirect to on share (optional)
// toysEl - toys select element (optional)
// enableHistory - enable using HTML5 history API (optional)
// transport - playground transport to use (default is HTTPTransport)
// enableShortcuts - whether to enable shortcuts (Ctrl+S/Cmd+S to save) (default is false)
// enableVet - enable running vet and displaying its errors
function playground(opts) {
var code = $(opts.codeEl);
var transport = opts['transport'] || new HTTPTransport(opts['enableVet']);
var running;
// autoindent helpers.
function insertTabs(n) {
// find the selection start and end
var start = code[0].selectionStart;
var end = code[0].selectionEnd;
// split the textarea content into two, and insert n tabs
var v = code[0].value;
var u = v.substr(0, start);
for (var i=0; i<n; i++) {
u += "\t";
}
u += v.substr(end);
// set revised content
code[0].value = u;
// reset caret position after inserted tabs
code[0].selectionStart = start+n;
code[0].selectionEnd = start+n;
}
function autoindent(el) {
var curpos = el.selectionStart;
var tabs = 0;
while (curpos > 0) {
curpos--;
if (el.value[curpos] == "\t") {
tabs++;
} else if (tabs > 0 || el.value[curpos] == "\n") {
break;
}
}
setTimeout(function() {
insertTabs(tabs);
}, 1);
}
// NOTE(cbro): e is a jQuery event, not a DOM event.
function handleSaveShortcut(e) {
if (e.isDefaultPrevented()) return false;
if (!e.metaKey && !e.ctrlKey) return false;
if (e.key != "S" && e.key != "s") return false;
e.preventDefault();
// Share and save
share(function(url) {
window.location.href = url + ".go?download=true";
});
return true;
}
function keyHandler(e) {
if (opts.enableShortcuts && handleSaveShortcut(e)) return;
if (e.keyCode == 9 && !e.ctrlKey) { // tab (but not ctrl-tab)
insertTabs(1);
e.preventDefault();
return false;
}
if (e.keyCode == 13) { // enter
if (e.shiftKey) { // +shift
run();
e.preventDefault();
return false;
} if (e.ctrlKey) { // +control
fmt();
e.preventDefault();
} else {
autoindent(e.target);
}
}
return true;
}
code.unbind('keydown').bind('keydown', keyHandler);
var outdiv = $(opts.outputEl).empty();
var output = $('<pre/>').appendTo(outdiv);
function body() {
return $(opts.codeEl).val();
}
function setBody(text) {
$(opts.codeEl).val(text);
}
function origin(href) {
return (""+href).split("/").slice(0, 3).join("/");
}
var pushedEmpty = (window.location.pathname == "/");
function inputChanged() {
if (pushedEmpty) {
return;
}
pushedEmpty = true;
$(opts.shareURLEl).hide();
window.history.pushState(null, "", "/");
}
function popState(e) {
if (e === null) {
return;
}
if (e && e.state && e.state.code) {
setBody(e.state.code);
}
}
var rewriteHistory = false;
if (window.history && window.history.pushState && window.addEventListener && opts.enableHistory) {
rewriteHistory = true;
code[0].addEventListener('input', inputChanged);
window.addEventListener('popstate', popState);
}
function setError(error) {
if (running) running.Kill();
lineClear();
lineHighlight(error);
output.empty().addClass("error").text(error);
}
function loading() {
lineClear();
if (running) running.Kill();
output.removeClass("error").text('Waiting for remote server...');
}
function run() {
loading();
running = transport.Run(body(), highlightOutput(PlaygroundOutput(output[0])));
}
function fmt() {
loading();
var data = {"body": body()};
if ($(opts.fmtImportEl).is(":checked")) {
data["imports"] = "true";
}
$.ajax("/fmt", {
data: data,
type: "POST",
dataType: "json",
success: function(data) {
if (data.Error) {
setError(data.Error);
} else {
setBody(data.Body);
setError("");
}
}
});
}
var shareURL; // jQuery element to show the shared URL.
var sharing = false; // true if there is a pending request.
var shareCallbacks = [];
function share(opt_callback) {
if (opt_callback) shareCallbacks.push(opt_callback);
if (sharing) return;
sharing = true;
var sharingData = body();
$.ajax("/share", {
processData: false,
data: sharingData,
type: "POST",
contentType: "text/plain; charset=utf-8",
complete: function(xhr) {
sharing = false;
if (xhr.status != 200) {
alert("Server error; try again.");
return;
}
if (opts.shareRedirect) {
window.location = opts.shareRedirect + xhr.responseText;
}
var path = "/p/" + xhr.responseText;
var url = origin(window.location) + path;
for (var i = 0; i < shareCallbacks.length; i++) {
shareCallbacks[i](url);
}
shareCallbacks = [];
if (shareURL) {
shareURL.show().val(url).focus().select();
if (rewriteHistory) {
var historyData = {"code": sharingData};
window.history.pushState(historyData, "", path);
pushedEmpty = false;
}
}
}
});
}
$(opts.runEl).click(run);
$(opts.fmtEl).click(fmt);
if (opts.shareEl !== null && (opts.shareURLEl !== null || opts.shareRedirect !== null)) {
if (opts.shareURLEl) {
shareURL = $(opts.shareURLEl).hide();
}
$(opts.shareEl).click(function() {
share();
});
}
if (opts.toysEl !== null) {
$(opts.toysEl).bind('change', function() {
var toy = $(this).val();
$.ajax("/doc/play/"+toy, {
processData: false,
type: "GET",
complete: function(xhr) {
if (xhr.status != 200) {
alert("Server error; try again.");
return;
}
setBody(xhr.responseText);
}
});
});
}
}
window.playground = playground;
})();

18
vendor/golang.org/x/tools/godoc/static/search.html generated vendored Normal file
View File

@@ -0,0 +1,18 @@
<!--
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.
-->
{{with .Alert}}
<p>
<span class="alert" style="font-size:120%">{{html .}}</span>
</p>
{{end}}
{{with .Alt}}
<p>
<span class="alert" style="font-size:120%">Did you mean: </span>
{{range .Alts}}
<a href="search?q={{urlquery .}}" style="font-size:120%">{{html .}}</a>
{{end}}
</p>
{{end}}

54
vendor/golang.org/x/tools/godoc/static/search.txt generated vendored Normal file
View File

@@ -0,0 +1,54 @@
QUERY
{{.Query}}
{{with .Alert}}{{.}}
{{end}}{{/* .Alert */}}{{/*
---------------------------------------
*/}}{{with .Alt}}DID YOU MEAN
{{range .Alts}} {{.}}
{{end}}
{{end}}{{/* .Alt */}}{{/*
---------------------------------------
*/}}{{with .Pak}}PACKAGE {{$.Query}}
{{range .}} {{pkgLink .Pak.Path}}
{{end}}
{{end}}{{/* .Pak */}}{{/*
---------------------------------------
*/}}{{range $key, $val := .Idents}}{{if $val}}{{$key.Name}}
{{range $val}} {{.Path}}.{{.Name}}
{{end}}
{{end}}{{end}}{{/* .Idents */}}{{/*
---------------------------------------
*/}}{{with .Hit}}{{with .Decls}}PACKAGE-LEVEL DECLARATIONS
{{range .}}package {{.Pak.Name}}
{{range $file := .Files}}{{range .Groups}}{{range .}} {{srcLink $file.File.Path}}:{{infoLine .}}{{end}}
{{end}}{{end}}{{/* .Files */}}
{{end}}{{end}}{{/* .Decls */}}{{/*
---------------------------------------
*/}}{{with .Others}}LOCAL DECLARATIONS AND USES
{{range .}}package {{.Pak.Name}}
{{range $file := .Files}}{{range .Groups}}{{range .}} {{srcLink $file.File.Path}}:{{infoLine .}}
{{end}}{{end}}{{end}}{{/* .Files */}}
{{end}}{{end}}{{/* .Others */}}{{end}}{{/* .Hit */}}{{/*
---------------------------------------
*/}}{{if .Textual}}{{if .Complete}}{{.Found}} TEXTUAL OCCURRENCES{{else}}MORE THAN {{.Found}} TEXTUAL OCCURRENCES{{end}}
{{range .Textual}}{{len .Lines}} {{srcLink .Filename}}
{{end}}{{if not .Complete}}... ...
{{end}}{{end}}

64
vendor/golang.org/x/tools/godoc/static/searchcode.html generated vendored Normal file
View File

@@ -0,0 +1,64 @@
<!--
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.
-->
{{$query_url := urlquery .Query}}
{{if not .Idents}}
{{with .Pak}}
<h2 id="Packages">Package {{html $.Query}}</h2>
<p>
<table class="layout">
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<tr><td><a href="/{{$pkg_html}}">{{$pkg_html}}</a></td></tr>
{{end}}
</table>
</p>
{{end}}
{{end}}
{{with .Hit}}
{{with .Decls}}
<h2 id="Global">Package-level declarations</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Global_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
{{range .Groups}}
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$file}}:{{$line}}</a>
{{infoSnippet_html .}}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}
{{with .Others}}
<h2 id="Local">Local declarations and uses</h2>
{{range .}}
{{$pkg_html := pkgLink .Pak.Path | html}}
<h3 id="Local_{{$pkg_html}}">package <a href="/{{$pkg_html}}">{{html .Pak.Name}}</a></h3>
{{range .Files}}
{{$file := .File.Path}}
<a href="{{queryLink $file $query_url 0 | html}}">{{$file}}</a>
<table class="layout">
{{range .Groups}}
<tr>
<td width="25"></td>
<th align="left" valign="top">{{index . 0 | infoKind_html}}</th>
<td align="left" width="4"></td>
<td>
{{range .}}
{{$line := infoLine .}}
<a href="{{queryLink $file $query_url $line | html}}">{{$line}}</a>
{{end}}
</td>
</tr>
{{end}}
</table>
{{end}}
{{end}}
{{end}}
{{end}}

24
vendor/golang.org/x/tools/godoc/static/searchdoc.html generated vendored Normal file
View File

@@ -0,0 +1,24 @@
<!--
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.
-->
{{range $key, $val := .Idents}}
{{if $val}}
<h2 id="{{$key.Name}}">{{$key.Name}}</h2>
{{range $val}}
{{$pkg_html := pkgLink .Path | html}}
{{if eq "Packages" $key.Name}}
<a href="/{{$pkg_html}}">{{html .Path}}</a>
{{else}}
{{$doc_html := docLink .Path .Name| html}}
<a href="/{{$pkg_html}}">{{html .Package}}</a>.<a href="{{$doc_html}}">{{.Name}}</a>
{{end}}
{{if .Doc}}
<p>{{comment_html .Doc}}</p>
{{else}}
<p><em>No documentation available</em></p>
{{end}}
{{end}}
{{end}}
{{end}}

42
vendor/golang.org/x/tools/godoc/static/searchtxt.html generated vendored Normal file
View File

@@ -0,0 +1,42 @@
<!--
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.
-->
{{$query_url := urlquery .Query}}
{{with .Textual}}
{{if $.Complete}}
<h2 id="Textual">{{html $.Found}} textual occurrences</h2>
{{else}}
<h2 id="Textual">More than {{html $.Found}} textual occurrences</h2>
<p>
<span class="alert" style="font-size:120%">Not all files or lines containing "{{html $.Query}}" are shown.</span>
</p>
{{end}}
<p>
<table class="layout">
{{range .}}
{{$file := .Filename}}
<tr>
<td align="left" valign="top">
<a href="{{queryLink $file $query_url 0}}">{{$file}}</a>:
</td>
<td align="left" width="4"></td>
<th align="left" valign="top">{{len .Lines}}</th>
<td align="left" width="4"></td>
<td align="left">
{{range .Lines}}
<a href="{{queryLink $file $query_url .}}">{{html .}}</a>
{{end}}
{{if not $.Complete}}
...
{{end}}
</td>
</tr>
{{end}}
{{if not $.Complete}}
<tr><td align="left">...</td></tr>
{{end}}
</table>
</p>
{{end}}

109
vendor/golang.org/x/tools/godoc/static/static.go generated vendored Normal file

File diff suppressed because one or more lines are too long

827
vendor/golang.org/x/tools/godoc/static/style.css generated vendored Normal file
View File

@@ -0,0 +1,827 @@
body {
margin: 0;
font-family: Arial, sans-serif;
background-color: #fff;
line-height: 1.3;
text-align: center;
color: #222;
}
pre,
code {
font-family: Menlo, monospace;
font-size: 0.875rem;
}
pre {
line-height: 1.4;
overflow-x: auto;
}
pre .comment {
color: #006600;
}
pre .highlight,
pre .highlight-comment,
pre .selection-highlight,
pre .selection-highlight-comment {
background: #FFFF00;
}
pre .selection,
pre .selection-comment {
background: #FF9632;
}
pre .ln {
color: #999;
background: #efefef;
}
.ln {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
a,
.exampleHeading .text,
.expandAll {
color: #375EAB;
text-decoration: none;
}
a:hover,
.exampleHeading .text:hover,
.expandAll:hover {
text-decoration: underline;
}
.article a {
text-decoration: underline;
}
.article .title a {
text-decoration: none;
}
.permalink {
display: none;
}
:hover > .permalink {
display: inline;
}
p, li {
max-width: 50rem;
word-wrap: break-word;
}
p,
pre,
ul,
ol {
margin: 1.25rem;
}
pre {
background: #EFEFEF;
padding: 0.625rem;
border-radius: 0.3125rem;
}
h1,
h2,
h3,
h4,
.rootHeading {
margin: 1.25rem 0 1.25rem;
padding: 0;
color: #375EAB;
font-weight: bold;
}
h1 {
font-size: 1.75rem;
line-height: 1;
}
h1 .text-muted {
color:#777;
}
h2 {
font-size: 1.25rem;
background: #E0EBF5;
padding: 0.5rem;
line-height: 1.25;
font-weight: normal;
}
h2 a {
font-weight: bold;
}
h3 {
font-size: 1.25rem;
}
h3,
h4 {
margin: 1.25rem 0.3125rem;
}
h4 {
font-size: 1rem;
}
.rootHeading {
font-size: 1.25rem;
margin: 0;
}
dl {
margin: 1.25rem;
}
dd {
margin: 0 0 0 1.25rem;
}
dl,
dd {
font-size: 0.875rem;
}
div#nav table td {
vertical-align: top;
}
#pkg-index h3 {
font-size: 1rem;
}
.pkg-dir {
padding: 0 0.625rem;
}
.pkg-dir table {
border-collapse: collapse;
border-spacing: 0;
}
.pkg-name {
padding-right: 0.625rem;
}
.alert {
color: #AA0000;
}
.top-heading {
float: left;
padding: 1.313rem 0;
font-size: 1.25rem;
font-weight: normal;
}
.top-heading a {
color: #222;
text-decoration: none;
}
#pkg-examples h3 {
float: left;
}
#pkg-examples dl {
clear: both;
}
.expandAll {
cursor: pointer;
float: left;
margin: 1.25rem 0;
}
div#topbar {
background: #E0EBF5;
height: 4rem;
overflow: hidden;
}
div#page {
width: 100%;
}
div#page > .container,
div#topbar > .container {
text-align: left;
margin-left: auto;
margin-right: auto;
padding: 0 1.25rem;
}
div#topbar > .container,
div#page > .container {
max-width: 59.38rem;
}
div#page.wide > .container,
div#topbar.wide > .container {
max-width: none;
}
div#plusone {
float: right;
clear: right;
margin-top: 0.3125rem;
}
div#footer {
text-align: center;
color: #666;
font-size: 0.875rem;
margin: 2.5rem 0;
}
div#menu > a,
input#search,
div#learn .buttons a,
div.play .buttons a,
div#blog .read a,
#menu-button {
padding: 0.625rem;
text-decoration: none;
font-size: 1rem;
border-radius: 0.3125rem;
}
div#playground .buttons a,
div#menu > a,
input#search,
#menu-button {
border: 0.0625rem solid #375EAB;
}
div#playground .buttons a,
div#menu > a,
#menu-button {
color: white;
background: #375EAB;
}
#playgroundButton.active {
background: white;
color: #375EAB;
}
a#start,
div#learn .buttons a,
div.play .buttons a,
div#blog .read a {
color: #222;
border: 0.0625rem solid #375EAB;
background: #E0EBF5;
}
.download {
width: 9.375rem;
}
div#menu {
text-align: right;
padding: 0.625rem;
white-space: nowrap;
max-height: 0;
-moz-transition: max-height .25s linear;
transition: max-height .25s linear;
width: 100%;
}
div#menu.menu-visible {
max-height: 31.25rem;
}
div#menu > a,
#menu-button {
margin: 0.625rem 0.125rem;
padding: 0.625rem;
}
::-webkit-input-placeholder {
color: #7f7f7f;
opacity: 1;
}
::placeholder {
color: #7f7f7f;
opacity: 1;
}
#menu .search-box {
display: inline-flex;
width: 8.75rem;
}
input#search {
background: white;
color: #222;
box-sizing: border-box;
-webkit-appearance: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
margin-right: 0;
flex-grow: 1;
max-width: 100%;
min-width: 5.625rem;
}
input#search:-moz-ui-invalid {
box-shadow: unset;
}
input#search + button {
display: inline;
font-size: 1em;
background-color: #375EAB;
color: white;
border: 0.0625rem solid #375EAB;
border-top-left-radius: 0;
border-top-right-radius: 0.3125rem;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0.3125rem;
margin-left: 0;
cursor: pointer;
}
input#search + button span {
display: flex;
}
input#search + button svg {
fill: white
}
#menu-button {
display: none;
position: absolute;
right: 0.3125rem;
top: 0;
margin-right: 0.3125rem;
}
#menu-button-arrow {
display: inline-block;
}
.vertical-flip {
transform: rotate(-180deg);
}
div.left {
float: left;
clear: left;
margin-right: 2.5%;
}
div.right {
float: right;
clear: right;
margin-left: 2.5%;
}
div.left,
div.right {
width: 45%;
}
div#learn,
div#about {
padding-top: 1.25rem;
}
div#learn h2,
div#about {
margin: 0;
}
div#about {
font-size: 1.25rem;
margin: 0 auto 1.875rem;
}
div#gopher {
background: url(/doc/gopher/frontpage.png) no-repeat;
background-position: center top;
height: 9.688rem;
max-height: 200px; /* Setting in px to prevent the gopher from blowing up in very high default font-sizes */
}
a#start {
display: block;
padding: 0.625rem;
text-align: center;
text-decoration: none;
border-radius: 0.3125rem;
}
a#start .big {
display: block;
font-weight: bold;
font-size: 1.25rem;
}
a#start .desc {
display: block;
font-size: 0.875rem;
font-weight: normal;
margin-top: 0.3125rem;
}
div#learn .popout {
float: right;
display: block;
cursor: pointer;
font-size: 0.75rem;
background: url(/doc/share.png) no-repeat;
background-position: right center;
padding: 0.375rem 1.688rem;
}
div#learn pre,
div#learn textarea {
padding: 0;
margin: 0;
font-family: Menlo, monospace;
font-size: 0.875rem;
}
div#learn .input {
padding: 0.625rem;
margin-top: 0.625rem;
height: 9.375rem;
border-top-left-radius: 0.3125rem;
border-top-right-radius: 0.3125rem;
}
div#learn .input textarea {
width: 100%;
height: 100%;
border: none;
outline: none;
resize: none;
}
div#learn .output {
border-top: none !important;
padding: 0.625rem;
height: 3.688rem;
overflow: auto;
border-bottom-right-radius: 0.3125rem;
border-bottom-left-radius: 0.3125rem;
}
div#learn .output pre {
padding: 0;
border-radius: 0;
}
div#learn .input,
div#learn .input textarea,
div#learn .output,
div#learn .output pre {
background: #FFFFD8;
}
div#learn .input,
div#learn .output {
border: 0.0625rem solid #375EAB;
}
div#learn .buttons {
float: right;
padding: 1.25rem 0 0.625rem 0;
text-align: right;
}
div#learn .buttons a {
height: 1rem;
margin-left: 0.3125rem;
padding: 0.625rem;
}
div#learn .toys {
margin-top: 0.5rem;
}
div#learn .toys select {
font-size: 0.875rem;
border: 0.0625rem solid #375EAB;
margin: 0;
}
div#learn .output .exit {
display: none;
}
div#video {
max-width: 100%;
}
div#blog,
div#video {
margin-top: 2.5rem;
}
div#blog > a,
div#blog > div,
div#blog > h2,
div#video > a,
div#video > div,
div#video > h2 {
margin-bottom: 0.625rem;
}
div#blog .title,
div#video .title {
display: block;
font-size: 1.25rem;
}
div#blog .when {
color: #666;
font-size: 0.875rem;
}
div#blog .read {
text-align: right;
}
@supports (--c: 0) {
[style*="--aspect-ratio-padding:"] {
position: relative;
overflow: hidden;
padding-top: var(--aspect-ratio-padding);
}
[style*="--aspect-ratio-padding:"]>* {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
.toggleButton { cursor: pointer; }
.toggle > .collapsed { display: block; }
.toggle > .expanded { display: none; }
.toggleVisible > .collapsed { display: none; }
.toggleVisible > .expanded { display: block; }
table.codetable { margin-left: auto; margin-right: auto; border-style: none; }
table.codetable td { padding-right: 0.625rem; }
hr { border-style: none; border-top: 0.0625rem solid black; }
img.gopher {
float: right;
margin-left: 0.625rem;
margin-bottom: 0.625rem;
z-index: -1;
}
h2 { clear: right; }
/* example and drop-down playground */
div.play {
padding: 0 1.25rem 2.5rem 1.25rem;
}
div.play pre,
div.play textarea,
div.play .lines {
padding: 0;
margin: 0;
font-family: Menlo, monospace;
font-size: 0.875rem;
}
div.play .input {
padding: 0.625rem;
margin-top: 0.625rem;
border-top-left-radius: 0.3125rem;
border-top-right-radius: 0.3125rem;
overflow: hidden;
}
div.play .input textarea {
width: 100%;
height: 100%;
border: none;
outline: none;
resize: none;
overflow: hidden;
}
div#playground .input textarea {
overflow: auto;
resize: auto;
}
div.play .output {
border-top: none !important;
padding: 0.625rem;
max-height: 12.5rem;
overflow: auto;
border-bottom-right-radius: 0.3125rem;
border-bottom-left-radius: 0.3125rem;
}
div.play .output pre {
padding: 0;
border-radius: 0;
}
div.play .input,
div.play .input textarea,
div.play .output,
div.play .output pre {
background: #FFFFD8;
}
div.play .input,
div.play .output {
border: 0.0625rem solid #375EAB;
}
div.play .buttons {
float: right;
padding: 1.25rem 0 0.625rem 0;
text-align: right;
}
div.play .buttons a {
height: 1rem;
margin-left: 0.3125rem;
padding: 0.625rem;
cursor: pointer;
}
.output .stderr {
color: #933;
}
.output .system {
color: #999;
}
/* drop-down playground */
#playgroundButton,
div#playground {
/* start hidden; revealed by javascript */
display: none;
}
div#playground {
position: absolute;
top: 3.938rem;
right: 1.25rem;
padding: 0 0.625rem 0.625rem 0.625rem;
z-index: 1;
text-align: left;
background: #E0EBF5;
border: 0.0625rem solid #B0BBC5;
border-top: none;
border-bottom-left-radius: 0.3125rem;
border-bottom-right-radius: 0.3125rem;
}
div#playground .code {
width: 32.5rem;
height: 12.5rem;
}
div#playground .output {
height: 6.25rem;
}
/* Inline runnable snippets (play.js/initPlayground) */
#content .code pre, #content .playground pre, #content .output pre {
margin: 0;
padding: 0;
background: none;
border: none;
outline: 0 solid transparent;
overflow: auto;
}
#content .playground .number, #content .code .number {
color: #999;
}
#content .code, #content .playground, #content .output {
width: auto;
margin: 1.25rem;
padding: 0.625rem;
border-radius: 0.3125rem;
}
#content .code, #content .playground {
background: #e9e9e9;
}
#content .output {
background: #202020;
}
#content .output .stdout, #content .output pre {
color: #e6e6e6;
}
#content .output .stderr, #content .output .error {
color: rgb(244, 74, 63);
}
#content .output .system, #content .output .exit {
color: rgb(255, 209, 77)
}
#content .buttons {
position: relative;
float: right;
top: -3.125rem;
right: 1.875rem;
}
#content .output .buttons {
top: -3.75rem;
right: 0;
height: 0;
}
#content .buttons .kill {
display: none;
visibility: hidden;
}
a.error {
font-weight: bold;
color: white;
background-color: darkred;
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
padding: 0.125rem 0.25rem 0.125rem 0.25rem; /* TRBL */
}
#heading-narrow {
display: none;
}
.downloading {
background: #F9F9BE;
padding: 0.625rem;
text-align: center;
border-radius: 0.3125rem;
}
@media (max-width: 58.125em) {
#heading-wide {
display: none;
}
#heading-narrow {
display: block;
}
}
@media (max-width: 47.5em) {
.container .left,
.container .right {
width: auto;
float: none;
}
div#about {
max-width: 31.25rem;
text-align: center;
}
}
@media (min-width: 43.75em) and (max-width: 62.5em) {
div#menu > a {
margin: 0.3125rem 0;
font-size: 0.875rem;
}
input#search {
font-size: 0.875rem;
}
}
@media (max-width: 43.75em) {
body {
font-size: 0.9375rem;
}
pre,
code {
font-size: 0.866rem;
}
div#page > .container {
padding: 0 0.625rem;
}
div#topbar {
height: auto;
padding: 0.625rem;
}
div#topbar > .container {
padding: 0;
}
#heading-wide {
display: block;
}
#heading-narrow {
display: none;
}
.top-heading {
float: none;
display: inline-block;
padding: 0.75rem;
}
div#menu {
padding: 0;
min-width: 0;
text-align: left;
float: left;
}
div#menu > a {
display: block;
margin-left: 0;
margin-right: 0;
}
#menu .search-box {
display: flex;
width: 100%;
}
#menu-button {
display: inline-block;
}
p,
pre,
ul,
ol {
margin: 0.625rem;
}
.pkg-synopsis {
display: none;
}
img.gopher {
display: none;
}
}
@media (max-width: 30em) {
#heading-wide {
display: none;
}
#heading-narrow {
display: block;
}
}
@media print {
pre {
background: #FFF;
border: 0.0625rem solid #BBB;
white-space: pre-wrap;
}
}

82
vendor/golang.org/x/tools/godoc/tab.go generated vendored Normal file
View File

@@ -0,0 +1,82 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TODO(bradfitz,adg): move to util
package godoc
import "io"
var spaces = []byte(" ") // 32 spaces seems like a good number
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
p *Presentation
}
func (p *tconv) writeIndent() (err error) {
i := p.indent
for i >= len(spaces) {
i -= len(spaces)
if _, err = p.output.Write(spaces); err != nil {
return
}
}
// i < len(spaces)
if i > 0 {
_, err = p.output.Write(spaces[0:i])
}
return
}
func (p *tconv) Write(data []byte) (n int, err error) {
if len(data) == 0 {
return
}
pos := 0 // valid if p.state == collecting
var b byte
for n, b = range data {
switch p.state {
case indenting:
switch b {
case '\t':
p.indent += p.p.TabWidth
case '\n':
p.indent = 0
if _, err = p.output.Write(data[n : n+1]); err != nil {
return
}
case ' ':
p.indent++
default:
p.state = collecting
pos = n
if err = p.writeIndent(); err != nil {
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 pos < n && p.state == collecting {
_, err = p.output.Write(data[pos:])
}
return
}

179
vendor/golang.org/x/tools/godoc/template.go generated vendored Normal file
View File

@@ -0,0 +1,179 @@
// Copyright 2011 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.
// Template support for writing HTML documents.
// Documents that include Template: true in their
// metadata are executed as input to text/template.
//
// This file defines functions for those templates to invoke.
// The template uses the function "code" to inject program
// source into the output by extracting code from files and
// injecting them as HTML-escaped <pre> blocks.
//
// The syntax is simple: 1, 2, or 3 space-separated arguments:
//
// Whole file:
// {{code "foo.go"}}
// One line (here the signature of main):
// {{code "foo.go" `/^func.main/`}}
// Block of text, determined by start and end (here the body of main):
// {{code "foo.go" `/^func.main/` `/^}/`
//
// Patterns can be `/regular expression/`, a decimal number, or "$"
// to signify the end of the file. In multi-line matches,
// lines that end with the four characters
// OMIT
// are omitted from the output, making it easy to provide marker
// lines in the input that will not appear in the output but are easy
// to identify by pattern.
package godoc
import (
"bytes"
"fmt"
"log"
"regexp"
"strings"
"golang.org/x/tools/godoc/vfs"
)
// Functions in this file panic on error, but the panic is recovered
// to an error by 'code'.
// contents reads and returns the content of the named file
// (from the virtual file system, so for example /doc refers to $GOROOT/doc).
func (c *Corpus) contents(name string) string {
file, err := vfs.ReadFile(c.fs, name)
if err != nil {
log.Panic(err)
}
return string(file)
}
// stringFor returns a textual representation of the arg, formatted according to its nature.
func stringFor(arg interface{}) string {
switch arg := arg.(type) {
case int:
return fmt.Sprintf("%d", arg)
case string:
if len(arg) > 2 && arg[0] == '/' && arg[len(arg)-1] == '/' {
return fmt.Sprintf("%#q", arg)
}
return fmt.Sprintf("%q", arg)
default:
log.Panicf("unrecognized argument: %v type %T", arg, arg)
}
return ""
}
func (p *Presentation) code(file string, arg ...interface{}) (s string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
}()
text := p.Corpus.contents(file)
var command string
switch len(arg) {
case 0:
// text is already whole file.
command = fmt.Sprintf("code %q", file)
case 1:
command = fmt.Sprintf("code %q %s", file, stringFor(arg[0]))
text = p.Corpus.oneLine(file, text, arg[0])
case 2:
command = fmt.Sprintf("code %q %s %s", file, stringFor(arg[0]), stringFor(arg[1]))
text = p.Corpus.multipleLines(file, text, arg[0], arg[1])
default:
return "", fmt.Errorf("incorrect code invocation: code %q [%v, ...] (%d arguments)", file, arg[0], len(arg))
}
// Trim spaces from output.
text = strings.Trim(text, "\n")
// Replace tabs by spaces, which work better in HTML.
text = strings.Replace(text, "\t", " ", -1)
var buf bytes.Buffer
// HTML-escape text and syntax-color comments like elsewhere.
FormatText(&buf, []byte(text), -1, true, "", nil)
// Include the command as a comment.
text = fmt.Sprintf("<pre><!--{{%s}}\n-->%s</pre>", command, buf.Bytes())
return text, nil
}
// parseArg returns the integer or string value of the argument and tells which it is.
func parseArg(arg interface{}, file string, max int) (ival int, sval string, isInt bool) {
switch n := arg.(type) {
case int:
if n <= 0 || n > max {
log.Panicf("%q:%d is out of range", file, n)
}
return n, "", true
case string:
return 0, n, false
}
log.Panicf("unrecognized argument %v type %T", arg, arg)
return
}
// oneLine returns the single line generated by a two-argument code invocation.
func (c *Corpus) oneLine(file, text string, arg interface{}) string {
lines := strings.SplitAfter(c.contents(file), "\n")
line, pattern, isInt := parseArg(arg, file, len(lines))
if isInt {
return lines[line-1]
}
return lines[match(file, 0, lines, pattern)-1]
}
// multipleLines returns the text generated by a three-argument code invocation.
func (c *Corpus) multipleLines(file, text string, arg1, arg2 interface{}) string {
lines := strings.SplitAfter(c.contents(file), "\n")
line1, pattern1, isInt1 := parseArg(arg1, file, len(lines))
line2, pattern2, isInt2 := parseArg(arg2, file, len(lines))
if !isInt1 {
line1 = match(file, 0, lines, pattern1)
}
if !isInt2 {
line2 = match(file, line1, lines, pattern2)
} else if line2 < line1 {
log.Panicf("lines out of order for %q: %d %d", text, line1, line2)
}
for k := line1 - 1; k < line2; k++ {
if strings.HasSuffix(lines[k], "OMIT\n") {
lines[k] = ""
}
}
return strings.Join(lines[line1-1:line2], "")
}
// match identifies the input line that matches the pattern in a code invocation.
// If start>0, match lines starting there rather than at the beginning.
// The return value is 1-indexed.
func match(file string, start int, lines []string, pattern string) int {
// $ matches the end of the file.
if pattern == "$" {
if len(lines) == 0 {
log.Panicf("%q: empty file", file)
}
return len(lines)
}
// /regexp/ matches the line that matches the regexp.
if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
re, err := regexp.Compile(pattern[1 : len(pattern)-1])
if err != nil {
log.Panic(err)
}
for i := start; i < len(lines); i++ {
if re.MatchString(lines[i]) {
return i + 1
}
}
log.Panicf("%s: no match for %#q", file, pattern)
}
log.Panicf("unrecognized pattern: %q", pattern)
return 0
}

88
vendor/golang.org/x/tools/godoc/util/throttle.go generated vendored Normal file
View File

@@ -0,0 +1,88 @@
// Copyright 2011 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 util
import "time"
// A Throttle permits throttling of a goroutine by
// calling the Throttle method repeatedly.
//
type Throttle struct {
f float64 // f = (1-r)/r for 0 < r < 1
dt time.Duration // minimum run time slice; >= 0
tr time.Duration // accumulated time running
ts time.Duration // accumulated time stopped
tt time.Time // earliest throttle time (= time Throttle returned + tm)
}
// NewThrottle creates a new Throttle with a throttle value r and
// a minimum allocated run time slice of dt:
//
// r == 0: "empty" throttle; the goroutine is always sleeping
// r == 1: full throttle; the goroutine is never sleeping
//
// A value of r == 0.6 throttles a goroutine such that it runs
// approx. 60% of the time, and sleeps approx. 40% of the time.
// Values of r < 0 or r > 1 are clamped down to values between 0 and 1.
// Values of dt < 0 are set to 0.
//
func NewThrottle(r float64, dt time.Duration) *Throttle {
var f float64
switch {
case r <= 0:
f = -1 // indicates always sleep
case r >= 1:
f = 0 // assume r == 1 (never sleep)
default:
// 0 < r < 1
f = (1 - r) / r
}
if dt < 0 {
dt = 0
}
return &Throttle{f: f, dt: dt, tt: time.Now().Add(dt)}
}
// Throttle calls time.Sleep such that over time the ratio tr/ts between
// accumulated run (tr) and sleep times (ts) approximates the value 1/(1-r)
// where r is the throttle value. Throttle returns immediately (w/o sleeping)
// if less than tm ns have passed since the last call to Throttle.
//
func (p *Throttle) Throttle() {
if p.f < 0 {
select {} // always sleep
}
t0 := time.Now()
if t0.Before(p.tt) {
return // keep running (minimum time slice not exhausted yet)
}
// accumulate running time
p.tr += t0.Sub(p.tt) + p.dt
// compute sleep time
// Over time we want:
//
// tr/ts = r/(1-r)
//
// Thus:
//
// ts = tr*f with f = (1-r)/r
//
// After some incremental run time δr added to the total run time
// tr, the incremental sleep-time δs to get to the same ratio again
// after waking up from time.Sleep is:
if δs := time.Duration(float64(p.tr)*p.f) - p.ts; δs > 0 {
time.Sleep(δs)
}
// accumulate (actual) sleep time
t1 := time.Now()
p.ts += t1.Sub(t0)
// set earliest next throttle time
p.tt = t1.Add(p.dt)
}

89
vendor/golang.org/x/tools/godoc/util/util.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package util contains utility types and functions for godoc.
package util // import "golang.org/x/tools/godoc/util"
import (
pathpkg "path"
"sync"
"time"
"unicode/utf8"
"golang.org/x/tools/godoc/vfs"
)
// An RWValue wraps a value and permits mutually exclusive
// access to it and records the time the value was last set.
type RWValue struct {
mutex sync.RWMutex
value interface{}
timestamp time.Time // time of last set()
}
func (v *RWValue) Set(value interface{}) {
v.mutex.Lock()
v.value = value
v.timestamp = time.Now()
v.mutex.Unlock()
}
func (v *RWValue) Get() (interface{}, time.Time) {
v.mutex.RLock()
defer v.mutex.RUnlock()
return v.value, v.timestamp
}
// IsText reports whether a significant prefix of s looks like correct UTF-8;
// that is, if it is likely that s is human-readable text.
func IsText(s []byte) bool {
const max = 1024 // at least utf8.UTFMax
if len(s) > max {
s = s[0:max]
}
for i, c := range string(s) {
if i+utf8.UTFMax > len(s) {
// last char may be incomplete - ignore
break
}
if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
// decoding error or control character - not a text file
return false
}
}
return true
}
// textExt[x] is true if the extension x indicates a text file, and false otherwise.
var textExt = map[string]bool{
".css": false, // must be served raw
".js": false, // must be served raw
}
// IsTextFile reports whether the file has a known extension indicating
// a text file, or if a significant chunk of the specified file looks like
// correct UTF-8; that is, if it is likely that the file contains human-
// readable text.
func IsTextFile(fs vfs.Opener, filename string) bool {
// if the extension is known, use it for decision making
if isText, found := textExt[pathpkg.Ext(filename)]; found {
return isText
}
// the extension is not known; read an initial chunk
// of the file and check if it looks like text
f, err := fs.Open(filename)
if err != nil {
return false
}
defer f.Close()
var buf [1024]byte
n, err := f.Read(buf[0:])
if err != nil {
return false
}
return IsText(buf[0:n])
}

Some files were not shown because too many files have changed in this diff Show More