godoc, cmd/godoc: remove CLI support
godoc is now just a webserver (the one that runs golang.org)
A future change might rename this to something else, but not now.
Fixes golang/go#25443
Change-Id: Ib95d0589e13f4c7a717ff28d694f1472e427eef4
Reviewed-on: https://go-review.googlesource.com/c/141397
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/cmd/godoc/appinit.go b/cmd/godoc/appinit.go
index cb747b3..6318bb7 100644
--- a/cmd/godoc/appinit.go
+++ b/cmd/godoc/appinit.go
@@ -89,12 +89,11 @@
pres = godoc.NewPresentation(corpus)
pres.TabWidth = 8
pres.ShowPlayground = true
- pres.ShowExamples = true
pres.DeclLinks = true
pres.NotesRx = regexp.MustCompile("BUG")
pres.GoogleAnalytics = os.Getenv("GODOC_ANALYTICS")
- readTemplates(pres, true)
+ readTemplates(pres)
datastoreClient, memcacheClient := getClients()
diff --git a/cmd/godoc/godoc_test.go b/cmd/godoc/godoc_test.go
index 7200c91..54fe540 100644
--- a/cmd/godoc/godoc_test.go
+++ b/cmd/godoc/godoc_test.go
@@ -54,75 +54,6 @@
return bin, func() { os.RemoveAll(tmp) }
}
-// Basic regression test for godoc command-line tool.
-func TestCLI(t *testing.T) {
- bin, cleanup := buildGodoc(t)
- defer cleanup()
-
- tests := []struct {
- args []string
- matches []string // regular expressions
- dontmatch []string // regular expressions
- }{
- {
- args: []string{"fmt"},
- matches: []string{
- `import "fmt"`,
- `Package fmt implements formatted I/O`,
- },
- },
- {
- args: []string{"io", "WriteString"},
- matches: []string{
- `func WriteString\(`,
- `WriteString writes the contents of the string s to w`,
- },
- },
- {
- args: []string{"nonexistingpkg"},
- matches: []string{
- `cannot find package`,
- },
- },
- {
- args: []string{"fmt", "NonexistentSymbol"},
- matches: []string{
- `No match found\.`,
- },
- },
- {
- args: []string{"-src", "syscall", "Open"},
- matches: []string{
- `func Open\(`,
- },
- dontmatch: []string{
- `No match found\.`,
- },
- },
- }
- for _, test := range tests {
- cmd := exec.Command(bin, test.args...)
- cmd.Args[0] = "godoc"
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Errorf("Running with args %#v: %v", test.args, err)
- continue
- }
- for _, pat := range test.matches {
- re := regexp.MustCompile(pat)
- if !re.Match(out) {
- t.Errorf("godoc %v =\n%s\nwanted /%v/", strings.Join(test.args, " "), out, pat)
- }
- }
- for _, pat := range test.dontmatch {
- re := regexp.MustCompile(pat)
- if re.Match(out) {
- t.Errorf("godoc %v =\n%s\ndid not want /%v/", strings.Join(test.args, " "), out, pat)
- }
- }
- }
-}
-
func serverAddress(t *testing.T) string {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
diff --git a/cmd/godoc/handlers.go b/cmd/godoc/handlers.go
index fabb679..d393992 100644
--- a/cmd/godoc/handlers.go
+++ b/cmd/godoc/handlers.go
@@ -112,28 +112,23 @@
return t
}
-func readTemplates(p *godoc.Presentation, html bool) {
- p.PackageText = readTemplate("package.txt")
- p.SearchText = readTemplate("search.txt")
-
- if html || p.HTMLMode {
- codewalkHTML = readTemplate("codewalk.html")
- codewalkdirHTML = readTemplate("codewalkdir.html")
- p.CallGraphHTML = readTemplate("callgraph.html")
- p.DirlistHTML = readTemplate("dirlist.html")
- p.ErrorHTML = readTemplate("error.html")
- p.ExampleHTML = readTemplate("example.html")
- p.GodocHTML = readTemplate("godoc.html")
- p.ImplementsHTML = readTemplate("implements.html")
- p.MethodSetHTML = readTemplate("methodset.html")
- p.PackageHTML = readTemplate("package.html")
- p.PackageRootHTML = readTemplate("packageroot.html")
- p.SearchHTML = readTemplate("search.html")
- p.SearchDocHTML = readTemplate("searchdoc.html")
- p.SearchCodeHTML = readTemplate("searchcode.html")
- p.SearchTxtHTML = readTemplate("searchtxt.html")
- p.SearchDescXML = readTemplate("opensearch.xml")
- }
+func readTemplates(p *godoc.Presentation) {
+ codewalkHTML = readTemplate("codewalk.html")
+ codewalkdirHTML = readTemplate("codewalkdir.html")
+ p.CallGraphHTML = readTemplate("callgraph.html")
+ p.DirlistHTML = readTemplate("dirlist.html")
+ p.ErrorHTML = readTemplate("error.html")
+ p.ExampleHTML = readTemplate("example.html")
+ p.GodocHTML = readTemplate("godoc.html")
+ p.ImplementsHTML = readTemplate("implements.html")
+ p.MethodSetHTML = readTemplate("methodset.html")
+ p.PackageHTML = readTemplate("package.html")
+ p.PackageRootHTML = readTemplate("packageroot.html")
+ p.SearchHTML = readTemplate("search.html")
+ p.SearchDocHTML = readTemplate("searchdoc.html")
+ p.SearchCodeHTML = readTemplate("searchcode.html")
+ p.SearchTxtHTML = readTemplate("searchtxt.html")
+ p.SearchDescXML = readTemplate("opensearch.xml")
}
type fmtResponse struct {
diff --git a/cmd/godoc/main.go b/cmd/godoc/main.go
index 7f0ac0c..19b5a45 100644
--- a/cmd/godoc/main.go
+++ b/cmd/godoc/main.go
@@ -53,7 +53,7 @@
"golang.org/x/tools/godoc/vfs/zipfs"
)
-const defaultAddr = ":6060" // default webserver address
+const defaultAddr = "localhost:6060" // default webserver address
var (
// file system to serve
@@ -66,18 +66,11 @@
analysisFlag = flag.String("analysis", "", `comma-separated list of analyses to perform (supported: type, pointer). See http://golang.org/lib/godoc/analysis/help.html`)
// network
- httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')")
- serverAddr = flag.String("server", "", "webserver address for command line searches")
+ httpAddr = flag.String("http", defaultAddr, "HTTP service address")
// layout control
- html = flag.Bool("html", false, "print HTML in command-line mode")
- srcMode = flag.Bool("src", false, "print (exported) source in command-line mode")
- allMode = flag.Bool("all", false, "include unexported identifiers in command-line mode")
urlFlag = flag.String("url", "", "print HTML for named URL")
- // command-line searches
- query = flag.Bool("q", false, "arguments are considered search queries")
-
verbose = flag.Bool("v", false, "verbose mode")
// file system roots
@@ -85,11 +78,9 @@
goroot = flag.String("goroot", findGOROOT(), "Go root directory")
// layout control
- tabWidth = flag.Int("tabwidth", 4, "tab width")
showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
templateDir = flag.String("templates", "", "load templates/JS/CSS from disk in this directory")
showPlayground = flag.Bool("play", false, "enable playground in web interface")
- showExamples = flag.Bool("ex", false, "show examples in command line mode")
declLinks = flag.Bool("links", true, "link identifiers to their declarations")
// search index
@@ -104,9 +95,7 @@
)
func usage() {
- fmt.Fprintf(os.Stderr,
- "usage: godoc package [name ...]\n"+
- " godoc -http="+defaultAddr+"\n")
+ fmt.Fprintf(os.Stderr, "usage: godoc -http="+defaultAddr+"\n")
flag.PrintDefaults()
os.Exit(2)
}
@@ -173,7 +162,7 @@
// Check usage: server and no args.
if (*httpAddr != "" || *urlFlag != "") && (flag.NArg() > 0) {
- fmt.Fprintln(os.Stderr, "can't use -http with args.")
+ fmt.Fprintln(os.Stderr, "Unexpected arguments.")
usage()
}
@@ -183,11 +172,10 @@
usage()
}
- // Setting the resolved goroot.
+ // Set the resolved goroot.
vfs.GOROOT = *goroot
- var fsGate chan bool
- fsGate = make(chan bool, 20)
+ fsGate := make(chan bool, 20)
// Determine file system to use.
if *zipfile == "" {
@@ -214,8 +202,6 @@
fs.Bind("/src", gatefs.New(vfs.OS(p), fsGate), "/src", vfs.BindAfter)
}
- httpMode := *httpAddr != ""
-
var typeAnalysis, pointerAnalysis bool
if *analysisFlag != "" {
for _, a := range strings.Split(*analysisFlag, ",") {
@@ -233,7 +219,7 @@
corpus := godoc.NewCorpus(fs)
corpus.Verbose = *verbose
corpus.MaxResults = *maxResults
- corpus.IndexEnabled = *indexEnabled && httpMode
+ corpus.IndexEnabled = *indexEnabled
if *maxResults == 0 {
corpus.IndexFullText = false
}
@@ -245,32 +231,21 @@
corpus.IndexThrottle = 1.0
corpus.IndexEnabled = true
}
- if *writeIndex || httpMode || *urlFlag != "" {
- if httpMode {
- go initCorpus(corpus)
- } else {
- initCorpus(corpus)
- }
- }
+ go initCorpus(corpus)
// Initialize the version info before readTemplates, which saves
// the map value in a method value.
corpus.InitVersionInfo()
pres = godoc.NewPresentation(corpus)
- pres.TabWidth = *tabWidth
pres.ShowTimestamps = *showTimestamps
pres.ShowPlayground = *showPlayground
- pres.ShowExamples = *showExamples
pres.DeclLinks = *declLinks
- pres.SrcMode = *srcMode
- pres.HTMLMode = *html
- pres.AllMode = *allMode
if *notesRx != "" {
pres.NotesRx = regexp.MustCompile(*notesRx)
}
- readTemplates(pres, httpMode || *urlFlag != "")
+ readTemplates(pres)
registerHandlers(pres)
if *writeIndex {
@@ -305,67 +280,51 @@
return
}
- if httpMode {
- // HTTP server mode.
- var handler http.Handler = http.DefaultServeMux
- if *verbose {
- log.Printf("Go Documentation Server")
- log.Printf("version = %s", runtime.Version())
- log.Printf("address = %s", *httpAddr)
- log.Printf("goroot = %s", *goroot)
- log.Printf("tabwidth = %d", *tabWidth)
- switch {
- case !*indexEnabled:
- log.Print("search index disabled")
- case *maxResults > 0:
- log.Printf("full text index enabled (maxresults = %d)", *maxResults)
- default:
- log.Print("identifier search index enabled")
+ var handler http.Handler = http.DefaultServeMux
+ if *verbose {
+ log.Printf("Go Documentation Server")
+ log.Printf("version = %s", runtime.Version())
+ log.Printf("address = %s", *httpAddr)
+ log.Printf("goroot = %s", *goroot)
+ switch {
+ case !*indexEnabled:
+ log.Print("search index disabled")
+ case *maxResults > 0:
+ log.Printf("full text index enabled (maxresults = %d)", *maxResults)
+ default:
+ log.Print("identifier search index enabled")
+ }
+ fs.Fprint(os.Stderr)
+ handler = loggingHandler(handler)
+ }
+
+ // Initialize search index.
+ if *indexEnabled {
+ go corpus.RunIndexer()
+ }
+
+ // Start type/pointer analysis.
+ if typeAnalysis || pointerAnalysis {
+ go analysis.Run(pointerAnalysis, &corpus.Analysis)
+ }
+
+ if runHTTPS != nil {
+ go func() {
+ if err := runHTTPS(handler); err != nil {
+ log.Fatalf("ListenAndServe TLS: %v", err)
}
- fs.Fprint(os.Stderr)
- handler = loggingHandler(handler)
- }
-
- // Initialize search index.
- if *indexEnabled {
- go corpus.RunIndexer()
- }
-
- // Start type/pointer analysis.
- if typeAnalysis || pointerAnalysis {
- go analysis.Run(pointerAnalysis, &corpus.Analysis)
- }
-
- if runHTTPS != nil {
- go func() {
- if err := runHTTPS(handler); err != nil {
- log.Fatalf("ListenAndServe TLS: %v", err)
- }
- }()
- }
-
- // Start http server.
- if *verbose {
- log.Println("starting HTTP server")
- }
- if wrapHTTPMux != nil {
- handler = wrapHTTPMux(handler)
- }
- if err := http.ListenAndServe(*httpAddr, handler); err != nil {
- log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
- }
-
- return
+ }()
}
- if *query {
- handleRemoteSearch()
- return
+ // Start http server.
+ if *verbose {
+ log.Println("starting HTTP server")
}
-
- build.Default.GOROOT = *goroot
- if err := godoc.CommandLine(os.Stdout, fs, pres, flag.Args()); err != nil {
- log.Print(err)
+ if wrapHTTPMux != nil {
+ handler = wrapHTTPMux(handler)
+ }
+ if err := http.ListenAndServe(*httpAddr, handler); err != nil {
+ log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
}
}
diff --git a/cmd/godoc/remotesearch.go b/cmd/godoc/remotesearch.go
deleted file mode 100644
index 6f27d0b..0000000
--- a/cmd/godoc/remotesearch.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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.
-
-// +build !golangorg
-
-package main
-
-import (
- "errors"
- "flag"
- "io"
- "log"
- "net/http"
- "net/url"
- "os"
-)
-
-func handleRemoteSearch() {
- // Command-line queries.
- for i := 0; i < flag.NArg(); i++ {
- res, err := remoteSearch(flag.Arg(i))
- if err != nil {
- log.Fatalf("remoteSearch: %s", err)
- }
- io.Copy(os.Stdout, res.Body)
- }
- return
-}
-
-// remoteSearchURL returns the search URL for a given query as needed by
-// remoteSearch. If html is set, an html result is requested; otherwise
-// the result is in textual form.
-// Adjust this function as necessary if modeNames or FormValue parameters
-// change.
-func remoteSearchURL(query string, html bool) string {
- s := "/search?m=text&q="
- if html {
- s = "/search?q="
- }
- return s + url.QueryEscape(query)
-}
-
-func remoteSearch(query string) (res *http.Response, err error) {
- // list of addresses to try
- var addrs []string
- if *serverAddr != "" {
- // explicit server address - only try this one
- addrs = []string{*serverAddr}
- } else {
- addrs = []string{
- defaultAddr,
- "golang.org",
- }
- }
-
- // remote search
- search := remoteSearchURL(query, *html)
- for _, addr := range addrs {
- url := "http://" + addr + search
- res, err = http.Get(url)
- if err == nil && res.StatusCode == http.StatusOK {
- break
- }
- }
-
- if err == nil && res.StatusCode != http.StatusOK {
- err = errors.New(res.Status)
- }
-
- return
-}
diff --git a/godoc/cmdline.go b/godoc/cmdline.go
deleted file mode 100644
index 54f5f7e..0000000
--- a/godoc/cmdline.go
+++ /dev/null
@@ -1,217 +0,0 @@
-// 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)
-}
diff --git a/godoc/cmdline_test.go b/godoc/cmdline_test.go
deleted file mode 100644
index cebbf70..0000000
--- a/godoc/cmdline_test.go
+++ /dev/null
@@ -1,312 +0,0 @@
-// 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)
- }
- }
-}
diff --git a/godoc/godoc.go b/godoc/godoc.go
index 0acb49b..5add835 100644
--- a/godoc/godoc.go
+++ b/godoc/godoc.go
@@ -77,7 +77,6 @@
"node": p.nodeFunc,
"node_html": p.node_htmlFunc,
"comment_html": comment_htmlFunc,
- "comment_text": comment_textFunc,
"sanitize": sanitizeFunc,
// support for URL attributes
@@ -91,7 +90,6 @@
// formatting of Examples
"example_html": p.example_htmlFunc,
- "example_text": p.example_textFunc,
"example_name": p.example_nameFunc,
"example_suffix": p.example_suffixFunc,
@@ -372,15 +370,6 @@
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.
@@ -589,50 +578,6 @@
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 {
diff --git a/godoc/pres.go b/godoc/pres.go
index b0077fd..800937a 100644
--- a/godoc/pres.go
+++ b/godoc/pres.go
@@ -34,12 +34,10 @@
MethodSetHTML,
PackageHTML,
PackageRootHTML,
- PackageText,
SearchHTML,
SearchDocHTML,
SearchCodeHTML,
SearchTxtHTML,
- SearchText,
SearchDescXML *template.Template
// TabWidth optionally specifies the tab width.
@@ -47,7 +45,6 @@
ShowTimestamps bool
ShowPlayground bool
- ShowExamples bool
DeclLinks bool
// SrcMode outputs source code instead of documentation in command-line mode.
@@ -113,9 +110,8 @@
mux: http.NewServeMux(),
fileServer: http.FileServer(httpfs.New(c.fs)),
- TabWidth: 4,
- ShowExamples: true,
- DeclLinks: true,
+ TabWidth: 4,
+ DeclLinks: true,
SearchResults: []SearchResultFunc{
(*Presentation).SearchResultDoc,
(*Presentation).SearchResultCode,
diff --git a/godoc/search.go b/godoc/search.go
index d61193f..870aefc 100644
--- a/godoc/search.go
+++ b/godoc/search.go
@@ -99,11 +99,7 @@
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{}
+ var contents bytes.Buffer
for _, f := range p.SearchResults {
contents.Write(f(p, result))
}
diff --git a/godoc/server.go b/godoc/server.go
index 2998967..39f6922 100644
--- a/godoc/server.go
+++ b/godoc/server.go
@@ -276,11 +276,6 @@
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:
diff --git a/godoc/static/gen.go b/godoc/static/gen.go
index 399fb84..76f7d34 100644
--- a/godoc/static/gen.go
+++ b/godoc/static/gen.go
@@ -57,11 +57,9 @@
"opensearch.xml",
"package.html",
"packageroot.html",
- "package.txt",
"play.js",
"playground.js",
"search.html",
- "search.txt",
"searchcode.html",
"searchdoc.html",
"searchtxt.html",
diff --git a/godoc/static/package.txt b/godoc/static/package.txt
deleted file mode 100644
index e53fa6e..0000000
--- a/godoc/static/package.txt
+++ /dev/null
@@ -1,116 +0,0 @@
-{{$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
-*/}}
diff --git a/godoc/static/search.txt b/godoc/static/search.txt
deleted file mode 100644
index 0ae0c08..0000000
--- a/godoc/static/search.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-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}}
diff --git a/godoc/static/static.go b/godoc/static/static.go
index 0fc0b40..626b134 100644
--- a/godoc/static/static.go
+++ b/godoc/static/static.go
@@ -89,16 +89,12 @@
"packageroot.html": "<!--\x0a\x09Copyright\x202018\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a\x09Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a\x09license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a-->\x0a<!--\x0a\x09Note:\x20Static\x20(i.e.,\x20not\x20template-generated)\x20href\x20and\x20id\x0a\x09attributes\x20start\x20with\x20\"pkg-\"\x20to\x20make\x20it\x20impossible\x20for\x0a\x09them\x20to\x20conflict\x20with\x20generated\x20attributes\x20(some\x20of\x20which\x0a\x09correspond\x20to\x20Go\x20identifiers).\x0a-->\x0a{{with\x20.PAst}}\x0a\x09{{range\x20$filename,\x20$ast\x20:=\x20.}}\x0a\x09\x09<a\x20href=\"{{$filename|srcLink|html}}\">{{$filename|filename|html}}</a>:<pre>{{node_html\x20$\x20$ast\x20false}}</pre>\x0a\x09{{end}}\x0a{{end}}\x0a\x0a{{with\x20.Dirs}}\x0a\x09{{/*\x20DirList\x20entries\x20are\x20numbers\x20and\x20strings\x20-\x20no\x20need\x20for\x20FSet\x20*/}}\x0a\x09{{if\x20$.PDoc}}\x0a\x09\x09<h2\x20id=\"pkg-subdirectories\">Subdirectories</h2>\x0a\x09{{end}}\x0a\x09\x09<div\x20id=\"manual-nav\">\x0a\x09\x09\x09<dl>\x0a\x09\x09\x09\x09<dt><a\x20href=\"#stdlib\">Standard\x20library</a></dt>\x0a\x09\x09\x09\x09{{if\x20hasThirdParty\x20.List\x20}}\x0a\x09\x09\x09\x09\x09<dt><a\x20href=\"#thirdparty\">Third\x20party</a></dt>\x0a\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09<dt><a\x20href=\"#other\">Other\x20packages</a></dt>\x0a\x09\x09\x09\x09<dd><a\x20href=\"#subrepo\">Sub-repositories</a></dd>\x0a\x09\x09\x09\x09<dd><a\x20href=\"#community\">Community</a></dd>\x0a\x09\x09\x09</dl>\x0a\x09\x09</div>\x0a\x0a\x09\x09<div\x20id=\"stdlib\"\x20class=\"toggleVisible\">\x0a\x09\x09\x09<div\x20class=\"collapsed\">\x0a\x09\x09\x09\x09<h2\x20class=\"toggleButton\"\x20title=\"Click\x20to\x20show\x20Standard\x20library\x20section\">Standard\x20library\x20\xe2\x96\xb9</h2>\x0a\x09\x09\x09</div>\x0a\x09\x09\x09<div\x20class=\"expanded\">\x0a\x09\x09\x09\x09<h2\x20class=\"toggleButton\"\x20title=\"Click\x20to\x20hide\x20Standard\x20library\x20section\">Standard\x20library\x20\xe2\x96\xbe</h2>\x0a\x09\x09\x09\x09<img\x20alt=\"\"\x20class=\"gopher\"\x20src=\"/doc/gopher/pkg.png\"/>\x0a\x09\x09\x09\x09<div\x20class=\"pkg-dir\">\x0a\x09\x09\x09\x09\x09<table>\x0a\x09\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09\x09\x09<th\x20class=\"pkg-name\">Name</th>\x0a\x09\x09\x09\x09\x09\x09\x09<th\x20class=\"pkg-synopsis\">Synopsis</th>\x0a\x09\x09\x09\x09\x09\x09</tr>\x0a\x0a\x09\x09\x09\x09\x09\x09{{range\x20.List}}\x0a\x09\x09\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09\x09\x09{{if\x20eq\x20.RootType\x20\"GOROOT\"}}\x0a\x09\x09\x09\x09\x09\x09\x09{{if\x20$.DirFlat}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{if\x20.HasPkg}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-name\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<a\x20href=\"{{html\x20.Path}}/{{modeQueryString\x20$.Mode\x20|\x20html}}\">{{html\x20.Path}}</a>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09{{else}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-name\"\x20style=\"padding-left:\x20{{multiply\x20.Depth\x2020}}px;\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<a\x20href=\"{{html\x20.Path}}/{{modeQueryString\x20$.Mode\x20|\x20html}}\">{{html\x20.Name}}</a>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-synopsis\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09{{html\x20.Synopsis}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09</tr>\x0a\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09</table>\x0a\x09\x09\x09\x09</div>\x20<!--\x20.pkg-dir\x20-->\x0a\x09\x09\x09</div>\x20<!--\x20.expanded\x20-->\x0a\x09\x09</div>\x20<!--\x20#stdlib\x20.toggleVisible\x20-->\x0a\x0a\x09{{if\x20hasThirdParty\x20.List\x20}}\x0a\x09\x09<div\x20id=\"thirdparty\"\x20class=\"toggleVisible\">\x0a\x09\x09\x09<div\x20class=\"collapsed\">\x0a\x09\x09\x09\x09<h2\x20class=\"toggleButton\"\x20title=\"Click\x20to\x20show\x20Third\x20party\x20section\">Third\x20party\x20\xe2\x96\xb9</h2>\x0a\x09\x09\x09</div>\x0a\x09\x09\x09<div\x20class=\"expanded\">\x0a\x09\x09\x09\x09<h2\x20class=\"toggleButton\"\x20title=\"Click\x20to\x20hide\x20Third\x20party\x20section\">Third\x20party\x20\xe2\x96\xbe</h2>\x0a\x09\x09\x09\x09<div\x20class=\"pkg-dir\">\x0a\x09\x09\x09\x09\x09<table>\x0a\x09\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09\x09\x09<th\x20class=\"pkg-name\">Name</th>\x0a\x09\x09\x09\x09\x09\x09\x09<th\x20class=\"pkg-synopsis\">Synopsis</th>\x0a\x09\x09\x09\x09\x09\x09</tr>\x0a\x0a\x09\x09\x09\x09\x09\x09{{range\x20.List}}\x0a\x09\x09\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{if\x20eq\x20.RootType\x20\"GOPATH\"}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{if\x20$.DirFlat}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09{{if\x20.HasPkg}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-name\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<a\x20href=\"{{html\x20.Path}}/{{modeQueryString\x20$.Mode\x20|\x20html}}\">{{html\x20.Path}}</a>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{else}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-name\"\x20style=\"padding-left:\x20{{multiply\x20.Depth\x2020}}px;\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09<a\x20href=\"{{html\x20.Path}}/{{modeQueryString\x20$.Mode\x20|\x20html}}\">{{html\x20.Name}}</a>\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09<td\x20class=\"pkg-synopsis\">\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09{{html\x20.Synopsis}}\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09\x09\x09</tr>\x0a\x09\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09</table>\x0a\x09\x09\x09\x09</div>\x20<!--\x20.pkg-dir\x20-->\x0a\x09\x09\x09</div>\x20<!--\x20.expanded\x20-->\x0a\x09\x09</div>\x20<!--\x20#stdlib\x20.toggleVisible\x20-->\x0a\x09{{end}}\x0a\x0a\x09<h2\x20id=\"other\">Other\x20packages</h2>\x0a\x09<h3\x20id=\"subrepo\">Sub-repositories</h3>\x0a\x09<p>\x0a\x09These\x20packages\x20are\x20part\x20of\x20the\x20Go\x20Project\x20but\x20outside\x20the\x20main\x20Go\x20tree.\x0a\x09They\x20are\x20developed\x20under\x20looser\x20<a\x20href=\"/doc/go1compat\">compatibility\x20requirements</a>\x20than\x20the\x20Go\x20core.\x0a\x09Install\x20them\x20with\x20\"<a\x20href=\"/cmd/go/#hdr-Download_and_install_packages_and_dependencies\">go\x20get</a>\".\x0a\x09</p>\x0a\x09<ul>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/benchmarks\">benchmarks</a>\x20\xe2\x80\x94\x20benchmarks\x20to\x20measure\x20Go\x20as\x20it\x20is\x20developed.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/blog\">blog</a>\x20\xe2\x80\x94\x20<a\x20href=\"//blog.golang.org\">blog.golang.org</a>'s\x20implementation.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/build\">build</a>\x20\xe2\x80\x94\x20<a\x20href=\"//build.golang.org\">build.golang.org</a>'s\x20implementation.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/crypto\">crypto</a>\x20\xe2\x80\x94\x20additional\x20cryptography\x20packages.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/debug\">debug</a>\x20\xe2\x80\x94\x20an\x20experimental\x20debugger\x20for\x20Go.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/image\">image</a>\x20\xe2\x80\x94\x20additional\x20imaging\x20packages.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/mobile\">mobile</a>\x20\xe2\x80\x94\x20experimental\x20support\x20for\x20Go\x20on\x20mobile\x20platforms.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/net\">net</a>\x20\xe2\x80\x94\x20additional\x20networking\x20packages.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/perf\">perf</a>\x20\xe2\x80\x94\x20packages\x20and\x20tools\x20for\x20performance\x20measurement,\x20storage,\x20and\x20analysis.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/review\">review</a>\x20\xe2\x80\x94\x20a\x20tool\x20for\x20working\x20with\x20Gerrit\x20code\x20reviews.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/sync\">sync</a>\x20\xe2\x80\x94\x20additional\x20concurrency\x20primitives.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/sys\">sys</a>\x20\xe2\x80\x94\x20packages\x20for\x20making\x20system\x20calls.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/text\">text</a>\x20\xe2\x80\x94\x20packages\x20for\x20working\x20with\x20text.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/time\">time</a>\x20\xe2\x80\x94\x20additional\x20time\x20packages.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/tools\">tools</a>\x20\xe2\x80\x94\x20godoc,\x20goimports,\x20gorename,\x20and\x20other\x20tools.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/tour\">tour</a>\x20\xe2\x80\x94\x20<a\x20href=\"//tour.golang.org\">tour.golang.org</a>'s\x20implementation.</li>\x0a\x09\x09<li><a\x20href=\"//godoc.org/golang.org/x/exp\">exp</a>\x20\xe2\x80\x94\x20experimental\x20and\x20deprecated\x20packages\x20(handle\x20with\x20care;\x20may\x20change\x20without\x20warning).</li>\x0a\x09</ul>\x0a\x0a\x09<h3\x20id=\"community\">Community</h3>\x0a\x09<p>\x0a\x09These\x20services\x20can\x20help\x20you\x20find\x20Open\x20Source\x20packages\x20provided\x20by\x20the\x20community.\x0a\x09</p>\x0a\x09<ul>\x0a\x09\x09<li><a\x20href=\"//godoc.org\">GoDoc</a>\x20-\x20a\x20package\x20index\x20and\x20search\x20engine.</li>\x0a\x09\x09<li><a\x20href=\"http://go-search.org\">Go\x20Search</a>\x20-\x20a\x20code\x20search\x20engine.</li>\x0a\x09\x09<li><a\x20href=\"/wiki/Projects\">Projects\x20at\x20the\x20Go\x20Wiki</a>\x20-\x20a\x20curated\x20list\x20of\x20Go\x20projects.</li>\x0a\x09</ul>\x0a{{end}}\x0a",
- "package.txt": "{{$info\x20:=\x20.}}{{$filtered\x20:=\x20.IsFiltered}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20$filtered}}{{range\x20.PAst}}{{range\x20.Decls}}{{node\x20$info\x20.}}\x0a\x0a{{end}}{{end}}{{else}}{{with\x20.PAst}}{{range\x20$filename,\x20$ast\x20:=\x20.}}{{$filename}}:\x0a{{node\x20$\x20$ast}}{{end}}{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20and\x20$filtered\x20(not\x20(or\x20.PDoc\x20.PAst))}}No\x20match\x20found.\x0a{{end}}{{with\x20.PDoc}}{{if\x20$.IsMain}}COMMAND\x20DOCUMENTATION\x0a\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{else}}{{if\x20not\x20$filtered}}PACKAGE\x20DOCUMENTATION\x0a\x0apackage\x20{{.Name}}\x0a\x20\x20\x20\x20import\x20\"{{.ImportPath}}\"\x0a\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{example_text\x20$\x20\"\"\x20\"\x20\x20\x20\x20\"}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20.Consts}}{{if\x20not\x20$filtered}}CONSTANTS\x0a\x0a{{end}}{{range\x20.}}{{node\x20$\x20.Decl}}\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20.Vars}}{{if\x20not\x20$filtered}}VARIABLES\x0a\x0a{{end}}{{range\x20.}}{{node\x20$\x20.Decl}}\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20.Funcs}}{{if\x20not\x20$filtered}}FUNCTIONS\x0a\x0a{{end}}{{range\x20.}}{{node\x20$\x20.Decl}}\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{example_text\x20$\x20.Name\x20\"\x20\x20\x20\x20\"}}{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20.Types}}{{if\x20not\x20$filtered}}TYPES\x0a\x0a{{end}}{{range\x20.}}{{$tname\x20:=\x20.Name}}{{node\x20$\x20.Decl}}\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20.Consts}}{{range\x20.Consts}}{{node\x20$\x20.Decl}}\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20.Vars}}{{range\x20.Vars}}{{node\x20$\x20.Decl}}\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{range\x20$name\x20:=\x20.Names}}{{example_text\x20$\x20$name\x20\"\x20\x20\x20\x20\"}}{{end}}{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20.Funcs}}{{range\x20.Funcs}}{{node\x20$\x20.Decl}}\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{example_text\x20$\x20.Name\x20\"\x20\x20\x20\x20\"}}{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20.Methods}}{{range\x20.Methods}}{{node\x20$\x20.Decl}}\x0a{{comment_text\x20.Doc\x20\"\x20\x20\x20\x20\"\x20\"\\t\"}}\x0a{{$name\x20:=\x20printf\x20\"%s_%s\"\x20$tname\x20.Name}}{{example_text\x20$\x20$name\x20\"\x20\x20\x20\x20\"}}{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20and\x20$filtered\x20(not\x20(or\x20.Consts\x20(or\x20.Vars\x20(or\x20.Funcs\x20.Types))))}}No\x20match\x20found.\x0a{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20$.Notes}}\x0a{{range\x20$marker,\x20$content\x20:=\x20.}}\x0a{{$marker}}S\x0a\x0a{{range\x20$content}}{{comment_text\x20.Body\x20\"\x20\x20\x20\"\x20\"\\t\"}}\x0a{{end}}{{end}}{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20not\x20$filtered}}{{with\x20.Dirs}}SUBDIRECTORIES\x0a{{if\x20$.DirFlat}}{{range\x20.List}}{{if\x20.HasPkg}}\x0a\x09{{.Path}}{{end}}{{end}}\x0a{{else}}{{range\x20.List}}\x0a\x09{{repeat\x20`.\x20`\x20.Depth}}{{.Name}}{{end}}\x0a{{end}}{{end}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{end}}{{/*\x0aMake\x20sure\x20there\x20is\x20no\x20newline\x20at\x20the\x20end\x20of\x20this\x20file.\x0aperl\x20-i\x20-pe\x20'chomp\x20if\x20eof'\x20package.txt\x0a*/}}\x0a",
-
"play.js": "//\x20Copyright\x202012\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a//\x20Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a//\x20license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a\x0afunction\x20initPlayground(transport)\x20{\x0a\x09'use\x20strict';\x0a\x0a\x09function\x20text(node)\x20{\x0a\x09\x09var\x20s\x20=\x20'';\x0a\x09\x09for\x20(var\x20i\x20=\x200;\x20i\x20<\x20node.childNodes.length;\x20i++)\x20{\x0a\x09\x09\x09var\x20n\x20=\x20node.childNodes[i];\x0a\x09\x09\x09if\x20(n.nodeType\x20===\x201)\x20{\x0a\x09\x09\x09\x09if\x20(n.tagName\x20===\x20'BUTTON')\x20continue\x0a\x09\x09\x09\x09if\x20(n.tagName\x20===\x20'SPAN'\x20&&\x20n.className\x20===\x20'number')\x20continue;\x0a\x09\x09\x09\x09if\x20(n.tagName\x20===\x20'DIV'\x20||\x20n.tagName\x20==\x20'BR')\x20{\x0a\x09\x09\x09\x09\x09s\x20+=\x20\"\\n\";\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09\x09s\x20+=\x20text(n);\x0a\x09\x09\x09\x09continue;\x0a\x09\x09\x09}\x0a\x09\x09\x09if\x20(n.nodeType\x20===\x203)\x20{\x0a\x09\x09\x09\x09s\x20+=\x20n.nodeValue;\x0a\x09\x09\x09}\x0a\x09\x09}\x0a\x09\x09return\x20s.replace('\\xA0',\x20'\x20');\x20//\x20replace\x20non-breaking\x20spaces\x0a\x09}\x0a\x0a\x09//\x20When\x20presenter\x20notes\x20are\x20enabled,\x20the\x20index\x20passed\x0a\x09//\x20here\x20will\x20identify\x20the\x20playground\x20to\x20be\x20synced\x0a\x09function\x20init(code,\x20index)\x20{\x0a\x09\x09var\x20output\x20=\x20document.createElement('div');\x0a\x09\x09var\x20outpre\x20=\x20document.createElement('pre');\x0a\x09\x09var\x20running;\x0a\x0a\x09\x09if\x20($\x20&&\x20$(output).resizable)\x20{\x0a\x09\x09\x09$(output).resizable({\x0a\x09\x09\x09\x09handles:\x20\x09'n,w,nw',\x0a\x09\x09\x09\x09minHeight:\x0927,\x0a\x09\x09\x09\x09minWidth:\x09135,\x0a\x09\x09\x09\x09maxHeight:\x09608,\x0a\x09\x09\x09\x09maxWidth:\x09990\x0a\x09\x09\x09});\x0a\x09\x09}\x0a\x0a\x09\x09function\x20onKill()\x20{\x0a\x09\x09\x09if\x20(running)\x20running.Kill();\x0a\x09\x09\x09if\x20(window.notesEnabled)\x20updatePlayStorage('onKill',\x20index);\x0a\x09\x09}\x0a\x0a\x09\x09function\x20onRun(e)\x20{\x0a\x09\x09\x09var\x20sk\x20=\x20e.shiftKey\x20||\x20localStorage.getItem('play-shiftKey')\x20===\x20'true';\x0a\x09\x09\x09if\x20(running)\x20running.Kill();\x0a\x09\x09\x09output.style.display\x20=\x20'block';\x0a\x09\x09\x09outpre.innerHTML\x20=\x20'';\x0a\x09\x09\x09run1.style.display\x20=\x20'none';\x0a\x09\x09\x09var\x20options\x20=\x20{Race:\x20sk};\x0a\x09\x09\x09running\x20=\x20transport.Run(text(code),\x20PlaygroundOutput(outpre),\x20options);\x0a\x09\x09\x09if\x20(window.notesEnabled)\x20updatePlayStorage('onRun',\x20index,\x20e);\x0a\x09\x09}\x0a\x0a\x09\x09function\x20onClose()\x20{\x0a\x09\x09\x09if\x20(running)\x20running.Kill();\x0a\x09\x09\x09output.style.display\x20=\x20'none';\x0a\x09\x09\x09run1.style.display\x20=\x20'inline-block';\x0a\x09\x09\x09if\x20(window.notesEnabled)\x20updatePlayStorage('onClose',\x20index);\x0a\x09\x09}\x0a\x0a\x09\x09if\x20(window.notesEnabled)\x20{\x0a\x09\x09\x09playgroundHandlers.onRun.push(onRun);\x0a\x09\x09\x09playgroundHandlers.onClose.push(onClose);\x0a\x09\x09\x09playgroundHandlers.onKill.push(onKill);\x0a\x09\x09}\x0a\x0a\x09\x09var\x20run1\x20=\x20document.createElement('button');\x0a\x09\x09run1.innerHTML\x20=\x20'Run';\x0a\x09\x09run1.className\x20=\x20'run';\x0a\x09\x09run1.addEventListener(\"click\",\x20onRun,\x20false);\x0a\x09\x09var\x20run2\x20=\x20document.createElement('button');\x0a\x09\x09run2.className\x20=\x20'run';\x0a\x09\x09run2.innerHTML\x20=\x20'Run';\x0a\x09\x09run2.addEventListener(\"click\",\x20onRun,\x20false);\x0a\x09\x09var\x20kill\x20=\x20document.createElement('button');\x0a\x09\x09kill.className\x20=\x20'kill';\x0a\x09\x09kill.innerHTML\x20=\x20'Kill';\x0a\x09\x09kill.addEventListener(\"click\",\x20onKill,\x20false);\x0a\x09\x09var\x20close\x20=\x20document.createElement('button');\x0a\x09\x09close.className\x20=\x20'close';\x0a\x09\x09close.innerHTML\x20=\x20'Close';\x0a\x09\x09close.addEventListener(\"click\",\x20onClose,\x20false);\x0a\x0a\x09\x09var\x20button\x20=\x20document.createElement('div');\x0a\x09\x09button.classList.add('buttons');\x0a\x09\x09button.appendChild(run1);\x0a\x09\x09//\x20Hack\x20to\x20simulate\x20insertAfter\x0a\x09\x09code.parentNode.insertBefore(button,\x20code.nextSibling);\x0a\x0a\x09\x09var\x20buttons\x20=\x20document.createElement('div');\x0a\x09\x09buttons.classList.add('buttons');\x0a\x09\x09buttons.appendChild(run2);\x0a\x09\x09buttons.appendChild(kill);\x0a\x09\x09buttons.appendChild(close);\x0a\x0a\x09\x09output.classList.add('output');\x0a\x09\x09output.appendChild(buttons);\x0a\x09\x09output.appendChild(outpre);\x0a\x09\x09output.style.display\x20=\x20'none';\x0a\x09\x09code.parentNode.insertBefore(output,\x20button.nextSibling);\x0a\x09}\x0a\x0a\x09var\x20play\x20=\x20document.querySelectorAll('div.playground');\x0a\x09for\x20(var\x20i\x20=\x200;\x20i\x20<\x20play.length;\x20i++)\x20{\x0a\x09\x09init(play[i],\x20i);\x0a\x09}\x0a}\x0a",
"playground.js": "//\x20Copyright\x202012\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a//\x20Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a//\x20license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a\x0a/*\x0aIn\x20the\x20absence\x20of\x20any\x20formal\x20way\x20to\x20specify\x20interfaces\x20in\x20JavaScript,\x0ahere's\x20a\x20skeleton\x20implementation\x20of\x20a\x20playground\x20transport.\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20function\x20Transport()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Set\x20up\x20any\x20transport\x20state\x20(eg,\x20make\x20a\x20websocket\x20connection).\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Compile\x20and\x20run\x20the\x20program\x20'body'\x20with\x20'options'.\x0a\x09\x09\x09\x09//\x20Call\x20the\x20'output'\x20callback\x20to\x20display\x20program\x20output.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kill:\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20//\x20Kill\x20the\x20running\x20program.\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20};\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x0a\x09//\x20The\x20output\x20callback\x20is\x20called\x20multiple\x20times,\x20and\x20each\x20time\x20it\x20is\x0a\x09//\x20passed\x20an\x20object\x20of\x20this\x20form.\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20write\x20=\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Kind:\x20'string',\x20//\x20'start',\x20'stdout',\x20'stderr',\x20'end'\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Body:\x20'string'\x20\x20//\x20content\x20of\x20write\x20or\x20end\x20status\x20message\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x0a\x09//\x20The\x20first\x20call\x20must\x20be\x20of\x20Kind\x20'start'\x20with\x20no\x20body.\x0a\x09//\x20Subsequent\x20calls\x20may\x20be\x20of\x20Kind\x20'stdout'\x20or\x20'stderr'\x0a\x09//\x20and\x20must\x20have\x20a\x20non-null\x20Body\x20string.\x0a\x09//\x20The\x20final\x20call\x20should\x20be\x20of\x20Kind\x20'end'\x20with\x20an\x20optional\x0a\x09//\x20Body\x20string,\x20signifying\x20a\x20failure\x20(\"killed\",\x20for\x20example).\x0a\x0a\x09//\x20The\x20output\x20callback\x20must\x20be\x20of\x20this\x20form.\x0a\x09//\x20See\x20PlaygroundOutput\x20(below)\x20for\x20an\x20implementation.\x0a\x20\x20\x20\x20\x20\x20\x20\x20function\x20outputCallback(write)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a*/\x0a\x0a//\x20HTTPTransport\x20is\x20the\x20default\x20transport.\x0a//\x20enableVet\x20enables\x20running\x20vet\x20if\x20a\x20program\x20was\x20compiled\x20and\x20ran\x20successfully.\x0a//\x20If\x20vet\x20returned\x20any\x20errors,\x20display\x20them\x20before\x20the\x20output\x20of\x20a\x20program.\x0afunction\x20HTTPTransport(enableVet)\x20{\x0a\x09'use\x20strict';\x0a\x0a\x09function\x20playback(output,\x20events)\x20{\x0a\x09\x09var\x20timeout;\x0a\x09\x09output({Kind:\x20'start'});\x0a\x09\x09function\x20next()\x20{\x0a\x09\x09\x09if\x20(!events\x20||\x20events.length\x20===\x200)\x20{\x0a\x09\x09\x09\x09output({Kind:\x20'end'});\x0a\x09\x09\x09\x09return;\x0a\x09\x09\x09}\x0a\x09\x09\x09var\x20e\x20=\x20events.shift();\x0a\x09\x09\x09if\x20(e.Delay\x20===\x200)\x20{\x0a\x09\x09\x09\x09output({Kind:\x20e.Kind,\x20Body:\x20e.Message});\x0a\x09\x09\x09\x09next();\x0a\x09\x09\x09\x09return;\x0a\x09\x09\x09}\x0a\x09\x09\x09timeout\x20=\x20setTimeout(function()\x20{\x0a\x09\x09\x09\x09output({Kind:\x20e.Kind,\x20Body:\x20e.Message});\x0a\x09\x09\x09\x09next();\x0a\x09\x09\x09},\x20e.Delay\x20/\x201000000);\x0a\x09\x09}\x0a\x09\x09next();\x0a\x09\x09return\x20{\x0a\x09\x09\x09Stop:\x20function()\x20{\x0a\x09\x09\x09\x09clearTimeout(timeout);\x0a\x09\x09\x09}\x0a\x09\x09};\x0a\x09}\x0a\x0a\x09function\x20error(output,\x20msg)\x20{\x0a\x09\x09output({Kind:\x20'start'});\x0a\x09\x09output({Kind:\x20'stderr',\x20Body:\x20msg});\x0a\x09\x09output({Kind:\x20'end'});\x0a\x09}\x0a\x0a\x09var\x20seq\x20=\x200;\x0a\x09return\x20{\x0a\x09\x09Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x09\x09\x09seq++;\x0a\x09\x09\x09var\x20cur\x20=\x20seq;\x0a\x09\x09\x09var\x20playing;\x0a\x09\x09\x09$.ajax('/compile',\x20{\x0a\x09\x09\x09\x09type:\x20'POST',\x0a\x09\x09\x09\x09data:\x20{'version':\x202,\x20'body':\x20body},\x0a\x09\x09\x09\x09dataType:\x20'json',\x0a\x09\x09\x09\x09success:\x20function(data)\x20{\x0a\x09\x09\x09\x09\x09if\x20(seq\x20!=\x20cur)\x20return;\x0a\x09\x09\x09\x09\x09if\x20(!data)\x20return;\x0a\x09\x09\x09\x09\x09if\x20(playing\x20!=\x20null)\x20playing.Stop();\x0a\x09\x09\x09\x09\x09if\x20(data.Errors)\x20{\x0a\x09\x09\x09\x09\x09\x09error(output,\x20data.Errors);\x0a\x09\x09\x09\x09\x09\x09return;\x0a\x09\x09\x09\x09\x09}\x0a\x0a\x09\x09\x09\x09\x09if\x20(!enableVet)\x20{\x0a\x09\x09\x09\x09\x09\x09playing\x20=\x20playback(output,\x20data.Events);\x0a\x09\x09\x09\x09\x09\x09return;\x0a\x09\x09\x09\x09\x09}\x0a\x0a\x09\x09\x09\x09\x09$.ajax(\"/vet\",\x20{\x0a\x09\x09\x09\x09\x09\x09data:\x20{\"body\":\x20body},\x0a\x09\x09\x09\x09\x09\x09type:\x20\"POST\",\x0a\x09\x09\x09\x09\x09\x09dataType:\x20\"json\",\x0a\x09\x09\x09\x09\x09\x09success:\x20function(dataVet)\x20{\x0a\x09\x09\x09\x09\x09\x09\x09if\x20(dataVet.Errors)\x20{\x0a\x09\x09\x09\x09\x09\x09\x09\x09if\x20(!data.Events)\x20{\x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09data.Events\x20=\x20[];\x0a\x09\x09\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09\x09\x09\x09//\x20inject\x20errors\x20from\x20the\x20vet\x20as\x20the\x20first\x20events\x20in\x20the\x20output\x0a\x09\x09\x09\x09\x09\x09\x09\x09data.Events.unshift({Message:\x20'Go\x20vet\x20exited.\\n\\n',\x20Kind:\x20'system',\x20Delay:\x200});\x0a\x09\x09\x09\x09\x09\x09\x09\x09data.Events.unshift({Message:\x20dataVet.Errors,\x20Kind:\x20'stderr',\x20Delay:\x200});\x0a\x09\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09\x09\x09playing\x20=\x20playback(output,\x20data.Events);\x0a\x09\x09\x09\x09\x09\x09},\x0a\x09\x09\x09\x09\x09\x09error:\x20function()\x20{\x0a\x09\x09\x09\x09\x09\x09\x09playing\x20=\x20playback(output,\x20data.Events);\x0a\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09});\x0a\x09\x09\x09\x09},\x0a\x09\x09\x09\x09error:\x20function()\x20{\x0a\x09\x09\x09\x09\x09error(output,\x20'Error\x20communicating\x20with\x20remote\x20server.');\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09});\x0a\x09\x09\x09return\x20{\x0a\x09\x09\x09\x09Kill:\x20function()\x20{\x0a\x09\x09\x09\x09\x09if\x20(playing\x20!=\x20null)\x20playing.Stop();\x0a\x09\x09\x09\x09\x09output({Kind:\x20'end',\x20Body:\x20'killed'});\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09};\x0a\x09\x09}\x0a\x09};\x0a}\x0a\x0afunction\x20SocketTransport()\x20{\x0a\x09'use\x20strict';\x0a\x0a\x09var\x20id\x20=\x200;\x0a\x09var\x20outputs\x20=\x20{};\x0a\x09var\x20started\x20=\x20{};\x0a\x09var\x20websocket;\x0a\x09if\x20(window.location.protocol\x20==\x20\"http:\")\x20{\x0a\x09\x09websocket\x20=\x20new\x20WebSocket('ws://'\x20+\x20window.location.host\x20+\x20'/socket');\x0a\x09}\x20else\x20if\x20(window.location.protocol\x20==\x20\"https:\")\x20{\x0a\x09\x09websocket\x20=\x20new\x20WebSocket('wss://'\x20+\x20window.location.host\x20+\x20'/socket');\x0a\x09}\x0a\x0a\x09websocket.onclose\x20=\x20function()\x20{\x0a\x09\x09console.log('websocket\x20connection\x20closed');\x0a\x09};\x0a\x0a\x09websocket.onmessage\x20=\x20function(e)\x20{\x0a\x09\x09var\x20m\x20=\x20JSON.parse(e.data);\x0a\x09\x09var\x20output\x20=\x20outputs[m.Id];\x0a\x09\x09if\x20(output\x20===\x20null)\x0a\x09\x09\x09return;\x0a\x09\x09if\x20(!started[m.Id])\x20{\x0a\x09\x09\x09output({Kind:\x20'start'});\x0a\x09\x09\x09started[m.Id]\x20=\x20true;\x0a\x09\x09}\x0a\x09\x09output({Kind:\x20m.Kind,\x20Body:\x20m.Body});\x0a\x09};\x0a\x0a\x09function\x20send(m)\x20{\x0a\x09\x09websocket.send(JSON.stringify(m));\x0a\x09}\x0a\x0a\x09return\x20{\x0a\x09\x09Run:\x20function(body,\x20output,\x20options)\x20{\x0a\x09\x09\x09var\x20thisID\x20=\x20id+'';\x0a\x09\x09\x09id++;\x0a\x09\x09\x09outputs[thisID]\x20=\x20output;\x0a\x09\x09\x09send({Id:\x20thisID,\x20Kind:\x20'run',\x20Body:\x20body,\x20Options:\x20options});\x0a\x09\x09\x09return\x20{\x0a\x09\x09\x09\x09Kill:\x20function()\x20{\x0a\x09\x09\x09\x09\x09send({Id:\x20thisID,\x20Kind:\x20'kill'});\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09};\x0a\x09\x09}\x0a\x09};\x0a}\x0a\x0afunction\x20PlaygroundOutput(el)\x20{\x0a\x09'use\x20strict';\x0a\x0a\x09return\x20function(write)\x20{\x0a\x09\x09if\x20(write.Kind\x20==\x20'start')\x20{\x0a\x09\x09\x09el.innerHTML\x20=\x20'';\x0a\x09\x09\x09return;\x0a\x09\x09}\x0a\x0a\x09\x09var\x20cl\x20=\x20'system';\x0a\x09\x09if\x20(write.Kind\x20==\x20'stdout'\x20||\x20write.Kind\x20==\x20'stderr')\x0a\x09\x09\x09cl\x20=\x20write.Kind;\x0a\x0a\x09\x09var\x20m\x20=\x20write.Body;\x0a\x09\x09if\x20(write.Kind\x20==\x20'end')\x20{\x0a\x09\x09\x09m\x20=\x20'\\nProgram\x20exited'\x20+\x20(m?(':\x20'+m):'.');\x0a\x09\x09}\x0a\x0a\x09\x09if\x20(m.indexOf('IMAGE:')\x20===\x200)\x20{\x0a\x09\x09\x09//\x20TODO(adg):\x20buffer\x20all\x20writes\x20before\x20creating\x20image\x0a\x09\x09\x09var\x20url\x20=\x20'data:image/png;base64,'\x20+\x20m.substr(6);\x0a\x09\x09\x09var\x20img\x20=\x20document.createElement('img');\x0a\x09\x09\x09img.src\x20=\x20url;\x0a\x09\x09\x09el.appendChild(img);\x0a\x09\x09\x09return;\x0a\x09\x09}\x0a\x0a\x09\x09//\x20^L\x20clears\x20the\x20screen.\x0a\x09\x09var\x20s\x20=\x20m.split('\\x0c');\x0a\x09\x09if\x20(s.length\x20>\x201)\x20{\x0a\x09\x09\x09el.innerHTML\x20=\x20'';\x0a\x09\x09\x09m\x20=\x20s.pop();\x0a\x09\x09}\x0a\x0a\x09\x09m\x20=\x20m.replace(/&/g,\x20'&');\x0a\x09\x09m\x20=\x20m.replace(/</g,\x20'<');\x0a\x09\x09m\x20=\x20m.replace(/>/g,\x20'>');\x0a\x0a\x09\x09var\x20needScroll\x20=\x20(el.scrollTop\x20+\x20el.offsetHeight)\x20==\x20el.scrollHeight;\x0a\x0a\x09\x09var\x20span\x20=\x20document.createElement('span');\x0a\x09\x09span.className\x20=\x20cl;\x0a\x09\x09span.innerHTML\x20=\x20m;\x0a\x09\x09el.appendChild(span);\x0a\x0a\x09\x09if\x20(needScroll)\x0a\x09\x09\x09el.scrollTop\x20=\x20el.scrollHeight\x20-\x20el.offsetHeight;\x0a\x09};\x0a}\x0a\x0a(function()\x20{\x0a\x20\x20function\x20lineHighlight(error)\x20{\x0a\x20\x20\x20\x20var\x20regex\x20=\x20/prog.go:([0-9]+)/g;\x0a\x20\x20\x20\x20var\x20r\x20=\x20regex.exec(error);\x0a\x20\x20\x20\x20while\x20(r)\x20{\x0a\x20\x20\x20\x20\x20\x20$(\".lines\x20div\").eq(r[1]-1).addClass(\"lineerror\");\x0a\x20\x20\x20\x20\x20\x20r\x20=\x20regex.exec(error);\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x20\x20function\x20highlightOutput(wrappedOutput)\x20{\x0a\x20\x20\x20\x20return\x20function(write)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(write.Body)\x20lineHighlight(write.Body);\x0a\x20\x20\x20\x20\x20\x20wrappedOutput(write);\x0a\x20\x20\x20\x20};\x0a\x20\x20}\x0a\x20\x20function\x20lineClear()\x20{\x0a\x20\x20\x20\x20$(\".lineerror\").removeClass(\"lineerror\");\x0a\x20\x20}\x0a\x0a\x20\x20//\x20opts\x20is\x20an\x20object\x20with\x20these\x20keys\x0a\x20\x20//\x20\x20codeEl\x20-\x20code\x20editor\x20element\x0a\x20\x20//\x20\x20outputEl\x20-\x20program\x20output\x20element\x0a\x20\x20//\x20\x20runEl\x20-\x20run\x20button\x20element\x0a\x20\x20//\x20\x20fmtEl\x20-\x20fmt\x20button\x20element\x20(optional)\x0a\x20\x20//\x20\x20fmtImportEl\x20-\x20fmt\x20\"imports\"\x20checkbox\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareEl\x20-\x20share\x20button\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareURLEl\x20-\x20share\x20URL\x20text\x20input\x20element\x20(optional)\x0a\x20\x20//\x20\x20shareRedirect\x20-\x20base\x20URL\x20to\x20redirect\x20to\x20on\x20share\x20(optional)\x0a\x20\x20//\x20\x20toysEl\x20-\x20toys\x20select\x20element\x20(optional)\x0a\x20\x20//\x20\x20enableHistory\x20-\x20enable\x20using\x20HTML5\x20history\x20API\x20(optional)\x0a\x20\x20//\x20\x20transport\x20-\x20playground\x20transport\x20to\x20use\x20(default\x20is\x20HTTPTransport)\x0a\x20\x20//\x20\x20enableShortcuts\x20-\x20whether\x20to\x20enable\x20shortcuts\x20(Ctrl+S/Cmd+S\x20to\x20save)\x20(default\x20is\x20false)\x0a\x20\x20//\x20\x20enableVet\x20-\x20enable\x20running\x20vet\x20and\x20displaying\x20its\x20errors\x0a\x20\x20function\x20playground(opts)\x20{\x0a\x20\x20\x20\x20var\x20code\x20=\x20$(opts.codeEl);\x0a\x20\x20\x20\x20var\x20transport\x20=\x20opts['transport']\x20||\x20new\x20HTTPTransport(opts['enableVet']);\x0a\x20\x20\x20\x20var\x20running;\x0a\x0a\x20\x20\x20\x20//\x20autoindent\x20helpers.\x0a\x20\x20\x20\x20function\x20insertTabs(n)\x20{\x0a\x20\x20\x20\x20\x20\x20//\x20find\x20the\x20selection\x20start\x20and\x20end\x0a\x20\x20\x20\x20\x20\x20var\x20start\x20=\x20code[0].selectionStart;\x0a\x20\x20\x20\x20\x20\x20var\x20end\x20\x20\x20=\x20code[0].selectionEnd;\x0a\x20\x20\x20\x20\x20\x20//\x20split\x20the\x20textarea\x20content\x20into\x20two,\x20and\x20insert\x20n\x20tabs\x0a\x20\x20\x20\x20\x20\x20var\x20v\x20=\x20code[0].value;\x0a\x20\x20\x20\x20\x20\x20var\x20u\x20=\x20v.substr(0,\x20start);\x0a\x20\x20\x20\x20\x20\x20for\x20(var\x20i=0;\x20i<n;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20u\x20+=\x20\"\\t\";\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20u\x20+=\x20v.substr(end);\x0a\x20\x20\x20\x20\x20\x20//\x20set\x20revised\x20content\x0a\x20\x20\x20\x20\x20\x20code[0].value\x20=\x20u;\x0a\x20\x20\x20\x20\x20\x20//\x20reset\x20caret\x20position\x20after\x20inserted\x20tabs\x0a\x20\x20\x20\x20\x20\x20code[0].selectionStart\x20=\x20start+n;\x0a\x20\x20\x20\x20\x20\x20code[0].selectionEnd\x20=\x20start+n;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20autoindent(el)\x20{\x0a\x20\x20\x20\x20\x20\x20var\x20curpos\x20=\x20el.selectionStart;\x0a\x20\x20\x20\x20\x20\x20var\x20tabs\x20=\x200;\x0a\x20\x20\x20\x20\x20\x20while\x20(curpos\x20>\x200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20curpos--;\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(el.value[curpos]\x20==\x20\"\\t\")\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20tabs++;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20if\x20(tabs\x20>\x200\x20||\x20el.value[curpos]\x20==\x20\"\\n\")\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20break;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20setTimeout(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20insertTabs(tabs);\x0a\x20\x20\x20\x20\x20\x20},\x201);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20//\x20NOTE(cbro):\x20e\x20is\x20a\x20jQuery\x20event,\x20not\x20a\x20DOM\x20event.\x0a\x20\x20\x20\x20function\x20handleSaveShortcut(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(e.isDefaultPrevented())\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20if\x20(!e.metaKey\x20&&\x20!e.ctrlKey)\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20if\x20(e.key\x20!=\x20\"S\"\x20&&\x20e.key\x20!=\x20\"s\")\x20return\x20false;\x0a\x0a\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x0a\x20\x20\x20\x20\x20\x20//\x20Share\x20and\x20save\x0a\x20\x20\x20\x20\x20\x20share(function(url)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20window.location.href\x20=\x20url\x20+\x20\".go?download=true\";\x0a\x20\x20\x20\x20\x20\x20});\x0a\x0a\x20\x20\x20\x20\x20\x20return\x20true;\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20keyHandler(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opts.enableShortcuts\x20&&\x20handleSaveShortcut(e))\x20return;\x0a\x0a\x20\x20\x20\x20\x20\x20if\x20(e.keyCode\x20==\x209\x20&&\x20!e.ctrlKey)\x20{\x20//\x20tab\x20(but\x20not\x20ctrl-tab)\x0a\x20\x20\x20\x20\x20\x20\x20\x20insertTabs(1);\x0a\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(e.keyCode\x20==\x2013)\x20{\x20//\x20enter\x0a\x20\x20\x20\x20\x20\x20\x20\x20if\x20(e.shiftKey)\x20{\x20//\x20+shift\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20run();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20if\x20(e.ctrlKey)\x20{\x20//\x20+control\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20fmt();\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20e.preventDefault();\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20autoindent(e.target);\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20return\x20true;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20code.unbind('keydown').bind('keydown',\x20keyHandler);\x0a\x20\x20\x20\x20var\x20outdiv\x20=\x20$(opts.outputEl).empty();\x0a\x20\x20\x20\x20var\x20output\x20=\x20$('<pre/>').appendTo(outdiv);\x0a\x0a\x20\x20\x20\x20function\x20body()\x20{\x0a\x20\x20\x20\x20\x20\x20return\x20$(opts.codeEl).val();\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20setBody(text)\x20{\x0a\x20\x20\x20\x20\x20\x20$(opts.codeEl).val(text);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20origin(href)\x20{\x0a\x20\x20\x20\x20\x20\x20return\x20(\"\"+href).split(\"/\").slice(0,\x203).join(\"/\");\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20pushedEmpty\x20=\x20(window.location.pathname\x20==\x20\"/\");\x0a\x20\x20\x20\x20function\x20inputChanged()\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(pushedEmpty)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20pushedEmpty\x20=\x20true;\x0a\x20\x20\x20\x20\x20\x20$(opts.shareURLEl).hide();\x0a\x20\x20\x20\x20\x20\x20window.history.pushState(null,\x20\"\",\x20\"/\");\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20popState(e)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(e\x20===\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20if\x20(e\x20&&\x20e.state\x20&&\x20e.state.code)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20setBody(e.state.code);\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20var\x20rewriteHistory\x20=\x20false;\x0a\x20\x20\x20\x20if\x20(window.history\x20&&\x20window.history.pushState\x20&&\x20window.addEventListener\x20&&\x20opts.enableHistory)\x20{\x0a\x20\x20\x20\x20\x20\x20rewriteHistory\x20=\x20true;\x0a\x20\x20\x20\x20\x20\x20code[0].addEventListener('input',\x20inputChanged);\x0a\x20\x20\x20\x20\x20\x20window.addEventListener('popstate',\x20popState);\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20setError(error)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20lineClear();\x0a\x20\x20\x20\x20\x20\x20lineHighlight(error);\x0a\x20\x20\x20\x20\x20\x20output.empty().addClass(\"error\").text(error);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20loading()\x20{\x0a\x20\x20\x20\x20\x20\x20lineClear();\x0a\x20\x20\x20\x20\x20\x20if\x20(running)\x20running.Kill();\x0a\x20\x20\x20\x20\x20\x20output.removeClass(\"error\").text('Waiting\x20for\x20remote\x20server...');\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20function\x20run()\x20{\x0a\x20\x20\x20\x20\x20\x20loading();\x0a\x20\x20\x20\x20\x20\x20running\x20=\x20transport.Run(body(),\x20highlightOutput(PlaygroundOutput(output[0])));\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20function\x20fmt()\x20{\x0a\x20\x20\x20\x20\x20\x20loading();\x0a\x20\x20\x20\x20\x20\x20var\x20data\x20=\x20{\"body\":\x20body()};\x0a\x20\x20\x20\x20\x20\x20if\x20($(opts.fmtImportEl).is(\":checked\"))\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20data[\"imports\"]\x20=\x20\"true\";\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20$.ajax(\"/fmt\",\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20data:\x20data,\x0a\x20\x20\x20\x20\x20\x20\x20\x20type:\x20\"POST\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20dataType:\x20\"json\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20success:\x20function(data)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(data.Error)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setError(data.Error);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x20else\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setBody(data.Body);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setError(\"\");\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20var\x20shareURL;\x20//\x20jQuery\x20element\x20to\x20show\x20the\x20shared\x20URL.\x0a\x20\x20\x20\x20var\x20sharing\x20=\x20false;\x20//\x20true\x20if\x20there\x20is\x20a\x20pending\x20request.\x0a\x20\x20\x20\x20var\x20shareCallbacks\x20=\x20[];\x0a\x20\x20\x20\x20function\x20share(opt_callback)\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opt_callback)\x20shareCallbacks.push(opt_callback);\x0a\x0a\x20\x20\x20\x20\x20\x20if\x20(sharing)\x20return;\x0a\x20\x20\x20\x20\x20\x20sharing\x20=\x20true;\x0a\x0a\x20\x20\x20\x20\x20\x20var\x20sharingData\x20=\x20body();\x0a\x20\x20\x20\x20\x20\x20$.ajax(\"/share\",\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20processData:\x20false,\x0a\x20\x20\x20\x20\x20\x20\x20\x20data:\x20sharingData,\x0a\x20\x20\x20\x20\x20\x20\x20\x20type:\x20\"POST\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20contentType:\x20\"text/plain;\x20charset=utf-8\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20complete:\x20function(xhr)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20sharing\x20=\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(xhr.status\x20!=\x20200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20alert(\"Server\x20error;\x20try\x20again.\");\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(opts.shareRedirect)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20window.location\x20=\x20opts.shareRedirect\x20+\x20xhr.responseText;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20path\x20=\x20\"/p/\"\x20+\x20xhr.responseText;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20url\x20=\x20origin(window.location)\x20+\x20path;\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20for\x20(var\x20i\x20=\x200;\x20i\x20<\x20shareCallbacks.length;\x20i++)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareCallbacks[i](url);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareCallbacks\x20=\x20[];\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(shareURL)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20shareURL.show().val(url).focus().select();\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(rewriteHistory)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20historyData\x20=\x20{\"code\":\x20sharingData};\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20window.history.pushState(historyData,\x20\"\",\x20path);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20pushedEmpty\x20=\x20false;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20$(opts.runEl).click(run);\x0a\x20\x20\x20\x20$(opts.fmtEl).click(fmt);\x0a\x0a\x20\x20\x20\x20if\x20(opts.shareEl\x20!==\x20null\x20&&\x20(opts.shareURLEl\x20!==\x20null\x20||\x20opts.shareRedirect\x20!==\x20null))\x20{\x0a\x20\x20\x20\x20\x20\x20if\x20(opts.shareURLEl)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20shareURL\x20=\x20$(opts.shareURLEl).hide();\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20$(opts.shareEl).click(function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20share();\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x0a\x20\x20\x20\x20if\x20(opts.toysEl\x20!==\x20null)\x20{\x0a\x20\x20\x20\x20\x20\x20$(opts.toysEl).bind('change',\x20function()\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20var\x20toy\x20=\x20$(this).val();\x0a\x20\x20\x20\x20\x20\x20\x20\x20$.ajax(\"/doc/play/\"+toy,\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20processData:\x20false,\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20type:\x20\"GET\",\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20complete:\x20function(xhr)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20if\x20(xhr.status\x20!=\x20200)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20alert(\"Server\x20error;\x20try\x20again.\");\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20return;\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20setBody(xhr.responseText);\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20\x20\x20});\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x0a\x0a\x20\x20window.playground\x20=\x20playground;\x0a})();\x0a",
"search.html": "<!--\x0a\x09Copyright\x202009\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a\x09Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a\x09license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a-->\x0a{{with\x20.Alert}}\x0a\x09<p>\x0a\x09<span\x20class=\"alert\"\x20style=\"font-size:120%\">{{html\x20.}}</span>\x0a\x09</p>\x0a{{end}}\x0a{{with\x20.Alt}}\x0a\x09<p>\x0a\x09<span\x20class=\"alert\"\x20style=\"font-size:120%\">Did\x20you\x20mean:\x20</span>\x0a\x09{{range\x20.Alts}}\x0a\x09\x09<a\x20href=\"search?q={{urlquery\x20.}}\"\x20style=\"font-size:120%\">{{html\x20.}}</a>\x0a\x09{{end}}\x0a\x09</p>\x0a{{end}}\x0a",
- "search.txt": "QUERY\x0a\x09{{.Query}}\x0a\x0a{{with\x20.Alert}}{{.}}\x0a{{end}}{{/*\x20.Alert\x20*/}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20.Alt}}DID\x20YOU\x20MEAN\x0a\x0a{{range\x20.Alts}}\x09{{.}}\x0a{{end}}\x0a{{end}}{{/*\x20.Alt\x20*/}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20.Pak}}PACKAGE\x20{{$.Query}}\x0a\x0a{{range\x20.}}\x09{{pkgLink\x20.Pak.Path}}\x0a{{end}}\x0a{{end}}{{/*\x20.Pak\x20*/}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{range\x20$key,\x20$val\x20:=\x20.Idents}}{{if\x20$val}}{{$key.Name}}\x0a{{range\x20$val}}\x20\x20\x20\x20{{.Path}}.{{.Name}}\x0a{{end}}\x0a{{end}}{{end}}{{/*\x20.Idents\x20*/}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20.Hit}}{{with\x20.Decls}}PACKAGE-LEVEL\x20DECLARATIONS\x0a\x0a{{range\x20.}}package\x20{{.Pak.Name}}\x0a{{range\x20$file\x20:=\x20.Files}}{{range\x20.Groups}}{{range\x20.}}\x09{{srcLink\x20$file.File.Path}}:{{infoLine\x20.}}{{end}}\x0a{{end}}{{end}}{{/*\x20.Files\x20*/}}\x0a{{end}}{{end}}{{/*\x20.Decls\x20*/}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{with\x20.Others}}LOCAL\x20DECLARATIONS\x20AND\x20USES\x0a\x0a{{range\x20.}}package\x20{{.Pak.Name}}\x0a{{range\x20$file\x20:=\x20.Files}}{{range\x20.Groups}}{{range\x20.}}\x09{{srcLink\x20$file.File.Path}}:{{infoLine\x20.}}\x0a{{end}}{{end}}{{end}}{{/*\x20.Files\x20*/}}\x0a{{end}}{{end}}{{/*\x20.Others\x20*/}}{{end}}{{/*\x20.Hit\x20*/}}{{/*\x0a\x0a---------------------------------------\x0a\x0a*/}}{{if\x20.Textual}}{{if\x20.Complete}}{{.Found}}\x20TEXTUAL\x20OCCURRENCES{{else}}MORE\x20THAN\x20{{.Found}}\x20TEXTUAL\x20OCCURRENCES{{end}}\x0a\x0a{{range\x20.Textual}}{{len\x20.Lines}}\x09{{srcLink\x20.Filename}}\x0a{{end}}{{if\x20not\x20.Complete}}...\x09...\x0a{{end}}{{end}}\x0a",
-
"searchcode.html": "<!--\x0a\x09Copyright\x202009\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a\x09Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a\x09license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a-->\x0a{{$query_url\x20:=\x20urlquery\x20.Query}}\x0a{{if\x20not\x20.Idents}}\x0a\x09{{with\x20.Pak}}\x0a\x09\x09<h2\x20id=\"Packages\">Package\x20{{html\x20$.Query}}</h2>\x0a\x09\x09<p>\x0a\x09\x09<table\x20class=\"layout\">\x0a\x09\x09{{range\x20.}}\x0a\x09\x09\x09{{$pkg_html\x20:=\x20pkgLink\x20.Pak.Path\x20|\x20html}}\x0a\x09\x09\x09<tr><td><a\x20href=\"/{{$pkg_html}}\">{{$pkg_html}}</a></td></tr>\x0a\x09\x09{{end}}\x0a\x09\x09</table>\x0a\x09\x09</p>\x0a\x09{{end}}\x0a{{end}}\x0a{{with\x20.Hit}}\x0a\x09{{with\x20.Decls}}\x0a\x09\x09<h2\x20id=\"Global\">Package-level\x20declarations</h2>\x0a\x09\x09{{range\x20.}}\x0a\x09\x09\x09{{$pkg_html\x20:=\x20pkgLink\x20.Pak.Path\x20|\x20html}}\x0a\x09\x09\x09<h3\x20id=\"Global_{{$pkg_html}}\">package\x20<a\x20href=\"/{{$pkg_html}}\">{{html\x20.Pak.Name}}</a></h3>\x0a\x09\x09\x09{{range\x20.Files}}\x0a\x09\x09\x09\x09{{$file\x20:=\x20.File.Path}}\x0a\x09\x09\x09\x09{{range\x20.Groups}}\x0a\x09\x09\x09\x09\x09{{range\x20.}}\x0a\x09\x09\x09\x09\x09\x09{{$line\x20:=\x20infoLine\x20.}}\x0a\x09\x09\x09\x09\x09\x09<a\x20href=\"{{queryLink\x20$file\x20$query_url\x20$line\x20|\x20html}}\">{{$file}}:{{$line}}</a>\x0a\x09\x09\x09\x09\x09\x09{{infoSnippet_html\x20.}}\x0a\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09{{end}}\x0a\x09\x09{{end}}\x0a\x09{{end}}\x0a\x09{{with\x20.Others}}\x0a\x09\x09<h2\x20id=\"Local\">Local\x20declarations\x20and\x20uses</h2>\x0a\x09\x09{{range\x20.}}\x0a\x09\x09\x09{{$pkg_html\x20:=\x20pkgLink\x20.Pak.Path\x20|\x20html}}\x0a\x09\x09\x09<h3\x20id=\"Local_{{$pkg_html}}\">package\x20<a\x20href=\"/{{$pkg_html}}\">{{html\x20.Pak.Name}}</a></h3>\x0a\x09\x09\x09{{range\x20.Files}}\x0a\x09\x09\x09\x09{{$file\x20:=\x20.File.Path}}\x0a\x09\x09\x09\x09<a\x20href=\"{{queryLink\x20$file\x20$query_url\x200\x20|\x20html}}\">{{$file}}</a>\x0a\x09\x09\x09\x09<table\x20class=\"layout\">\x0a\x09\x09\x09\x09{{range\x20.Groups}}\x0a\x09\x09\x09\x09\x09<tr>\x0a\x09\x09\x09\x09\x09<td\x20width=\"25\"></td>\x0a\x09\x09\x09\x09\x09<th\x20align=\"left\"\x20valign=\"top\">{{index\x20.\x200\x20|\x20infoKind_html}}</th>\x0a\x09\x09\x09\x09\x09<td\x20align=\"left\"\x20width=\"4\"></td>\x0a\x09\x09\x09\x09\x09<td>\x0a\x09\x09\x09\x09\x09{{range\x20.}}\x0a\x09\x09\x09\x09\x09\x09{{$line\x20:=\x20infoLine\x20.}}\x0a\x09\x09\x09\x09\x09\x09<a\x20href=\"{{queryLink\x20$file\x20$query_url\x20$line\x20|\x20html}}\">{{$line}}</a>\x0a\x09\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09\x09</td>\x0a\x09\x09\x09\x09\x09</tr>\x0a\x09\x09\x09\x09{{end}}\x0a\x09\x09\x09\x09</table>\x0a\x09\x09\x09{{end}}\x0a\x09\x09{{end}}\x0a\x09{{end}}\x0a{{end}}\x0a",
"searchdoc.html": "<!--\x0a\x09Copyright\x202009\x20The\x20Go\x20Authors.\x20All\x20rights\x20reserved.\x0a\x09Use\x20of\x20this\x20source\x20code\x20is\x20governed\x20by\x20a\x20BSD-style\x0a\x09license\x20that\x20can\x20be\x20found\x20in\x20the\x20LICENSE\x20file.\x0a-->\x0a{{range\x20$key,\x20$val\x20:=\x20.Idents}}\x0a\x09{{if\x20$val}}\x0a\x09\x09<h2\x20id=\"{{$key.Name}}\">{{$key.Name}}</h2>\x0a\x09\x09{{range\x20$val}}\x0a\x09\x09\x09{{$pkg_html\x20:=\x20pkgLink\x20.Path\x20|\x20html}}\x0a\x09\x09\x09{{if\x20eq\x20\"Packages\"\x20$key.Name}}\x0a\x09\x09\x09\x09<a\x20href=\"/{{$pkg_html}}\">{{html\x20.Path}}</a>\x0a\x09\x09\x09{{else}}\x0a\x09\x09\x09\x09{{$doc_html\x20:=\x20docLink\x20.Path\x20.Name|\x20html}}\x0a\x09\x09\x09\x09<a\x20href=\"/{{$pkg_html}}\">{{html\x20.Package}}</a>.<a\x20href=\"{{$doc_html}}\">{{.Name}}</a>\x0a\x09\x09\x09{{end}}\x0a\x09\x09\x09{{if\x20.Doc}}\x0a\x09\x09\x09\x09<p>{{comment_html\x20.Doc}}</p>\x0a\x09\x09\x09{{else}}\x0a\x09\x09\x09\x09<p><em>No\x20documentation\x20available</em></p>\x0a\x09\x09\x09{{end}}\x0a\x09\x09{{end}}\x0a\x09{{end}}\x0a{{end}}\x0a",