summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Gerrand <adg@golang.org>2013-08-01 11:21:17 +1000
committerAndrew Gerrand <adg@golang.org>2013-08-01 11:21:17 +1000
commit4fa83edf0693099becc97df926ee05eb83d08a47 (patch)
tree3f2df38eee9d5467e0cf9b6e13fc5a3fe3f313ec
parente011ac5420e1bda1feb87bc398a61ebbb52c0332 (diff)
downloadgo-git-4fa83edf0693099becc97df926ee05eb83d08a47.tar.gz
cmd/godoc: delete from core repository
The godoc command now lives at code.google.com/p/go.tools/cmd/godoc. R=golang-dev, r CC=golang-dev https://golang.org/cl/12206044
-rw-r--r--lib/godoc/codewalk.html56
-rw-r--r--lib/godoc/codewalkdir.html16
-rw-r--r--lib/godoc/dirlist.html31
-rw-r--r--lib/godoc/error.html9
-rw-r--r--lib/godoc/example.html28
-rw-r--r--lib/godoc/godoc.html94
-rw-r--r--lib/godoc/opensearch.xml11
-rw-r--r--lib/godoc/package.html226
-rw-r--r--lib/godoc/package.txt80
-rw-r--r--lib/godoc/search.html109
-rw-r--r--lib/godoc/search.txt47
-rw-r--r--src/cmd/godoc/README.godoc-app61
-rw-r--r--src/cmd/godoc/appinit.go69
-rw-r--r--src/cmd/godoc/codewalk.go494
-rw-r--r--src/cmd/godoc/dirtrees.go320
-rw-r--r--src/cmd/godoc/doc.go135
-rw-r--r--src/cmd/godoc/filesystem.go562
-rw-r--r--src/cmd/godoc/format.go372
-rw-r--r--src/cmd/godoc/godoc.go1586
-rw-r--r--src/cmd/godoc/index.go1079
-rw-r--r--src/cmd/godoc/linkify.go234
-rw-r--r--src/cmd/godoc/main.go470
-rw-r--r--src/cmd/godoc/parser.go37
-rw-r--r--src/cmd/godoc/play-appengine.go35
-rw-r--r--src/cmd/godoc/play-local.go41
-rw-r--r--src/cmd/godoc/play.go52
-rwxr-xr-xsrc/cmd/godoc/setup-godoc-app.bash140
-rw-r--r--src/cmd/godoc/snippet.go112
-rw-r--r--src/cmd/godoc/spec.go179
-rw-r--r--src/cmd/godoc/template.go182
-rw-r--r--src/cmd/godoc/throttle.go88
-rw-r--r--src/cmd/godoc/utils.go91
-rw-r--r--src/cmd/godoc/zip.go236
33 files changed, 0 insertions, 7282 deletions
diff --git a/lib/godoc/codewalk.html b/lib/godoc/codewalk.html
deleted file mode 100644
index 313f1f6631..0000000000
--- a/lib/godoc/codewalk.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!--
- 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}}&lo={{urlquery .Lo}}&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>
diff --git a/lib/godoc/codewalkdir.html b/lib/godoc/codewalkdir.html
deleted file mode 100644
index b7674c6ce9..0000000000
--- a/lib/godoc/codewalkdir.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
- 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>
diff --git a/lib/godoc/dirlist.html b/lib/godoc/dirlist.html
deleted file mode 100644
index a3e1a2fa88..0000000000
--- a/lib/godoc/dirlist.html
+++ /dev/null
@@ -1,31 +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.
--->
-
-<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>
diff --git a/lib/godoc/error.html b/lib/godoc/error.html
deleted file mode 100644
index 7573aa2367..0000000000
--- a/lib/godoc/error.html
+++ /dev/null
@@ -1,9 +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.
--->
-
-<p>
-<span class="alert" style="font-size:120%">{{html .}}</span>
-</p>
diff --git a/lib/godoc/example.html b/lib/godoc/example.html
deleted file mode 100644
index cda2a8491e..0000000000
--- a/lib/godoc/example.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<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">{{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>
- <a class="share" title="Share this code">Share</a>
- </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>
diff --git a/lib/godoc/godoc.html b/lib/godoc/godoc.html
deleted file mode 100644
index ccf5b6ed6a..0000000000
--- a/lib/godoc/godoc.html
+++ /dev/null
@@ -1,94 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-{{with .Tabtitle}}
- <title>{{html .}} - The Go Programming Language</title>
-{{else}}
- <title>The Go Programming Language</title>
-{{end}}
-<link type="text/css" rel="stylesheet" href="/doc/style.css">
-{{if .SearchBox}}
-<link rel="search" type="application/opensearchdescription+xml" title="godoc" href="/opensearch.xml" />
-{{end}}
-<script type="text/javascript">window.initFuncs = [];</script>
-</head>
-<body>
-
-<div id="topbar"{{if .Title}} class="wide"{{end}}><div class="container">
-
-<form method="GET" action="/search">
-<div id="menu">
-<a href="/doc/">Documents</a>
-<a href="/ref/">References</a>
-<a href="/pkg/">Packages</a>
-<a href="/project/">The Project</a>
-<a href="/help/">Help</a>
-{{if .Playground}}
-<a id="playgroundButton" href="http://play.golang.org/" title="Show Go Playground">Play</a>
-{{end}}
-<input type="text" id="search" name="q" class="inactive" value="Search" placeholder="Search">
-</div>
-<div id="heading"><a href="/">The Go Programming Language</a></div>
-</form>
-
-</div></div>
-
-{{if .Playground}}
-<div id="playground" class="play">
- <div class="input"><textarea class="code">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>
- <a class="share" title="Share this code">Share</a>
- </div>
-</div>
-{{end}}
-
-<div id="page"{{if .Title}} class="wide"{{end}}>
-<div class="container">
-
-{{with .Title}}
- <div id="plusone"><g:plusone size="small" annotation="none"></g:plusone></div>
- <h1>{{html .}}</h1>
-{{end}}
-{{with .Subtitle}}
- <h2>{{html .}}</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="http://code.google.com/policies.html#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 -->
-
-<script type="text/javascript" src="/doc/jquery.js"></script>
-{{if .Playground}}
-<script type="text/javascript" src="/doc/play/playground.js"></script>
-{{end}}
-<script type="text/javascript" src="/doc/godocs.js"></script>
-
-</body>
-</html>
-
diff --git a/lib/godoc/opensearch.xml b/lib/godoc/opensearch.xml
deleted file mode 100644
index 1b652db376..0000000000
--- a/lib/godoc/opensearch.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?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>
diff --git a/lib/godoc/package.html b/lib/godoc/package.html
deleted file mode 100644
index 8d28652fc3..0000000000
--- a/lib/godoc/package.html
+++ /dev/null
@@ -1,226 +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.
--->
-<!--
- 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}}
- {{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}}
- </div>
- </div>
- {{example_html $ ""}}
-
- <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}}</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}}</a></dd>
- {{end}}
- {{range .Methods}}
- {{$name_html := html .Name}}
- <dd>&nbsp; &nbsp; <a href="#{{$tname_html}}.{{$name_html}}">{{node_html $ .Decl false}}</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">
- <h4>Examples</h4>
- <dl>
- {{range $.Examples}}
- <dd><a class="exampleLink" href="#example_{{.Name}}">{{example_name .Name}}</a></dd>
- {{end}}
- </dl>
- </div>
- {{end}}
-
- {{with .Filenames}}
- <h4>Package files</h4>
- <p>
- <span style="font-size:90%">
- {{range .}}
- <a href="{{.|srcLink|html}}">{{.|filename|html}}</a>
- {{end}}
- </span>
- </p>
- {{end}}
- </div><!-- .expanded -->
- </div><!-- #pkg-index -->
-
- {{with .Consts}}
- <h2 id="pkg-constants">Constants</h2>
- {{range .}}
- <pre>{{node_html $ .Decl true}}</pre>
- {{comment_html .Doc}}
- {{end}}
- {{end}}
- {{with .Vars}}
- <h2 id="pkg-variables">Variables</h2>
- {{range .}}
- <pre>{{node_html $ .Decl true}}</pre>
- {{comment_html .Doc}}
- {{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></h2>
- <pre>{{node_html $ .Decl true}}</pre>
- {{comment_html .Doc}}
- {{example_html $ .Name}}
- {{end}}
- {{range .Types}}
- {{$tname := .Name}}
- {{$tname_html := html .Name}}
- <h2 id="{{$tname_html}}">type <a href="{{posLink_url $ .Decl}}">{{$tname_html}}</a></h2>
- <pre>{{node_html $ .Decl true}}</pre>
- {{comment_html .Doc}}
-
- {{range .Consts}}
- <pre>{{node_html $ .Decl true}}</pre>
- {{comment_html .Doc}}
- {{end}}
-
- {{range .Vars}}
- <pre>{{node_html $ .Decl true}}</pre>
- {{comment_html .Doc}}
- {{end}}
-
- {{example_html $ $tname}}
-
- {{range .Funcs}}
- {{$name_html := html .Name}}
- <h3 id="{{$name_html}}">func <a href="{{posLink_url $ .Decl}}">{{$name_html}}</a></h3>
- <pre>{{node_html $ .Decl true}}</pre>
- {{comment_html .Doc}}
- {{example_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></h3>
- <pre>{{node_html $ .Decl true}}</pre>
- {{comment_html .Doc}}
- {{$name := printf "%s_%s" $tname .Name}}
- {{example_html $ $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 $ .}}">&#x261e;</a> {{html .Body}}</li>
- {{end}}
- </ul>
- {{end}}
- {{end}}
-{{end}}
-
-{{with .PAst}}
- <pre>{{node_html $ . false}}</pre>
-{{end}}
-
-{{with .Dirs}}
- {{/* DirList entries are numbers and strings - no need for FSet */}}
- {{if $.PDoc}}
- <h2 id="pkg-subdirectories">Subdirectories</h2>
- {{else}}
- <div class="pkgGopher">
- <img class="gopher" src="/doc/gopher/pkg.png"/>
- </div>
- {{end}}
- <table class="dir">
- <tr>
- <th>Name</th>
- <th>&nbsp;&nbsp;&nbsp;&nbsp;</th>
- <th style="text-align: left; width: auto">Synopsis</th>
- </tr>
- {{if not $.DirFlat}}
- <tr>
- <td><a href="..">..</a></td>
- </tr>
- {{end}}
- {{range .List}}
- {{if $.DirFlat}}
- {{if .HasPkg}}
- <tr>
- <td class="name"><a href="{{html .Path}}/">{{html .Path}}</a></td>
- <td>&nbsp;&nbsp;&nbsp;&nbsp;</td>
- <td style="width: auto">{{html .Synopsis}}</td>
- </tr>
- {{end}}
- {{else}}
- <tr>
- <td class="name">{{repeat `&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` .Depth}}<a href="{{html .Path}}/">{{html .Name}}</a></td>
- <td>&nbsp;&nbsp;&nbsp;&nbsp;</td>
- <td style="width: auto">{{html .Synopsis}}</td>
- </tr>
- {{end}}
- {{end}}
- </table>
- {{if $.PDoc}}{{else}}
- <p>Need more packages? Take a look at the <a href="http://code.google.com/p/go-wiki/wiki/Projects">Go Projects wiki page</a>.</p>
- {{end}}
-{{end}}
diff --git a/lib/godoc/package.txt b/lib/godoc/package.txt
deleted file mode 100644
index d191621c00..0000000000
--- a/lib/godoc/package.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-{{with .PAst}}{{node $ .}}{{end}}{{/*
-
----------------------------------------
-
-*/}}{{with .PDoc}}{{if $.IsMain}}COMMAND DOCUMENTATION
-
-{{comment_text .Doc " " "\t"}}
-{{else}}PACKAGE DOCUMENTATION
-
-package {{.Name}}
- import "{{.ImportPath}}"
-
-{{comment_text .Doc " " "\t"}}
-{{example_text $ "" " "}}{{/*
-
----------------------------------------
-
-*/}}{{with .Consts}}
-CONSTANTS
-
-{{range .}}{{node $ .Decl}}
-{{comment_text .Doc " " "\t"}}
-{{end}}{{end}}{{/*
-
----------------------------------------
-
-*/}}{{with .Vars}}
-VARIABLES
-
-{{range .}}{{node $ .Decl}}
-{{comment_text .Doc " " "\t"}}
-{{end}}{{end}}{{/*
-
----------------------------------------
-
-*/}}{{with .Funcs}}
-FUNCTIONS
-
-{{range .}}{{node $ .Decl}}
-{{comment_text .Doc " " "\t"}}
-{{example_text $ .Name " "}}{{end}}{{end}}{{/*
-
----------------------------------------
-
-*/}}{{with .Types}}
-TYPES
-
-{{range .}}{{$tname := .Name}}{{node $ .Decl}}
-{{comment_text .Doc " " "\t"}}
-{{range .Consts}}{{node $ .Decl}}
-{{comment_text .Doc " " "\t"}}
-{{end}}{{range .Vars}}{{node $ .Decl}}
-{{comment_text .Doc " " "\t"}}
-{{end}}{{example_text $ .Name " "}}
-{{range .Funcs}}{{node $ .Decl}}
-{{comment_text .Doc " " "\t"}}
-{{example_text $ .Name " "}}
-{{end}}{{range .Methods}}{{node $ .Decl}}
-{{comment_text .Doc " " "\t"}}
-{{$name := printf "%s_%s" $tname .Name}}{{example_text $ $name " "}}{{end}}
-{{end}}{{end}}{{end}}{{/*
-
----------------------------------------
-
-*/}}{{with $.Notes}}
-{{range $marker, $content := .}}
-{{$marker}}S
-
-{{range $content}}{{comment_text .Body " " "\t"}}
-{{end}}{{end}}{{end}}{{end}}{{/*
-
----------------------------------------
-
-*/}}{{with .Dirs}}
-SUBDIRECTORIES
-{{if $.DirFlat}}{{range .List}}{{if .HasPkg}}
- {{.Path}}{{end}}{{end}}
-{{else}}{{range .List}}
- {{repeat `. ` .Depth}}{{.Name}}{{end}}
-{{end}}{{end}}
diff --git a/lib/godoc/search.html b/lib/godoc/search.html
deleted file mode 100644
index 5b54d71267..0000000000
--- a/lib/godoc/search.html
+++ /dev/null
@@ -1,109 +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.
--->
-{{$query_url := urlquery .Query}}
-{{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}}
-{{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}}
-{{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}}
- {{$src_html := srcLink .File.Path | html}}
- {{range .Groups}}
- {{range .}}
- <a href="{{$src_html}}?h={{$query_url}}#L{{infoLine .}}">{{$src_html}}:{{infoLine .}}</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}}
- {{$src_html := srcLink .File.Path | html}}
- <a href="{{$src_html}}?h={{$query_url}}">{{$src_html}}</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 .}}
- <a href="{{$src_html}}?h={{$query_url}}#L{{infoLine .}}">{{infoLine .}}</a>
- {{end}}
- </td>
- </tr>
- {{end}}
- </table>
- {{end}}
- {{end}}
- {{end}}
-{{end}}
-{{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 .}}
- {{$src_html := srcLink .Filename | html}}
- <tr>
- <td align="left" valign="top">
- <a href="{{$src_html}}?h={{$query_url}}">{{$src_html}}</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="{{$src_html}}?h={{$query_url}}#L{{html .}}">{{html .}}</a>
- {{end}}
- {{if not $.Complete}}
- ...
- {{end}}
- </td>
- </tr>
- {{end}}
- {{if not $.Complete}}
- <tr><td align="left">...</td></tr>
- {{end}}
- </table>
- </p>
-{{end}}
diff --git a/lib/godoc/search.txt b/lib/godoc/search.txt
deleted file mode 100644
index 5251a388e0..0000000000
--- a/lib/godoc/search.txt
+++ /dev/null
@@ -1,47 +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 */}}{{/*
-
----------------------------------------
-
-*/}}{{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/src/cmd/godoc/README.godoc-app b/src/cmd/godoc/README.godoc-app
deleted file mode 100644
index cff7d387c1..0000000000
--- a/src/cmd/godoc/README.godoc-app
+++ /dev/null
@@ -1,61 +0,0 @@
-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.
-
-godoc on appengine
-------------------
-
-Prerequisites
--------------
-
-* Go appengine SDK
- https://developers.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go
-
-* Go sources at tip under $GOROOT
-
-
-Directory structure
--------------------
-
-* Let $APPDIR be the directory containing the app engine files.
- (e.g., $APPDIR=$HOME/godoc-app)
-
-* $APPDIR contains the following entries (this may change depending on
- app-engine release and version of godoc):
-
- app.yaml
- godoc.zip
- godoc/
- index.split.*
-
-* The app.yaml file is set up per app engine documentation.
- For instance:
-
- application: godoc-app
- version: 1
- runtime: go
- api_version: go1
-
- handlers:
- - url: /.*
- script: _go_app
-
-* The godoc/ directory contains a copy of the files under $GOROOT/src/cmd/godoc
- with doc.go excluded (it belongs to pseudo-package "documentation")
-
-
-Configuring and running godoc
------------------------------
-
-To configure godoc, run
-
- bash setup-godoc-app.bash
-
-to create the godoc.zip, index.split.*, and godoc/appconfig.go files
-based on $GOROOT and $APPDIR. See the script for details on usage.
-
-To run godoc locally, using the app-engine emulator, run
-
- <path to google_appengine>/dev_appserver.py $APPDIR
-
-godoc should come up at http://localhost:8080 .
diff --git a/src/cmd/godoc/appinit.go b/src/cmd/godoc/appinit.go
deleted file mode 100644
index 996b2b8504..0000000000
--- a/src/cmd/godoc/appinit.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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.
-
-// +build appengine
-
-package main
-
-// This file replaces main.go when running godoc under app-engine.
-// See README.godoc-app for details.
-
-import (
- "archive/zip"
- "log"
- "net/http"
- "path"
-)
-
-func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
- w.WriteHeader(http.StatusNotFound)
- servePage(w, Page{
- Title: "File " + relpath,
- Subtitle: relpath,
- Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path!
- })
-}
-
-func init() {
- log.Println("initializing godoc ...")
- log.Printf(".zip file = %s", zipFilename)
- log.Printf(".zip GOROOT = %s", zipGoroot)
- log.Printf("index files = %s", indexFilenames)
-
- // initialize flags for app engine
- *goroot = path.Join("/", zipGoroot) // fsHttp paths are relative to '/'
- *indexEnabled = true
- *indexFiles = indexFilenames
- *maxResults = 100 // reduce latency by limiting the number of fulltext search results
- *indexThrottle = 0.3 // in case *indexFiles is empty (and thus the indexer is run)
- *showPlayground = true
-
- // read .zip file and set up file systems
- const zipfile = zipFilename
- rc, err := zip.OpenReader(zipfile)
- if err != nil {
- log.Fatalf("%s: %s\n", zipfile, err)
- }
- // rc is never closed (app running forever)
- fs.Bind("/", NewZipFS(rc, zipFilename), *goroot, bindReplace)
-
- // initialize http handlers
- readTemplates()
- initHandlers()
- registerPublicHandlers(http.DefaultServeMux)
- registerPlaygroundHandlers(http.DefaultServeMux)
-
- // initialize default directory tree with corresponding timestamp.
- initFSTree()
-
- // Immediately update metadata.
- updateMetadata()
-
- // initialize search index
- if *indexEnabled {
- go indexer()
- }
-
- log.Println("godoc initialization complete")
-}
diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go
deleted file mode 100644
index e68c0fa6ba..0000000000
--- a/src/cmd/godoc/codewalk.go
+++ /dev/null
@@ -1,494 +0,0 @@
-// 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.
-
-// The /doc/codewalk/ tree is synthesized from codewalk descriptions,
-// files named $GOROOT/doc/codewalk/*.xml.
-// For an example and a description of the format, see
-// http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060
-// and see http://localhost:6060/doc/codewalk/codewalk .
-// That page is itself a codewalk; the source code for it is
-// $GOROOT/doc/codewalk/codewalk.xml.
-
-package main
-
-import (
- "encoding/xml"
- "errors"
- "fmt"
- "io"
- "log"
- "net/http"
- "os"
- "regexp"
- "sort"
- "strconv"
- "strings"
- "text/template"
- "unicode/utf8"
-)
-
-// Handler for /doc/codewalk/ and below.
-func codewalk(w http.ResponseWriter, r *http.Request) {
- relpath := r.URL.Path[len("/doc/codewalk/"):]
- abspath := r.URL.Path
-
- r.ParseForm()
- if f := r.FormValue("fileprint"); f != "" {
- codewalkFileprint(w, r, f)
- return
- }
-
- // If directory exists, serve list of code walks.
- dir, err := fs.Lstat(abspath)
- if err == nil && dir.IsDir() {
- codewalkDir(w, r, relpath, abspath)
- return
- }
-
- // If file exists, serve using standard file server.
- if err == nil {
- serveFile(w, r)
- return
- }
-
- // Otherwise append .xml and hope to find
- // a codewalk description, but before trim
- // the trailing /.
- abspath = strings.TrimRight(abspath, "/")
- cw, err := loadCodewalk(abspath + ".xml")
- if err != nil {
- log.Print(err)
- serveError(w, r, relpath, err)
- return
- }
-
- // Canonicalize the path and redirect if changed
- if redirect(w, r) {
- return
- }
-
- servePage(w, Page{
- Title: "Codewalk: " + cw.Title,
- Tabtitle: cw.Title,
- Body: applyTemplate(codewalkHTML, "codewalk", cw),
- })
-}
-
-// A Codewalk represents a single codewalk read from an XML file.
-type Codewalk struct {
- Title string `xml:"title,attr"`
- File []string `xml:"file"`
- Step []*Codestep `xml:"step"`
-}
-
-// A Codestep is a single step in a codewalk.
-type Codestep struct {
- // Filled in from XML
- Src string `xml:"src,attr"`
- Title string `xml:"title,attr"`
- XML string `xml:",innerxml"`
-
- // Derived from Src; not in XML.
- Err error
- File string
- Lo int
- LoByte int
- Hi int
- HiByte int
- Data []byte
-}
-
-// String method for printing in template.
-// Formats file address nicely.
-func (st *Codestep) String() string {
- s := st.File
- if st.Lo != 0 || st.Hi != 0 {
- s += fmt.Sprintf(":%d", st.Lo)
- if st.Lo != st.Hi {
- s += fmt.Sprintf(",%d", st.Hi)
- }
- }
- return s
-}
-
-// loadCodewalk reads a codewalk from the named XML file.
-func loadCodewalk(filename string) (*Codewalk, error) {
- f, err := fs.Open(filename)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- cw := new(Codewalk)
- d := xml.NewDecoder(f)
- d.Entity = xml.HTMLEntity
- err = d.Decode(cw)
- if err != nil {
- return nil, &os.PathError{Op: "parsing", Path: filename, Err: err}
- }
-
- // Compute file list, evaluate line numbers for addresses.
- m := make(map[string]bool)
- for _, st := range cw.Step {
- i := strings.Index(st.Src, ":")
- if i < 0 {
- i = len(st.Src)
- }
- filename := st.Src[0:i]
- data, err := ReadFile(fs, filename)
- if err != nil {
- st.Err = err
- continue
- }
- if i < len(st.Src) {
- lo, hi, err := addrToByteRange(st.Src[i+1:], 0, data)
- if err != nil {
- st.Err = err
- continue
- }
- // Expand match to line boundaries.
- for lo > 0 && data[lo-1] != '\n' {
- lo--
- }
- for hi < len(data) && (hi == 0 || data[hi-1] != '\n') {
- hi++
- }
- st.Lo = byteToLine(data, lo)
- st.Hi = byteToLine(data, hi-1)
- }
- st.Data = data
- st.File = filename
- m[filename] = true
- }
-
- // Make list of files
- cw.File = make([]string, len(m))
- i := 0
- for f := range m {
- cw.File[i] = f
- i++
- }
- sort.Strings(cw.File)
-
- return cw, nil
-}
-
-// codewalkDir serves the codewalk directory listing.
-// It scans the directory for subdirectories or files named *.xml
-// and prepares a table.
-func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string) {
- type elem struct {
- Name string
- Title string
- }
-
- dir, err := fs.ReadDir(abspath)
- if err != nil {
- log.Print(err)
- serveError(w, r, relpath, err)
- return
- }
- var v []interface{}
- for _, fi := range dir {
- name := fi.Name()
- if fi.IsDir() {
- v = append(v, &elem{name + "/", ""})
- } else if strings.HasSuffix(name, ".xml") {
- cw, err := loadCodewalk(abspath + "/" + name)
- if err != nil {
- continue
- }
- v = append(v, &elem{name[0 : len(name)-len(".xml")], cw.Title})
- }
- }
-
- servePage(w, Page{
- Title: "Codewalks",
- Body: applyTemplate(codewalkdirHTML, "codewalkdir", v),
- })
-}
-
-// codewalkFileprint serves requests with ?fileprint=f&lo=lo&hi=hi.
-// The filename f has already been retrieved and is passed as an argument.
-// Lo and hi are the numbers of the first and last line to highlight
-// in the response. This format is used for the middle window pane
-// of the codewalk pages. It is a separate iframe and does not get
-// the usual godoc HTML wrapper.
-func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) {
- abspath := f
- data, err := ReadFile(fs, abspath)
- if err != nil {
- log.Print(err)
- serveError(w, r, f, err)
- return
- }
- lo, _ := strconv.Atoi(r.FormValue("lo"))
- hi, _ := strconv.Atoi(r.FormValue("hi"))
- if hi < lo {
- hi = lo
- }
- lo = lineToByte(data, lo)
- hi = lineToByte(data, hi+1)
-
- // Put the mark 4 lines before lo, so that the iframe
- // shows a few lines of context before the highlighted
- // section.
- n := 4
- mark := lo
- for ; mark > 0 && n > 0; mark-- {
- if data[mark-1] == '\n' {
- if n--; n == 0 {
- break
- }
- }
- }
-
- io.WriteString(w, `<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`)
- template.HTMLEscape(w, data[0:mark])
- io.WriteString(w, "<a name='mark'></a>")
- template.HTMLEscape(w, data[mark:lo])
- if lo < hi {
- io.WriteString(w, "<div class='codewalkhighlight'>")
- template.HTMLEscape(w, data[lo:hi])
- io.WriteString(w, "</div>")
- }
- template.HTMLEscape(w, data[hi:])
- io.WriteString(w, "</pre>")
-}
-
-// addrToByte evaluates the given address starting at offset start in data.
-// It returns the lo and hi byte offset of the matched region within data.
-// See http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II
-// for details on the syntax.
-func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err error) {
- var (
- dir byte
- prevc byte
- charOffset bool
- )
- lo = start
- hi = start
- for addr != "" && err == nil {
- c := addr[0]
- switch c {
- default:
- err = errors.New("invalid address syntax near " + string(c))
- case ',':
- if len(addr) == 1 {
- hi = len(data)
- } else {
- _, hi, err = addrToByteRange(addr[1:], hi, data)
- }
- return
-
- case '+', '-':
- if prevc == '+' || prevc == '-' {
- lo, hi, err = addrNumber(data, lo, hi, prevc, 1, charOffset)
- }
- dir = c
-
- case '$':
- lo = len(data)
- hi = len(data)
- if len(addr) > 1 {
- dir = '+'
- }
-
- case '#':
- charOffset = true
-
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- var i int
- for i = 1; i < len(addr); i++ {
- if addr[i] < '0' || addr[i] > '9' {
- break
- }
- }
- var n int
- n, err = strconv.Atoi(addr[0:i])
- if err != nil {
- break
- }
- lo, hi, err = addrNumber(data, lo, hi, dir, n, charOffset)
- dir = 0
- charOffset = false
- prevc = c
- addr = addr[i:]
- continue
-
- case '/':
- var i, j int
- Regexp:
- for i = 1; i < len(addr); i++ {
- switch addr[i] {
- case '\\':
- i++
- case '/':
- j = i + 1
- break Regexp
- }
- }
- if j == 0 {
- j = i
- }
- pattern := addr[1:i]
- lo, hi, err = addrRegexp(data, lo, hi, dir, pattern)
- prevc = c
- addr = addr[j:]
- continue
- }
- prevc = c
- addr = addr[1:]
- }
-
- if err == nil && dir != 0 {
- lo, hi, err = addrNumber(data, lo, hi, dir, 1, charOffset)
- }
- if err != nil {
- return 0, 0, err
- }
- return lo, hi, nil
-}
-
-// addrNumber applies the given dir, n, and charOffset to the address lo, hi.
-// dir is '+' or '-', n is the count, and charOffset is true if the syntax
-// used was #n. Applying +n (or +#n) means to advance n lines
-// (or characters) after hi. Applying -n (or -#n) means to back up n lines
-// (or characters) before lo.
-// The return value is the new lo, hi.
-func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int, int, error) {
- switch dir {
- case 0:
- lo = 0
- hi = 0
- fallthrough
-
- case '+':
- if charOffset {
- pos := hi
- for ; n > 0 && pos < len(data); n-- {
- _, size := utf8.DecodeRune(data[pos:])
- pos += size
- }
- if n == 0 {
- return pos, pos, nil
- }
- break
- }
- // find next beginning of line
- if hi > 0 {
- for hi < len(data) && data[hi-1] != '\n' {
- hi++
- }
- }
- lo = hi
- if n == 0 {
- return lo, hi, nil
- }
- for ; hi < len(data); hi++ {
- if data[hi] != '\n' {
- continue
- }
- switch n--; n {
- case 1:
- lo = hi + 1
- case 0:
- return lo, hi + 1, nil
- }
- }
-
- case '-':
- if charOffset {
- // Scan backward for bytes that are not UTF-8 continuation bytes.
- pos := lo
- for ; pos > 0 && n > 0; pos-- {
- if data[pos]&0xc0 != 0x80 {
- n--
- }
- }
- if n == 0 {
- return pos, pos, nil
- }
- break
- }
- // find earlier beginning of line
- for lo > 0 && data[lo-1] != '\n' {
- lo--
- }
- hi = lo
- if n == 0 {
- return lo, hi, nil
- }
- for ; lo >= 0; lo-- {
- if lo > 0 && data[lo-1] != '\n' {
- continue
- }
- switch n--; n {
- case 1:
- hi = lo
- case 0:
- return lo, hi, nil
- }
- }
- }
-
- return 0, 0, errors.New("address out of range")
-}
-
-// addrRegexp searches for pattern in the given direction starting at lo, hi.
-// The direction dir is '+' (search forward from hi) or '-' (search backward from lo).
-// Backward searches are unimplemented.
-func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, error) {
- re, err := regexp.Compile(pattern)
- if err != nil {
- return 0, 0, err
- }
- if dir == '-' {
- // Could implement reverse search using binary search
- // through file, but that seems like overkill.
- return 0, 0, errors.New("reverse search not implemented")
- }
- m := re.FindIndex(data[hi:])
- if len(m) > 0 {
- m[0] += hi
- m[1] += hi
- } else if hi > 0 {
- // No match. Wrap to beginning of data.
- m = re.FindIndex(data)
- }
- if len(m) == 0 {
- return 0, 0, errors.New("no match for " + pattern)
- }
- return m[0], m[1], nil
-}
-
-// lineToByte returns the byte index of the first byte of line n.
-// Line numbers begin at 1.
-func lineToByte(data []byte, n int) int {
- if n <= 1 {
- return 0
- }
- n--
- for i, c := range data {
- if c == '\n' {
- if n--; n == 0 {
- return i + 1
- }
- }
- }
- return len(data)
-}
-
-// byteToLine returns the number of the line containing the byte at index i.
-func byteToLine(data []byte, i int) int {
- l := 1
- for j, c := range data {
- if j == i {
- return l
- }
- if c == '\n' {
- l++
- }
- }
- return l
-}
diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go
deleted file mode 100644
index fda7adce52..0000000000
--- a/src/cmd/godoc/dirtrees.go
+++ /dev/null
@@ -1,320 +0,0 @@
-// 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 main
-
-import (
- "bytes"
- "go/doc"
- "go/parser"
- "go/token"
- "log"
- "os"
- pathpkg "path"
- "strings"
-)
-
-// 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
- 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 {
- maxDepth int
-}
-
-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,
- }
- }
-
- list, _ := fs.ReadDir(path)
-
- // determine number of subdirectories and if there are package files
- ndirs := 0
- hasPkgFiles := false
- var synopses [3]string // prioritized package documentation (0 == highest priority)
- for _, d := range list {
- switch {
- case isPkgDir(d):
- ndirs++
- case 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)
- if synopses[0] == "" {
- // no "optimal" package synopsis yet; continue to collect synopses
- file, err := parseFile(fset, pathpkg.Join(path, d.Name()),
- parser.ParseComments|parser.PackageClauseOnly)
- if err == nil {
- 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())
- }
- }
- }
- }
- }
- }
-
- // create subdirectory tree
- var dirs []*Directory
- if ndirs > 0 {
- dirs = make([]*Directory, ndirs)
- i := 0
- for _, d := range list {
- if isPkgDir(d) {
- name := d.Name()
- dd := b.newDirTree(fset, pathpkg.Join(path, name), name, depth+1)
- if dd != nil {
- dirs[i] = dd
- i++
- }
- }
- }
- dirs = dirs[0:i]
- }
-
- // 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,
- Synopsis: synopsis,
- 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 newDirectory(root string, maxDepth int) *Directory {
- // The root could be a symbolic link so use Stat not Lstat.
- d, err := 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 !isPkgDir(d):
- log.Printf("newDirectory(%s): not a package directory", root)
- return nil
- }
- if maxDepth < 0 {
- maxDepth = 1e6 // "infinity"
- }
- b := treeBuilder{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) writeLeafs(buf *bytes.Buffer) {
- if dir != nil {
- if len(dir.Dirs) == 0 {
- buf.WriteString(dir.Path)
- buf.WriteByte('\n')
- return
- }
-
- for _, d := range dir.Dirs {
- d.writeLeafs(buf)
- }
- }
-}
-
-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
-}
-
-type DirList struct {
- MaxHeight int // directory tree height, > 0
- List []DirEntry
-}
-
-// listing creates a (linear) directory listing from a directory tree.
-// If skipRoot is set, the root directory itself is excluded from the list.
-//
-func (root *Directory) listing(skipRoot 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, n)
- i := 0
- for d := range root.iter(skipRoot) {
- p := &list[i]
- 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
- i++
- }
-
- return &DirList{maxHeight, list}
-}
diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go
deleted file mode 100644
index 1fa57a8b31..0000000000
--- a/src/cmd/godoc/doc.go
+++ /dev/null
@@ -1,135 +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.
-
-/*
-
-Godoc extracts and generates documentation for Go programs.
-
-It has two modes.
-
-Without the -http flag, it runs in command-line mode and prints plain text
-documentation to standard output and exits. If both a library package and
-a command with the same name exists, using the prefix cmd/ will force
-documentation on the command rather than the library package. If the -src
-flag is specified, godoc prints the exported interface of a package in Go
-source form, or the implementation of a specific exported language entity:
-
- godoc fmt # documentation for package fmt
- godoc fmt Printf # documentation for fmt.Printf
- godoc cmd/go # force documentation for the go command
- godoc -src fmt # fmt package interface in Go source form
- godoc -src fmt Printf # implementation of fmt.Printf
-
-In command-line mode, the -q flag enables search queries against a godoc running
-as a webserver. If no explicit server address is specified with the -server flag,
-godoc first tries localhost:6060 and then http://golang.org.
-
- godoc -q Reader
- godoc -q math.Sin
- godoc -server=:6060 -q sin
-
-With the -http flag, it runs as a web server and presents the documentation as a
-web page.
-
- godoc -http=:6060
-
-Usage:
- godoc [flag] package [name ...]
-
-The flags are:
- -v
- verbose mode
- -q
- arguments are considered search queries: a legal query is a
- single identifier (such as ToLower) or a qualified identifier
- (such as math.Sin).
- -src
- print (exported) source in command-line mode
- -tabwidth=4
- width of tabs in units of spaces
- -timestamps=true
- show timestamps with directory listings
- -index
- enable identifier and full text search index
- (no search box is shown if -index is not set)
- -index_files=""
- glob pattern specifying index files; if not empty,
- the index is read from these files in sorted order
- -index_throttle=0.75
- index throttle value; a value of 0 means no time is allocated
- to the indexer (the indexer will never finish), a value of 1.0
- means that index creation is running at full throttle (other
- goroutines may get no time while the index is built)
- -links=true:
- link identifiers to their declarations
- -write_index=false
- write index to a file; the file name must be specified with
- -index_files
- -maxresults=10000
- maximum number of full text search results shown
- (no full text index is built if maxresults <= 0)
- -notes="BUG"
- regular expression matching note markers to show
- (e.g., "BUG|TODO", ".*")
- -html
- print HTML in command-line mode
- -goroot=$GOROOT
- Go root directory
- -http=addr
- HTTP service address (e.g., '127.0.0.1:6060' or just ':6060')
- -server=addr
- webserver address for command line searches
- -templates=""
- directory containing alternate template files; if set,
- the directory may provide alternative template files
- for the files in $GOROOT/lib/godoc
- -url=path
- print to standard output the data that would be served by
- an HTTP request for path
- -zip=""
- zip file providing the file system to serve; disabled if empty
-
-By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set).
-This behavior can be altered by providing an alternative $GOROOT with the -goroot
-flag.
-
-When godoc runs as a web server and -index is set, a search index is maintained.
-The index is created at startup.
-
-The index contains both identifier and full text search information (searchable
-via regular expressions). The maximum number of full text search results shown
-can be set with the -maxresults flag; if set to 0, no full text results are
-shown, and only an identifier index but no full text search index is created.
-
-The presentation mode of web pages served by godoc can be controlled with the
-"m" URL parameter; it accepts a comma-separated list of flag names as value:
-
- all show documentation for all declarations, not just the exported ones
- methods show all embedded methods, not just those of unexported anonymous fields
- src show the original source code rather then the extracted documentation
- text present the page in textual (command-line) form rather than HTML
- flat present flat (not indented) directory listings using full paths
-
-For instance, http://golang.org/pkg/math/big/?m=all,text shows the documentation
-for all (not just the exported) declarations of package big, in textual form (as
-it would appear when using godoc from the command line: "godoc -src math/big .*").
-
-By default, godoc serves files from the file system of the underlying OS.
-Instead, a .zip file may be provided via the -zip flag, which contains
-the file system to serve. The file paths stored in the .zip file must use
-slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot)
-must be set to the .zip file directory path containing the Go root directory.
-For instance, for a .zip file created by the command:
-
- zip go.zip $HOME/go
-
-one may run godoc as follows:
-
- godoc -http=:6060 -zip=go.zip -goroot=$HOME/go
-
-See "Godoc: documenting Go code" for how to write good comments for godoc:
-http://golang.org/doc/articles/godoc_documenting_go_code.html
-
-*/
-package main
diff --git a/src/cmd/godoc/filesystem.go b/src/cmd/godoc/filesystem.go
deleted file mode 100644
index 0309d7cabe..0000000000
--- a/src/cmd/godoc/filesystem.go
+++ /dev/null
@@ -1,562 +0,0 @@
-// 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 defines types for abstract file system access and
-// provides an implementation accessing the file system of the
-// underlying OS.
-
-package main
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "os"
- pathpkg "path"
- "path/filepath"
- "sort"
- "strings"
- "time"
-)
-
-// fs is the file system that godoc reads from and serves.
-// It is a virtual file system that operates on slash-separated paths,
-// and its root corresponds to the Go distribution root: /src/pkg
-// holds the source tree, and so on. This means that the URLs served by
-// the godoc server are the same as the paths in the virtual file
-// system, which helps keep things simple.
-//
-// New file trees - implementations of FileSystem - can be added to
-// the virtual file system using nameSpace's Bind method.
-// The usual setup is to bind OS(runtime.GOROOT) to the root
-// of the name space and then bind any GOPATH/src directories
-// on top of /src/pkg, so that all sources are in /src/pkg.
-//
-// For more about name spaces, see the nameSpace type's
-// documentation below.
-//
-// The use of this virtual file system means that most code processing
-// paths can assume they are slash-separated and should be using
-// package path (often imported as pathpkg) to manipulate them,
-// even on Windows.
-//
-var fs = nameSpace{} // the underlying file system for godoc
-
-// Setting debugNS = true will enable debugging prints about
-// name space translations.
-const debugNS = false
-
-// The FileSystem interface specifies the methods godoc is using
-// to access the file system for which it serves documentation.
-type FileSystem interface {
- Open(path string) (readSeekCloser, error)
- Lstat(path string) (os.FileInfo, error)
- Stat(path string) (os.FileInfo, error)
- ReadDir(path string) ([]os.FileInfo, error)
- String() string
-}
-
-type readSeekCloser interface {
- io.Reader
- io.Seeker
- io.Closer
-}
-
-// ReadFile reads the file named by path from fs and returns the contents.
-func ReadFile(fs FileSystem, path string) ([]byte, error) {
- rc, err := fs.Open(path)
- if err != nil {
- return nil, err
- }
- defer rc.Close()
- return ioutil.ReadAll(rc)
-}
-
-// OS returns an implementation of FileSystem reading from the
-// tree rooted at root. Recording a root is convenient everywhere
-// but necessary on Windows, because the slash-separated path
-// passed to Open has no way to specify a drive letter. Using a root
-// lets code refer to OS(`c:\`), OS(`d:\`) and so on.
-func OS(root string) FileSystem {
- return osFS(root)
-}
-
-type osFS string
-
-func (root osFS) String() string { return "os(" + string(root) + ")" }
-
-func (root osFS) resolve(path string) string {
- // Clean the path so that it cannot possibly begin with ../.
- // If it did, the result of filepath.Join would be outside the
- // tree rooted at root. We probably won't ever see a path
- // with .. in it, but be safe anyway.
- path = pathpkg.Clean("/" + path)
-
- return filepath.Join(string(root), path)
-}
-
-func (root osFS) Open(path string) (readSeekCloser, error) {
- f, err := os.Open(root.resolve(path))
- if err != nil {
- return nil, err
- }
- fi, err := f.Stat()
- if err != nil {
- return nil, err
- }
- if fi.IsDir() {
- return nil, fmt.Errorf("Open: %s is a directory", path)
- }
- return f, nil
-}
-
-func (root osFS) Lstat(path string) (os.FileInfo, error) {
- return os.Lstat(root.resolve(path))
-}
-
-func (root osFS) Stat(path string) (os.FileInfo, error) {
- return os.Stat(root.resolve(path))
-}
-
-func (root osFS) ReadDir(path string) ([]os.FileInfo, error) {
- return ioutil.ReadDir(root.resolve(path)) // is sorted
-}
-
-// hasPathPrefix returns true if x == y or x == y + "/" + more
-func hasPathPrefix(x, y string) bool {
- return x == y || strings.HasPrefix(x, y) && (strings.HasSuffix(y, "/") || strings.HasPrefix(x[len(y):], "/"))
-}
-
-// A nameSpace is a file system made up of other file systems
-// mounted at specific locations in the name space.
-//
-// The representation is a map from mount point locations
-// to the list of file systems mounted at that location. A traditional
-// Unix mount table would use a single file system per mount point,
-// but we want to be able to mount multiple file systems on a single
-// mount point and have the system behave as if the union of those
-// file systems were present at the mount point.
-// For example, if the OS file system has a Go installation in
-// c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then
-// this name space creates the view we want for the godoc server:
-//
-// nameSpace{
-// "/": {
-// {old: "/", fs: OS(`c:\Go`), new: "/"},
-// },
-// "/src/pkg": {
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-// },
-// }
-//
-// This is created by executing:
-//
-// ns := nameSpace{}
-// ns.Bind("/", OS(`c:\Go`), "/", bindReplace)
-// ns.Bind("/src/pkg", OS(`d:\Work1`), "/src", bindAfter)
-// ns.Bind("/src/pkg", OS(`d:\Work2`), "/src", bindAfter)
-//
-// A particular mount point entry is a triple (old, fs, new), meaning that to
-// operate on a path beginning with old, replace that prefix (old) with new
-// and then pass that path to the FileSystem implementation fs.
-//
-// Given this name space, a ReadDir of /src/pkg/code will check each prefix
-// of the path for a mount point (first /src/pkg/code, then /src/pkg, then /src,
-// then /), stopping when it finds one. For the above example, /src/pkg/code
-// will find the mount point at /src/pkg:
-//
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-//
-// ReadDir will when execute these three calls and merge the results:
-//
-// OS(`c:\Go`).ReadDir("/src/pkg/code")
-// OS(`d:\Work1').ReadDir("/src/code")
-// OS(`d:\Work2').ReadDir("/src/code")
-//
-// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by
-// just "/src" in the final two calls.
-//
-// OS is itself an implementation of a file system: it implements
-// OS(`c:\Go`).ReadDir("/src/pkg/code") as ioutil.ReadDir(`c:\Go\src\pkg\code`).
-//
-// Because the new path is evaluated by fs (here OS(root)), another way
-// to read the mount table is to mentally combine fs+new, so that this table:
-//
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-//
-// reads as:
-//
-// "/src/pkg" -> c:\Go\src\pkg
-// "/src/pkg" -> d:\Work1\src
-// "/src/pkg" -> d:\Work2\src
-//
-// An invariant (a redundancy) of the name space representation is that
-// ns[mtpt][i].old is always equal to mtpt (in the example, ns["/src/pkg"]'s
-// mount table entries always have old == "/src/pkg"). The 'old' field is
-// useful to callers, because they receive just a []mountedFS and not any
-// other indication of which mount point was found.
-//
-type nameSpace map[string][]mountedFS
-
-// A mountedFS handles requests for path by replacing
-// a prefix 'old' with 'new' and then calling the fs methods.
-type mountedFS struct {
- old string
- fs FileSystem
- new string
-}
-
-// translate translates path for use in m, replacing old with new.
-//
-// mountedFS{"/src/pkg", fs, "/src"}.translate("/src/pkg/code") == "/src/code".
-func (m mountedFS) translate(path string) string {
- path = pathpkg.Clean("/" + path)
- if !hasPathPrefix(path, m.old) {
- panic("translate " + path + " but old=" + m.old)
- }
- return pathpkg.Join(m.new, path[len(m.old):])
-}
-
-func (nameSpace) String() string {
- return "ns"
-}
-
-// Fprint writes a text representation of the name space to w.
-func (ns nameSpace) Fprint(w io.Writer) {
- fmt.Fprint(w, "name space {\n")
- var all []string
- for mtpt := range ns {
- all = append(all, mtpt)
- }
- sort.Strings(all)
- for _, mtpt := range all {
- fmt.Fprintf(w, "\t%s:\n", mtpt)
- for _, m := range ns[mtpt] {
- fmt.Fprintf(w, "\t\t%s %s\n", m.fs, m.new)
- }
- }
- fmt.Fprint(w, "}\n")
-}
-
-// clean returns a cleaned, rooted path for evaluation.
-// It canonicalizes the path so that we can use string operations
-// to analyze it.
-func (nameSpace) clean(path string) string {
- return pathpkg.Clean("/" + path)
-}
-
-// Bind causes references to old to redirect to the path new in newfs.
-// If mode is bindReplace, old redirections are discarded.
-// If mode is bindBefore, this redirection takes priority over existing ones,
-// but earlier ones are still consulted for paths that do not exist in newfs.
-// If mode is bindAfter, this redirection happens only after existing ones
-// have been tried and failed.
-
-const (
- bindReplace = iota
- bindBefore
- bindAfter
-)
-
-func (ns nameSpace) Bind(old string, newfs FileSystem, new string, mode int) {
- old = ns.clean(old)
- new = ns.clean(new)
- m := mountedFS{old, newfs, new}
- var mtpt []mountedFS
- switch mode {
- case bindReplace:
- mtpt = append(mtpt, m)
- case bindAfter:
- mtpt = append(mtpt, ns.resolve(old)...)
- mtpt = append(mtpt, m)
- case bindBefore:
- mtpt = append(mtpt, m)
- mtpt = append(mtpt, ns.resolve(old)...)
- }
-
- // Extend m.old, m.new in inherited mount point entries.
- for i := range mtpt {
- m := &mtpt[i]
- if m.old != old {
- if !hasPathPrefix(old, m.old) {
- // This should not happen. If it does, panic so
- // that we can see the call trace that led to it.
- panic(fmt.Sprintf("invalid Bind: old=%q m={%q, %s, %q}", old, m.old, m.fs.String(), m.new))
- }
- suffix := old[len(m.old):]
- m.old = pathpkg.Join(m.old, suffix)
- m.new = pathpkg.Join(m.new, suffix)
- }
- }
-
- ns[old] = mtpt
-}
-
-// resolve resolves a path to the list of mountedFS to use for path.
-func (ns nameSpace) resolve(path string) []mountedFS {
- path = ns.clean(path)
- for {
- if m := ns[path]; m != nil {
- if debugNS {
- fmt.Printf("resolve %s: %v\n", path, m)
- }
- return m
- }
- if path == "/" {
- break
- }
- path = pathpkg.Dir(path)
- }
- return nil
-}
-
-// Open implements the FileSystem Open method.
-func (ns nameSpace) Open(path string) (readSeekCloser, error) {
- var err error
- for _, m := range ns.resolve(path) {
- if debugNS {
- fmt.Printf("tx %s: %v\n", path, m.translate(path))
- }
- r, err1 := m.fs.Open(m.translate(path))
- if err1 == nil {
- return r, nil
- }
- if err == nil {
- err = err1
- }
- }
- if err == nil {
- err = &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
- }
- return nil, err
-}
-
-// stat implements the FileSystem Stat and Lstat methods.
-func (ns nameSpace) stat(path string, f func(FileSystem, string) (os.FileInfo, error)) (os.FileInfo, error) {
- var err error
- for _, m := range ns.resolve(path) {
- fi, err1 := f(m.fs, m.translate(path))
- if err1 == nil {
- return fi, nil
- }
- if err == nil {
- err = err1
- }
- }
- if err == nil {
- err = &os.PathError{Op: "stat", Path: path, Err: os.ErrNotExist}
- }
- return nil, err
-}
-
-func (ns nameSpace) Stat(path string) (os.FileInfo, error) {
- return ns.stat(path, FileSystem.Stat)
-}
-
-func (ns nameSpace) Lstat(path string) (os.FileInfo, error) {
- return ns.stat(path, FileSystem.Lstat)
-}
-
-// dirInfo is a trivial implementation of os.FileInfo for a directory.
-type dirInfo string
-
-func (d dirInfo) Name() string { return string(d) }
-func (d dirInfo) Size() int64 { return 0 }
-func (d dirInfo) Mode() os.FileMode { return os.ModeDir | 0555 }
-func (d dirInfo) ModTime() time.Time { return startTime }
-func (d dirInfo) IsDir() bool { return true }
-func (d dirInfo) Sys() interface{} { return nil }
-
-var startTime = time.Now()
-
-// ReadDir implements the FileSystem ReadDir method. It's where most of the magic is.
-// (The rest is in resolve.)
-//
-// Logically, ReadDir must return the union of all the directories that are named
-// by path. In order to avoid misinterpreting Go packages, of all the directories
-// that contain Go source code, we only include the files from the first,
-// but we include subdirectories from all.
-//
-// ReadDir must also return directory entries needed to reach mount points.
-// If the name space looks like the example in the type nameSpace comment,
-// but c:\Go does not have a src/pkg subdirectory, we still want to be able
-// to find that subdirectory, because we've mounted d:\Work1 and d:\Work2
-// there. So if we don't see "src" in the directory listing for c:\Go, we add an
-// entry for it before returning.
-//
-func (ns nameSpace) ReadDir(path string) ([]os.FileInfo, error) {
- path = ns.clean(path)
-
- var (
- haveGo = false
- haveName = map[string]bool{}
- all []os.FileInfo
- err error
- first []os.FileInfo
- )
-
- for _, m := range ns.resolve(path) {
- dir, err1 := m.fs.ReadDir(m.translate(path))
- if err1 != nil {
- if err == nil {
- err = err1
- }
- continue
- }
-
- if dir == nil {
- dir = []os.FileInfo{}
- }
-
- if first == nil {
- first = dir
- }
-
- // If we don't yet have Go files in 'all' and this directory
- // has some, add all the files from this directory.
- // Otherwise, only add subdirectories.
- useFiles := false
- if !haveGo {
- for _, d := range dir {
- if strings.HasSuffix(d.Name(), ".go") {
- useFiles = true
- haveGo = true
- break
- }
- }
- }
-
- for _, d := range dir {
- name := d.Name()
- if (d.IsDir() || useFiles) && !haveName[name] {
- haveName[name] = true
- all = append(all, d)
- }
- }
- }
-
- // We didn't find any directories containing Go files.
- // If some directory returned successfully, use that.
- if !haveGo {
- for _, d := range first {
- if !haveName[d.Name()] {
- haveName[d.Name()] = true
- all = append(all, d)
- }
- }
- }
-
- // Built union. Add any missing directories needed to reach mount points.
- for old := range ns {
- if hasPathPrefix(old, path) && old != path {
- // Find next element after path in old.
- elem := old[len(path):]
- elem = strings.TrimPrefix(elem, "/")
- if i := strings.Index(elem, "/"); i >= 0 {
- elem = elem[:i]
- }
- if !haveName[elem] {
- haveName[elem] = true
- all = append(all, dirInfo(elem))
- }
- }
- }
-
- if len(all) == 0 {
- return nil, err
- }
-
- sort.Sort(byName(all))
- return all, nil
-}
-
-// byName implements sort.Interface.
-type byName []os.FileInfo
-
-func (f byName) Len() int { return len(f) }
-func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
-func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
-
-// An httpFS implements http.FileSystem using a FileSystem.
-type httpFS struct {
- fs FileSystem
-}
-
-func (h *httpFS) Open(name string) (http.File, error) {
- fi, err := h.fs.Stat(name)
- if err != nil {
- return nil, err
- }
- if fi.IsDir() {
- return &httpDir{h.fs, name, nil}, nil
- }
- f, err := h.fs.Open(name)
- if err != nil {
- return nil, err
- }
- return &httpFile{h.fs, f, name}, nil
-}
-
-// httpDir implements http.File for a directory in a FileSystem.
-type httpDir struct {
- fs FileSystem
- name string
- pending []os.FileInfo
-}
-
-func (h *httpDir) Close() error { return nil }
-func (h *httpDir) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) }
-func (h *httpDir) Read([]byte) (int, error) {
- return 0, fmt.Errorf("cannot Read from directory %s", h.name)
-}
-
-func (h *httpDir) Seek(offset int64, whence int) (int64, error) {
- if offset == 0 && whence == 0 {
- h.pending = nil
- return 0, nil
- }
- return 0, fmt.Errorf("unsupported Seek in directory %s", h.name)
-}
-
-func (h *httpDir) Readdir(count int) ([]os.FileInfo, error) {
- if h.pending == nil {
- d, err := h.fs.ReadDir(h.name)
- if err != nil {
- return nil, err
- }
- if d == nil {
- d = []os.FileInfo{} // not nil
- }
- h.pending = d
- }
-
- if len(h.pending) == 0 && count > 0 {
- return nil, io.EOF
- }
- if count <= 0 || count > len(h.pending) {
- count = len(h.pending)
- }
- d := h.pending[:count]
- h.pending = h.pending[count:]
- return d, nil
-}
-
-// httpFile implements http.File for a file (not directory) in a FileSystem.
-type httpFile struct {
- fs FileSystem
- readSeekCloser
- name string
-}
-
-func (h *httpFile) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) }
-func (h *httpFile) Readdir(int) ([]os.FileInfo, error) {
- return nil, fmt.Errorf("cannot Readdir from file %s", h.name)
-}
diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go
deleted file mode 100644
index 59a89c5bf9..0000000000
--- a/src/cmd/godoc/format.go
+++ /dev/null
@@ -1,372 +0,0 @@
-// 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 main
-
-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, "<a id=\"L%d\"></a><span class=\"ln\">%6d</span>\t", line, line)
- line++
- }
- }
- }
- FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
- } else {
- template.HTMLEscape(w, text)
- }
-}
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
deleted file mode 100644
index 79d485b93d..0000000000
--- a/src/cmd/godoc/godoc.go
+++ /dev/null
@@ -1,1586 +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.
-
-package main
-
-import (
- "bytes"
- "encoding/json"
- "flag"
- "fmt"
- "go/ast"
- "go/build"
- "go/doc"
- "go/format"
- "go/printer"
- "go/token"
- htmlpkg "html"
- "io"
- "io/ioutil"
- "log"
- "net/http"
- "net/url"
- "os"
- pathpkg "path"
- "path/filepath"
- "regexp"
- "runtime"
- "sort"
- "strings"
- "text/template"
- "time"
- "unicode"
- "unicode/utf8"
-)
-
-// ----------------------------------------------------------------------------
-// Globals
-
-type delayTime struct {
- RWValue
-}
-
-func (dt *delayTime) backoff(max time.Duration) {
- dt.mutex.Lock()
- v := dt.value.(time.Duration) * 2
- if v > max {
- v = max
- }
- dt.value = v
- // don't change dt.timestamp - calling backoff indicates an error condition
- dt.mutex.Unlock()
-}
-
-var (
- verbose = flag.Bool("v", false, "verbose mode")
-
- // file system roots
- // TODO(gri) consider the invariant that goroot always end in '/'
- goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
- testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)")
-
- // layout control
- tabwidth = flag.Int("tabwidth", 4, "tab width")
- showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
- templateDir = flag.String("templates", "", "directory containing alternate template files")
- 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
- indexEnabled = flag.Bool("index", false, "enable search index")
- indexFiles = flag.String("index_files", "", "glob pattern specifying index files;"+
- "if not empty, the index is read from these files in sorted order")
- maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
- indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle")
-
- // file system information
- fsTree RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now)
- fsModified RWValue // timestamp of last call to invalidateIndex
- docMetadata RWValue // mapping from paths to *Metadata
-
- // http handlers
- fileServer http.Handler // default file server
- cmdHandler docServer
- pkgHandler docServer
-
- // source code notes
- notes = flag.String("notes", "BUG", "regular expression matching note markers to show")
-)
-
-func initHandlers() {
- fileServer = http.FileServer(&httpFS{fs})
- cmdHandler = docServer{"/cmd/", "/src/cmd"}
- pkgHandler = docServer{"/pkg/", "/src/pkg"}
-}
-
-func registerPublicHandlers(mux *http.ServeMux) {
- mux.Handle(cmdHandler.pattern, &cmdHandler)
- mux.Handle(pkgHandler.pattern, &pkgHandler)
- mux.HandleFunc("/doc/codewalk/", codewalk)
- mux.Handle("/doc/play/", fileServer)
- mux.HandleFunc("/search", search)
- mux.Handle("/robots.txt", fileServer)
- mux.HandleFunc("/opensearch.xml", serveSearchDesc)
- mux.HandleFunc("/", serveFile)
-}
-
-func initFSTree() {
- dir := newDirectory(pathpkg.Join("/", *testDir), -1)
- if dir == nil {
- log.Println("Warning: FSTree is nil")
- return
- }
- fsTree.set(dir)
- invalidateIndex()
-}
-
-// ----------------------------------------------------------------------------
-// Tab conversion
-
-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
-}
-
-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 += *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
-}
-
-// ----------------------------------------------------------------------------
-// Templates
-
-// Write an AST node to w.
-func 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: *tabwidth}).Fprint(&tconv{output: w}, fset, x)
- if err != nil {
- log.Print(err)
- }
-}
-
-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 infoLineFunc(info SpotInfo) int {
- line := info.Lori()
- if info.IsIndex() {
- index, _ := 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 infoSnippet_htmlFunc(info SpotInfo) string {
- if info.IsIndex() {
- index, _ := 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 nodeFunc(info *PageInfo, node interface{}) string {
- var buf bytes.Buffer
- writeNode(&buf, info.FSet, node)
- return buf.String()
-}
-
-func node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string {
- var buf1 bytes.Buffer
- writeNode(&buf1, info.FSet, node)
-
- var buf2 bytes.Buffer
- if n, _ := node.(ast.Node); n != nil && linkify && *declLinks {
- LinkifyText(&buf2, buf1.Bytes(), n)
- } else {
- FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
- }
-
- return buf2.String()
-}
-
-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 comment_textFunc(comment, indent, preIndent string) string {
- var buf bytes.Buffer
- doc.ToText(&buf, comment, indent, preIndent, punchCardWidth-2*len(indent))
- return buf.String()
-}
-
-func startsWithUppercase(s string) bool {
- r, _ := utf8.DecodeRuneInString(s)
- return unicode.IsUpper(r)
-}
-
-var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*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 example_textFunc(info *PageInfo, funcName, indent string) string {
- if !*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}
- var buf1 bytes.Buffer
- writeNode(&buf1, info.FSet, cnode)
- code := buf1.String()
- // Additional formatting if this is a function body.
- if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
- // remove surrounding braces
- code = code[1 : n-1]
- // unindent
- code = strings.Replace(code, "\n ", "\n", -1)
- }
- code = strings.Trim(code, "\n")
- code = strings.Replace(code, "\n", "\n\t", -1)
-
- buf.WriteString(indent)
- buf.WriteString("Example:\n\t")
- buf.WriteString(code)
- buf.WriteString("\n")
- }
- return buf.String()
-}
-
-func 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 := 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 = strings.Replace(code, "\n ", "\n", -1)
- // 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 && *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 = ""
- }
-
- err := exampleHTML.Execute(&buf, struct {
- Name, Doc, Code, Play, Output string
- }{eg.Name, eg.Doc, code, play, out})
- 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 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 example_suffixFunc(name string) string {
- _, suffix := splitExampleName(name)
- return suffix
-}
-
-func noteTitle(note string) string {
- return strings.Title(strings.ToLower(note))
-}
-
-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
-}
-
-func pkgLinkFunc(path string) string {
- relpath := path[1:]
- // because of the irregular mapping under goroot
- // we need to correct certain relative paths
- relpath = strings.TrimPrefix(relpath, "src/pkg/")
- return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL
-}
-
-// n must be an ast.Node or a *doc.Note
-func posLink_urlFunc(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
- }
-
- var buf bytes.Buffer
- template.HTMLEscape(&buf, []byte(relpath))
- // 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 {
- return pathpkg.Clean("/" + s)
-}
-
-// fmap describes the template functions installed with all 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.
-var fmap = 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": infoLineFunc,
- "infoSnippet_html": infoSnippet_htmlFunc,
-
- // formatting of AST nodes
- "node": nodeFunc,
- "node_html": node_htmlFunc,
- "comment_html": comment_htmlFunc,
- "comment_text": comment_textFunc,
-
- // support for URL attributes
- "pkgLink": pkgLinkFunc,
- "srcLink": srcLinkFunc,
- "posLink_url": posLink_urlFunc,
-
- // formatting of Examples
- "example_html": example_htmlFunc,
- "example_text": example_textFunc,
- "example_name": example_nameFunc,
- "example_suffix": example_suffixFunc,
-
- // formatting of Notes
- "noteTitle": noteTitle,
-}
-
-func readTemplate(name string) *template.Template {
- path := "lib/godoc/" + name
-
- // use underlying file system fs to read the template file
- // (cannot use template ParseFile functions directly)
- data, err := ReadFile(fs, path)
- if err != nil {
- log.Fatal("readTemplate: ", err)
- }
- // be explicit with errors (for app engine use)
- t, err := template.New(name).Funcs(fmap).Parse(string(data))
- if err != nil {
- log.Fatal("readTemplate: ", err)
- }
- return t
-}
-
-var (
- codewalkHTML,
- codewalkdirHTML,
- dirlistHTML,
- errorHTML,
- exampleHTML,
- godocHTML,
- packageHTML,
- packageText,
- searchHTML,
- searchText,
- searchDescXML *template.Template
-)
-
-func readTemplates() {
- // have to delay until after flags processing since paths depend on goroot
- codewalkHTML = readTemplate("codewalk.html")
- codewalkdirHTML = readTemplate("codewalkdir.html")
- dirlistHTML = readTemplate("dirlist.html")
- errorHTML = readTemplate("error.html")
- exampleHTML = readTemplate("example.html")
- godocHTML = readTemplate("godoc.html")
- packageHTML = readTemplate("package.html")
- packageText = readTemplate("package.txt")
- searchHTML = readTemplate("search.html")
- searchText = readTemplate("search.txt")
- searchDescXML = readTemplate("opensearch.xml")
-}
-
-// ----------------------------------------------------------------------------
-// Generic HTML wrapper
-
-// Page describes the contents of the top-level godoc webpage.
-type Page struct {
- Title string
- Tabtitle string
- Subtitle string
- Query string
- Body []byte
-
- // filled in by servePage
- SearchBox bool
- Playground bool
- Version string
-}
-
-func servePage(w http.ResponseWriter, page Page) {
- if page.Tabtitle == "" {
- page.Tabtitle = page.Title
- }
- page.SearchBox = *indexEnabled
- page.Playground = *showPlayground
- page.Version = runtime.Version()
- if err := godocHTML.Execute(w, page); err != nil && err != http.ErrBodyNotAllowed {
- // Only log if there's an error that's not about writing on HEAD requests.
- // See Issues 5451 and 5454.
- log.Printf("godocHTML.Execute: %s", err)
- }
-}
-
-func serveText(w http.ResponseWriter, text []byte) {
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- w.Write(text)
-}
-
-// ----------------------------------------------------------------------------
-// Files
-
-var (
- doctype = []byte("<!DOCTYPE ")
- jsonStart = []byte("<!--{")
- jsonEnd = []byte("}-->")
-)
-
-func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
- // get HTML body contents
- src, err := ReadFile(fs, abspath)
- if err != nil {
- log.Printf("ReadFile: %s", err)
- 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)
- }
-
- // evaluate as template if indicated
- if meta.Template {
- tmpl, err := template.New("main").Funcs(templateFuncs).Parse(string(src))
- if err != nil {
- log.Printf("parsing template %s: %v", relpath, err)
- serveError(w, r, relpath, err)
- return
- }
- var buf bytes.Buffer
- if err := tmpl.Execute(&buf, nil); err != nil {
- log.Printf("executing template %s: %v", relpath, err)
- 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()
- }
-
- servePage(w, Page{
- Title: meta.Title,
- Subtitle: meta.Subtitle,
- Body: src,
- })
-}
-
-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()
-}
-
-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 serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
- src, err := ReadFile(fs, abspath)
- if err != nil {
- log.Printf("ReadFile: %s", err)
- serveError(w, r, relpath, err)
- return
- }
-
- if r.FormValue("m") == "text" {
- serveText(w, src)
- return
- }
-
- var buf bytes.Buffer
- buf.WriteString("<pre>")
- FormatText(&buf, src, 1, pathpkg.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s")))
- buf.WriteString("</pre>")
- fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath))
-
- servePage(w, Page{
- Title: title + " " + relpath,
- Tabtitle: relpath,
- Body: buf.Bytes(),
- })
-}
-
-func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
- if redirect(w, r) {
- return
- }
-
- list, err := fs.ReadDir(abspath)
- if err != nil {
- serveError(w, r, relpath, err)
- return
- }
-
- servePage(w, Page{
- Title: "Directory " + relpath,
- Tabtitle: relpath,
- Body: applyTemplate(dirlistHTML, "dirlistHTML", list),
- })
-}
-
-func 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 := 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
- }
- serveHTMLDoc(w, r, abspath, relpath)
- return
-
- case ".go":
- serveTextFile(w, r, abspath, relpath, "Source file")
- return
- }
-
- dir, err := fs.Lstat(abspath)
- if err != nil {
- log.Print(err)
- serveError(w, r, relpath, err)
- return
- }
-
- if dir != nil && dir.IsDir() {
- if redirect(w, r) {
- return
- }
- if index := pathpkg.Join(abspath, "index.html"); isTextFile(index) {
- serveHTMLDoc(w, r, index, index)
- return
- }
- serveDirectory(w, r, abspath, relpath)
- return
- }
-
- if isTextFile(abspath) {
- if redirectFile(w, r) {
- return
- }
- serveTextFile(w, r, abspath, relpath, "Text file")
- return
- }
-
- fileServer.ServeHTTP(w, r)
-}
-
-func 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),
- }
- if err := searchDescXML.Execute(w, &data); err != nil && err != http.ErrBodyNotAllowed {
- // Only log if there's an error that's not about writing on HEAD requests.
- // See Issues 5451 and 5454.
- log.Printf("searchDescXML.Execute: %s", err)
- }
-}
-
-// ----------------------------------------------------------------------------
-// Packages
-
-// 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"
-
-type PageInfoMode uint
-
-const (
- 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
-)
-
-// modeNames defines names for each PageInfoMode flag.
-var modeNames = map[string]PageInfoMode{
- "all": noFiltering,
- "methods": allMethods,
- "src": showSource,
- "text": noHtml,
- "flat": flatDir,
-}
-
-// 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 getPageInfoMode(r *http.Request) PageInfoMode {
- var mode PageInfoMode
- for _, k := range strings.Split(r.FormValue("m"), ",") {
- if m, found := modeNames[strings.TrimSpace(k)]; found {
- mode |= m
- }
- }
- return adjustPageInfoMode(r, mode)
-}
-
-// Specialized versions of godoc may adjust the PageInfoMode by overriding
-// this variable.
-var adjustPageInfoMode = func(_ *http.Request, mode PageInfoMode) PageInfoMode {
- return mode
-}
-
-// 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)
-}
-
-type PageInfo struct {
- Dirname string // directory containing the package
- Err error // error or nil
-
- // 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 *ast.File // nil if no AST with package exports
- IsMain bool // true for package main
-
- // 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
-}
-
-type docServer struct {
- pattern string // url pattern; e.g. "/pkg/"
- fsRoot string // file system root to which the pattern is mapped
-}
-
-// fsReadDir implements ReadDir for the go/build package.
-func fsReadDir(dir string) ([]os.FileInfo, error) {
- return fs.ReadDir(filepath.ToSlash(dir))
-}
-
-// fsOpenFile implements OpenFile for the go/build package.
-func fsOpenFile(name string) (r io.ReadCloser, err error) {
- data, err := ReadFile(fs, filepath.ToSlash(name))
- if err != nil {
- return nil, err
- }
- return ioutil.NopCloser(bytes.NewReader(data)), nil
-}
-
-// 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()
- }
-}
-
-// 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
- }
- }
- }
- }
-}
-
-// 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(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 {
- log.Printf("skipping example 'Example%s' because '%s' is not a known function or type", e.Name, e.Name)
- }
- }
-
- return examples
-}
-
-// 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
-}
-
-// 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 *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
- info := &PageInfo{Dirname: abspath}
-
- // 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: Uses current binary's GOOS/GOARCH.
- // To use different pair, such as if we allowed the user to choose,
- // set ctxt.GOOS and ctxt.GOARCH before calling ctxt.ImportDir.
- ctxt := build.Default
- ctxt.IsAbsPath = pathpkg.IsAbs
- ctxt.ReadDir = fsReadDir
- ctxt.OpenFile = fsOpenFile
- 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 := parseFiles(fset, 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
-
- // collect examples
- testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...)
- files, err = parseFiles(fset, abspath, testfiles)
- if err != nil {
- log.Println("parsing examples:", err)
- }
- info.Examples = collectExamples(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, err := regexp.Compile(*notes); err == 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 = ast.MergePackageFiles(pkg, 0)
- }
- info.IsMain = pkgname == "main"
- }
-
- // get directory information, if any
- var dir *Directory
- var timestamp time.Time
- if tree, ts := 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 (too early after startup or
- // command-line mode); compute one level for this page
- // note: cannot use path filter here because in general
- // it doesn't contain the fsTree path
- dir = newDirectory(abspath, 1)
- timestamp = time.Now()
- }
- info.Dirs = dir.listing(true)
- info.DirTime = timestamp
- info.DirFlat = mode&flatDir != 0
-
- return info
-}
-
-func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if redirect(w, r) {
- return
- }
-
- relpath := pathpkg.Clean(r.URL.Path[len(h.pattern):])
- abspath := pathpkg.Join(h.fsRoot, relpath)
- mode := getPageInfoMode(r)
- if relpath == builtinPkgPath {
- mode = noFiltering
- }
- info := h.getPageInfo(abspath, relpath, mode)
- if info.Err != nil {
- log.Print(info.Err)
- serveError(w, r, relpath, info.Err)
- return
- }
-
- if mode&noHtml != 0 {
- serveText(w, applyTemplate(packageText, "packageText", info))
- return
- }
-
- var tabtitle, title, subtitle string
- switch {
- case info.PAst != nil:
- tabtitle = info.PAst.Name.Name
- case info.PDoc != nil:
- tabtitle = info.PDoc.Name
- default:
- tabtitle = info.Dirname
- title = "Directory "
- if *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/pkg":
- tabtitle = "Packages"
- case "/src/cmd":
- tabtitle = "Commands"
- }
-
- servePage(w, Page{
- Title: title,
- Tabtitle: tabtitle,
- Subtitle: subtitle,
- Body: applyTemplate(packageHTML, "packageHTML", info),
- })
-}
-
-// ----------------------------------------------------------------------------
-// Search
-
-var searchIndex RWValue
-
-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
-}
-
-func lookup(query string) (result SearchResult) {
- result.Query = query
-
- index, timestamp := searchIndex.get()
- if index != nil {
- index := index.(*Index)
-
- // identifier search
- var err error
- result.Pak, result.Hit, result.Alt, err = index.Lookup(query)
- if err != nil && *maxResults <= 0 {
- // 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
- }
-
- // full text search
- if *maxResults > 0 && query != "" {
- rx, err := regexp.Compile(query)
- if err != nil {
- result.Alert = "Error in query regular expression: " + err.Error()
- return
- }
- // If we get maxResults+1 results we know that there are more than
- // maxResults results and thus the result may be incomplete (to be
- // precise, we should remove one result from the result set, but
- // nobody is going to count the results on the result page).
- result.Found, result.Textual = index.LookupRegexp(rx, *maxResults+1)
- result.Complete = result.Found <= *maxResults
- if !result.Complete {
- result.Found-- // since we looked for maxResults+1
- }
- }
- }
-
- // is the result accurate?
- if *indexEnabled {
- if _, ts := fsModified.get(); 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
-}
-
-func search(w http.ResponseWriter, r *http.Request) {
- query := strings.TrimSpace(r.FormValue("q"))
- result := lookup(query)
-
- if getPageInfoMode(r)&noHtml != 0 {
- serveText(w, applyTemplate(searchText, "searchText", result))
- return
- }
-
- var title string
- if result.Hit != nil || len(result.Textual) > 0 {
- title = fmt.Sprintf(`Results for query %q`, query)
- } else {
- title = fmt.Sprintf(`No results found for query %q`, query)
- }
-
- servePage(w, Page{
- Title: title,
- Tabtitle: query,
- Query: query,
- Body: applyTemplate(searchHTML, "searchHTML", result),
- })
-}
-
-// ----------------------------------------------------------------------------
-// Documentation Metadata
-
-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
-}
-
-// 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 updateMetadata() {
- metadata := make(map[string]*Metadata)
- var scan func(string) // scan is recursive
- scan = func(dir string) {
- fis, err := 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 := ReadFile(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")
- docMetadata.set(metadata)
-}
-
-// 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.
-//
-var refreshMetadataSignal = make(chan bool, 1)
-
-// refreshMetadata sends a signal to update docMetadata. If a refresh is in
-// progress the metadata will be refreshed again afterward.
-//
-func refreshMetadata() {
- select {
- case refreshMetadataSignal <- true:
- default:
- }
-}
-
-// refreshMetadataLoop runs forever, updating docMetadata when the underlying
-// file system changes. It should be launched in a goroutine by main.
-//
-func refreshMetadataLoop() {
- for {
- <-refreshMetadataSignal
- updateMetadata()
- time.Sleep(10 * time.Second) // at most once every 10 seconds
- }
-}
-
-// metadataFor returns the *Metadata for a given relative path or nil if none
-// exists.
-//
-func metadataFor(relpath string) *Metadata {
- if m, _ := 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
-}
-
-// ----------------------------------------------------------------------------
-// Indexer
-
-// invalidateIndex should be called whenever any of the file systems
-// under godoc's observation change so that the indexer is kicked on.
-//
-func invalidateIndex() {
- fsModified.set(nil)
- refreshMetadata()
-}
-
-// indexUpToDate() returns true if the search index is not older
-// than any of the file systems under godoc's observation.
-//
-func indexUpToDate() bool {
- _, fsTime := fsModified.get()
- _, siTime := searchIndex.get()
- return !fsTime.After(siTime)
-}
-
-// feedDirnames feeds the directory names of all directories
-// under the file system given by root to channel c.
-//
-func feedDirnames(root *RWValue, c chan<- string) {
- if dir, _ := root.get(); dir != nil {
- for d := range dir.(*Directory).iter(false) {
- c <- d.Path
- }
- }
-}
-
-// fsDirnames() returns a channel sending all directory names
-// of all the file systems under godoc's observation.
-//
-func fsDirnames() <-chan string {
- c := make(chan string, 256) // buffered for fewer context switches
- go func() {
- feedDirnames(&fsTree, c)
- close(c)
- }()
- return c
-}
-
-func readIndex(filenames string) error {
- matches, err := filepath.Glob(filenames)
- if err != nil {
- return err
- } else if matches == nil {
- return fmt.Errorf("no index files match %q", filenames)
- }
- sort.Strings(matches) // make sure files are in the right order
- files := make([]io.Reader, 0, len(matches))
- for _, filename := range matches {
- f, err := os.Open(filename)
- if err != nil {
- return err
- }
- defer f.Close()
- files = append(files, f)
- }
- x := new(Index)
- if err := x.Read(io.MultiReader(files...)); err != nil {
- return err
- }
- searchIndex.set(x)
- return nil
-}
-
-func updateIndex() {
- if *verbose {
- log.Printf("updating index...")
- }
- start := time.Now()
- index := NewIndex(fsDirnames(), *maxResults > 0, *indexThrottle)
- stop := time.Now()
- searchIndex.set(index)
- if *verbose {
- secs := stop.Sub(start).Seconds()
- stats := index.Stats()
- log.Printf("index updated (%gs, %d bytes of source, %d files, %d lines, %d unique words, %d spots)",
- secs, stats.Bytes, stats.Files, stats.Lines, stats.Words, stats.Spots)
- }
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- log.Printf("before GC: bytes = %d footprint = %d", memstats.HeapAlloc, memstats.Sys)
- runtime.GC()
- runtime.ReadMemStats(memstats)
- log.Printf("after GC: bytes = %d footprint = %d", memstats.HeapAlloc, memstats.Sys)
-}
-
-func indexer() {
- // initialize the index from disk if possible
- if *indexFiles != "" {
- if err := readIndex(*indexFiles); err != nil {
- log.Printf("error reading index: %s", err)
- }
- }
-
- // repeatedly update the index when it goes out of date
- for {
- if !indexUpToDate() {
- // index possibly out of date - make a new one
- updateIndex()
- }
- delay := 60 * time.Second // by default, try every 60s
- if *testDir != "" {
- // in test mode, try once a second for fast startup
- delay = 1 * time.Second
- }
- time.Sleep(delay)
- }
-}
diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go
deleted file mode 100644
index d1292d5053..0000000000
--- a/src/cmd/godoc/index.go
+++ /dev/null
@@ -1,1079 +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.
-
-// This file contains the infrastructure to create an
-// identifier and full-text index for a set of Go files.
-//
-// Algorithm for identifier index:
-// - traverse all .go files of the file tree specified by root
-// - for each identifier (word) encountered, collect all occurrences (spots)
-// into a list; this produces a list of spots for each word
-// - reduce the lists: from a list of spots to a list of FileRuns,
-// and from a list of FileRuns into a list of PakRuns
-// - make a HitList from the PakRuns
-//
-// Details:
-// - keep two lists per word: one containing package-level declarations
-// that have snippets, and one containing all other spots
-// - keep the snippets in a separate table indexed by snippet index
-// and store the snippet index in place of the line number in a SpotInfo
-// (the line number for spots with snippets is stored in the snippet)
-// - at the end, create lists of alternative spellings for a given
-// word
-//
-// Algorithm for full text index:
-// - concatenate all source code in a byte buffer (in memory)
-// - add the files to a file set in lockstep as they are added to the byte
-// buffer such that a byte buffer offset corresponds to the Pos value for
-// that file location
-// - create a suffix array from the concatenated sources
-//
-// String lookup in full text index:
-// - use the suffix array to lookup a string's offsets - the offsets
-// correspond to the Pos values relative to the file set
-// - translate the Pos values back into file and line information and
-// sort the result
-
-package main
-
-import (
- "bufio"
- "bytes"
- "encoding/gob"
- "errors"
- "go/ast"
- "go/parser"
- "go/token"
- "index/suffixarray"
- "io"
- "os"
- pathpkg "path"
- "regexp"
- "sort"
- "strings"
- "time"
- "unicode"
-)
-
-// ----------------------------------------------------------------------------
-// InterfaceSlice is a helper type for sorting interface
-// slices according to some slice-specific sort criteria.
-
-type Comparer func(x, y interface{}) bool
-
-type InterfaceSlice struct {
- slice []interface{}
- less Comparer
-}
-
-func (p *InterfaceSlice) Len() int { return len(p.slice) }
-func (p *InterfaceSlice) Less(i, j int) bool { return p.less(p.slice[i], p.slice[j]) }
-func (p *InterfaceSlice) Swap(i, j int) { p.slice[i], p.slice[j] = p.slice[j], p.slice[i] }
-
-// ----------------------------------------------------------------------------
-// RunList
-
-// A RunList is a list of entries that can be sorted according to some
-// criteria. A RunList may be compressed by grouping "runs" of entries
-// which are equal (according to the sort critera) into a new RunList of
-// runs. For instance, a RunList containing pairs (x, y) may be compressed
-// into a RunList containing pair runs (x, {y}) where each run consists of
-// a list of y's with the same x.
-type RunList []interface{}
-
-func (h RunList) sort(less Comparer) {
- sort.Sort(&InterfaceSlice{h, less})
-}
-
-// Compress entries which are the same according to a sort criteria
-// (specified by less) into "runs".
-func (h RunList) reduce(less Comparer, newRun func(h RunList) interface{}) RunList {
- if len(h) == 0 {
- return nil
- }
- // len(h) > 0
-
- // create runs of entries with equal values
- h.sort(less)
-
- // for each run, make a new run object and collect them in a new RunList
- var hh RunList
- i, x := 0, h[0]
- for j, y := range h {
- if less(x, y) {
- hh = append(hh, newRun(h[i:j]))
- i, x = j, h[j] // start a new run
- }
- }
- // add final run, if any
- if i < len(h) {
- hh = append(hh, newRun(h[i:]))
- }
-
- return hh
-}
-
-// ----------------------------------------------------------------------------
-// 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
-)
-
-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 }
-
-// ----------------------------------------------------------------------------
-// KindRun
-
-// Debugging support. Disable to see multiple entries per line.
-const removeDuplicates = true
-
-// A KindRun is a run of SpotInfos of the same kind in a given file.
-// The kind (3 bits) is stored in each SpotInfo element; to find the
-// kind of a KindRun, look at any of it's elements.
-type KindRun []SpotInfo
-
-// KindRuns are sorted by line number or index. Since the isIndex bit
-// is always the same for all infos in one list we can compare lori's.
-func (k KindRun) Len() int { return len(k) }
-func (k KindRun) Less(i, j int) bool { return k[i].Lori() < k[j].Lori() }
-func (k KindRun) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
-
-// FileRun contents are sorted by Kind for the reduction into KindRuns.
-func lessKind(x, y interface{}) bool { return x.(SpotInfo).Kind() < y.(SpotInfo).Kind() }
-
-// newKindRun allocates a new KindRun from the SpotInfo run h.
-func newKindRun(h RunList) interface{} {
- run := make(KindRun, len(h))
- for i, x := range h {
- run[i] = x.(SpotInfo)
- }
-
- // Spots were sorted by file and kind to create this run.
- // Within this run, sort them by line number or index.
- sort.Sort(run)
-
- if removeDuplicates {
- // Since both the lori and kind field must be
- // same for duplicates, and since the isIndex
- // bit is always the same for all infos in one
- // list we can simply compare the entire info.
- k := 0
- prev := SpotInfo(1<<32 - 1) // an unlikely value
- for _, x := range run {
- if x != prev {
- run[k] = x
- k++
- prev = x
- }
- }
- run = run[0:k]
- }
-
- return run
-}
-
-// ----------------------------------------------------------------------------
-// FileRun
-
-// A Pak describes a Go package.
-type Pak struct {
- Path string // path of directory containing the package
- Name string // package name as declared by package clause
-}
-
-// Paks are sorted by name (primary key) and by import path (secondary key).
-func (p *Pak) less(q *Pak) bool {
- return p.Name < q.Name || p.Name == q.Name && p.Path < q.Path
-}
-
-// A File describes a Go file.
-type File struct {
- Name string // directory-local file name
- Pak *Pak // the package to which the file belongs
-}
-
-// Path returns the file path of f.
-func (f *File) Path() string {
- return pathpkg.Join(f.Pak.Path, f.Name)
-}
-
-// A Spot describes a single occurrence of a word.
-type Spot struct {
- File *File
- Info SpotInfo
-}
-
-// A FileRun is a list of KindRuns belonging to the same file.
-type FileRun struct {
- File *File
- Groups []KindRun
-}
-
-// Spots are sorted by file path for the reduction into FileRuns.
-func lessSpot(x, y interface{}) bool {
- fx := x.(Spot).File
- fy := y.(Spot).File
- // same as "return fx.Path() < fy.Path()" but w/o computing the file path first
- px := fx.Pak.Path
- py := fy.Pak.Path
- return px < py || px == py && fx.Name < fy.Name
-}
-
-// newFileRun allocates a new FileRun from the Spot run h.
-func newFileRun(h RunList) interface{} {
- file := h[0].(Spot).File
-
- // reduce the list of Spots into a list of KindRuns
- h1 := make(RunList, len(h))
- for i, x := range h {
- h1[i] = x.(Spot).Info
- }
- h2 := h1.reduce(lessKind, newKindRun)
-
- // create the FileRun
- groups := make([]KindRun, len(h2))
- for i, x := range h2 {
- groups[i] = x.(KindRun)
- }
- return &FileRun{file, groups}
-}
-
-// ----------------------------------------------------------------------------
-// PakRun
-
-// A PakRun describes a run of *FileRuns of a package.
-type PakRun struct {
- Pak *Pak
- Files []*FileRun
-}
-
-// Sorting support for files within a PakRun.
-func (p *PakRun) Len() int { return len(p.Files) }
-func (p *PakRun) Less(i, j int) bool { return p.Files[i].File.Name < p.Files[j].File.Name }
-func (p *PakRun) Swap(i, j int) { p.Files[i], p.Files[j] = p.Files[j], p.Files[i] }
-
-// FileRuns are sorted by package for the reduction into PakRuns.
-func lessFileRun(x, y interface{}) bool {
- return x.(*FileRun).File.Pak.less(y.(*FileRun).File.Pak)
-}
-
-// newPakRun allocates a new PakRun from the *FileRun run h.
-func newPakRun(h RunList) interface{} {
- pak := h[0].(*FileRun).File.Pak
- files := make([]*FileRun, len(h))
- for i, x := range h {
- files[i] = x.(*FileRun)
- }
- run := &PakRun{pak, files}
- sort.Sort(run) // files were sorted by package; sort them by file now
- return run
-}
-
-// ----------------------------------------------------------------------------
-// HitList
-
-// A HitList describes a list of PakRuns.
-type HitList []*PakRun
-
-// PakRuns are sorted by package.
-func lessPakRun(x, y interface{}) bool { return x.(*PakRun).Pak.less(y.(*PakRun).Pak) }
-
-func reduce(h0 RunList) HitList {
- // reduce a list of Spots into a list of FileRuns
- h1 := h0.reduce(lessSpot, newFileRun)
- // reduce a list of FileRuns into a list of PakRuns
- h2 := h1.reduce(lessFileRun, newPakRun)
- // sort the list of PakRuns by package
- h2.sort(lessPakRun)
- // create a HitList
- h := make(HitList, len(h2))
- for i, p := range h2 {
- h[i] = p.(*PakRun)
- }
- return h
-}
-
-// filter returns a new HitList created by filtering
-// all PakRuns from h that have a matching pakname.
-func (h HitList) filter(pakname string) HitList {
- var hh HitList
- for _, p := range h {
- if p.Pak.Name == pakname {
- hh = append(hh, p)
- }
- }
- return hh
-}
-
-// ----------------------------------------------------------------------------
-// AltWords
-
-type wordPair struct {
- canon string // canonical word spelling (all lowercase)
- alt string // alternative spelling
-}
-
-// An AltWords describes a list of alternative spellings for a
-// canonical (all lowercase) spelling of a word.
-type AltWords struct {
- Canon string // canonical word spelling (all lowercase)
- Alts []string // alternative spelling for the same word
-}
-
-// wordPairs are sorted by their canonical spelling.
-func lessWordPair(x, y interface{}) bool { return x.(*wordPair).canon < y.(*wordPair).canon }
-
-// newAltWords allocates a new AltWords from the *wordPair run h.
-func newAltWords(h RunList) interface{} {
- canon := h[0].(*wordPair).canon
- alts := make([]string, len(h))
- for i, x := range h {
- alts[i] = x.(*wordPair).alt
- }
- return &AltWords{canon, alts}
-}
-
-func (a *AltWords) filter(s string) *AltWords {
- var alts []string
- for _, w := range a.Alts {
- if w != s {
- alts = append(alts, w)
- }
- }
- if len(alts) > 0 {
- return &AltWords{a.Canon, alts}
- }
- return nil
-}
-
-// ----------------------------------------------------------------------------
-// Indexer
-
-// Adjust these flags as seems best.
-const includeMainPackages = true
-const includeTestFiles = true
-
-type IndexResult struct {
- Decls RunList // package-level declarations (with snippets)
- Others RunList // all other occurrences
-}
-
-// Statistics provides statistics information for an index.
-type Statistics struct {
- Bytes int // total size of indexed source files
- Files int // number of indexed source files
- Lines int // number of lines (all files)
- Words int // number of different identifiers
- Spots int // number of identifier occurrences
-}
-
-// An Indexer maintains the data structures and provides the machinery
-// for indexing .go files under a file tree. It implements the path.Visitor
-// interface for walking file trees, and the ast.Visitor interface for
-// walking Go ASTs.
-type Indexer struct {
- fset *token.FileSet // file set for all indexed files
- sources bytes.Buffer // concatenated sources
- packages map[string]*Pak // map of canonicalized *Paks
- words map[string]*IndexResult // RunLists of Spots
- snippets []*Snippet // indices are stored in SpotInfos
- current *token.File // last file added to file set
- file *File // AST for current file
- decl ast.Decl // AST for current decl
- stats Statistics
-}
-
-func (x *Indexer) lookupPackage(path, name string) *Pak {
- // In the source directory tree, more than one package may
- // live in the same directory. For the packages map, construct
- // a key that includes both the directory path and the package
- // name.
- key := path + ":" + name
- pak := x.packages[key]
- if pak == nil {
- pak = &Pak{path, name}
- x.packages[key] = pak
- }
- return pak
-}
-
-func (x *Indexer) addSnippet(s *Snippet) int {
- index := len(x.snippets)
- x.snippets = append(x.snippets, s)
- return index
-}
-
-func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) {
- if id != nil {
- lists, found := x.words[id.Name]
- if !found {
- lists = new(IndexResult)
- x.words[id.Name] = lists
- }
-
- if kind == Use || x.decl == nil {
- // not a declaration or no snippet required
- info := makeSpotInfo(kind, x.current.Line(id.Pos()), false)
- lists.Others = append(lists.Others, Spot{x.file, info})
- } else {
- // a declaration with snippet
- index := x.addSnippet(NewSnippet(x.fset, x.decl, id))
- info := makeSpotInfo(kind, index, true)
- lists.Decls = append(lists.Decls, Spot{x.file, info})
- }
-
- x.stats.Spots++
- }
-}
-
-func (x *Indexer) visitFieldList(kind SpotKind, list *ast.FieldList) {
- for _, f := range list.List {
- x.decl = nil // no snippets for fields
- for _, name := range f.Names {
- x.visitIdent(kind, name)
- }
- ast.Walk(x, f.Type)
- // ignore tag - not indexed at the moment
- }
-}
-
-func (x *Indexer) visitSpec(kind SpotKind, spec ast.Spec) {
- switch n := spec.(type) {
- case *ast.ImportSpec:
- x.visitIdent(ImportDecl, n.Name)
- // ignore path - not indexed at the moment
-
- case *ast.ValueSpec:
- for _, n := range n.Names {
- x.visitIdent(kind, n)
- }
- ast.Walk(x, n.Type)
- for _, v := range n.Values {
- ast.Walk(x, v)
- }
-
- case *ast.TypeSpec:
- x.visitIdent(TypeDecl, n.Name)
- ast.Walk(x, n.Type)
- }
-}
-
-func (x *Indexer) visitGenDecl(decl *ast.GenDecl) {
- kind := VarDecl
- if decl.Tok == token.CONST {
- kind = ConstDecl
- }
- x.decl = decl
- for _, s := range decl.Specs {
- x.visitSpec(kind, s)
- }
-}
-
-func (x *Indexer) Visit(node ast.Node) ast.Visitor {
- switch n := node.(type) {
- case nil:
- // nothing to do
-
- case *ast.Ident:
- x.visitIdent(Use, n)
-
- case *ast.FieldList:
- x.visitFieldList(VarDecl, n)
-
- case *ast.InterfaceType:
- x.visitFieldList(MethodDecl, n.Methods)
-
- case *ast.DeclStmt:
- // local declarations should only be *ast.GenDecls;
- // ignore incorrect ASTs
- if decl, ok := n.Decl.(*ast.GenDecl); ok {
- x.decl = nil // no snippets for local declarations
- x.visitGenDecl(decl)
- }
-
- case *ast.GenDecl:
- x.decl = n
- x.visitGenDecl(n)
-
- case *ast.FuncDecl:
- kind := FuncDecl
- if n.Recv != nil {
- kind = MethodDecl
- ast.Walk(x, n.Recv)
- }
- x.decl = n
- x.visitIdent(kind, n.Name)
- ast.Walk(x, n.Type)
- if n.Body != nil {
- ast.Walk(x, n.Body)
- }
-
- case *ast.File:
- x.decl = nil
- x.visitIdent(PackageClause, n.Name)
- for _, d := range n.Decls {
- ast.Walk(x, d)
- }
-
- default:
- return x
- }
-
- return nil
-}
-
-func pkgName(filename string) string {
- // use a new file set each time in order to not pollute the indexer's
- // file set (which must stay in sync with the concatenated source code)
- file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
- if err != nil || file == nil {
- return ""
- }
- return file.Name.Name
-}
-
-// addFile adds a file to the index if possible and returns the file set file
-// and the file's AST if it was successfully parsed as a Go file. If addFile
-// failed (that is, if the file was not added), it returns file == nil.
-func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *ast.File) {
- // open file
- f, err := fs.Open(filename)
- if err != nil {
- return
- }
- defer f.Close()
-
- // The file set's base offset and x.sources size must be in lock-step;
- // this permits the direct mapping of suffix array lookup results to
- // to corresponding Pos values.
- //
- // When a file is added to the file set, its offset base increases by
- // the size of the file + 1; and the initial base offset is 1. Add an
- // extra byte to the sources here.
- x.sources.WriteByte(0)
-
- // If the sources length doesn't match the file set base at this point
- // the file set implementation changed or we have another error.
- base := x.fset.Base()
- if x.sources.Len() != base {
- panic("internal error: file base incorrect")
- }
-
- // append file contents (src) to x.sources
- if _, err := x.sources.ReadFrom(f); err == nil {
- src := x.sources.Bytes()[base:]
-
- if goFile {
- // parse the file and in the process add it to the file set
- if ast, err = parser.ParseFile(x.fset, filename, src, parser.ParseComments); err == nil {
- file = x.fset.File(ast.Pos()) // ast.Pos() is inside the file
- return
- }
- // file has parse errors, and the AST may be incorrect -
- // set lines information explicitly and index as ordinary
- // text file (cannot fall through to the text case below
- // because the file has already been added to the file set
- // by the parser)
- file = x.fset.File(token.Pos(base)) // token.Pos(base) is inside the file
- file.SetLinesForContent(src)
- ast = nil
- return
- }
-
- if isText(src) {
- // only add the file to the file set (for the full text index)
- file = x.fset.AddFile(filename, x.fset.Base(), len(src))
- file.SetLinesForContent(src)
- return
- }
- }
-
- // discard possibly added data
- x.sources.Truncate(base - 1) // -1 to remove added byte 0 since no file was added
- return
-}
-
-// Design note: Using an explicit white list of permitted files for indexing
-// makes sure that the important files are included and massively reduces the
-// number of files to index. The advantage over a blacklist is that unexpected
-// (non-blacklisted) files won't suddenly explode the index.
-
-// Files are whitelisted if they have a file name or extension
-// present as key in whitelisted.
-var whitelisted = map[string]bool{
- ".bash": true,
- ".c": true,
- ".cc": true,
- ".cpp": true,
- ".cxx": true,
- ".css": true,
- ".go": true,
- ".goc": true,
- ".h": true,
- ".hh": true,
- ".hpp": true,
- ".hxx": true,
- ".html": true,
- ".js": true,
- ".out": true,
- ".py": true,
- ".s": true,
- ".sh": true,
- ".txt": true,
- ".xml": true,
- "AUTHORS": true,
- "CONTRIBUTORS": true,
- "LICENSE": true,
- "Makefile": true,
- "PATENTS": true,
- "README": true,
-}
-
-// isWhitelisted returns true if a file is on the list
-// of "permitted" files for indexing. The filename must
-// be the directory-local name of the file.
-func isWhitelisted(filename string) bool {
- key := pathpkg.Ext(filename)
- if key == "" {
- // file has no extension - use entire filename
- key = filename
- }
- return whitelisted[key]
-}
-
-func (x *Indexer) visitFile(dirname string, f os.FileInfo, fulltextIndex bool) {
- if f.IsDir() {
- return
- }
-
- filename := pathpkg.Join(dirname, f.Name())
- goFile := false
-
- switch {
- case isGoFile(f):
- if !includeTestFiles && (!isPkgFile(f) || strings.HasPrefix(filename, "test/")) {
- return
- }
- if !includeMainPackages && pkgName(filename) == "main" {
- return
- }
- goFile = true
-
- case !fulltextIndex || !isWhitelisted(f.Name()):
- return
- }
-
- file, fast := x.addFile(filename, goFile)
- if file == nil {
- return // addFile failed
- }
-
- if fast != nil {
- // we've got a Go file to index
- x.current = file
- pak := x.lookupPackage(dirname, fast.Name.Name)
- x.file = &File{f.Name(), pak}
- ast.Walk(x, fast)
- }
-
- // update statistics
- x.stats.Bytes += file.Size()
- x.stats.Files++
- x.stats.Lines += file.LineCount()
-}
-
-// ----------------------------------------------------------------------------
-// Index
-
-type LookupResult struct {
- Decls HitList // package-level declarations (with snippets)
- Others HitList // all other occurrences
-}
-
-type Index struct {
- fset *token.FileSet // file set used during indexing; nil if no textindex
- suffixes *suffixarray.Index // suffixes for concatenated sources; nil if no textindex
- words map[string]*LookupResult // maps words to hit lists
- alts map[string]*AltWords // maps canonical(words) to lists of alternative spellings
- snippets []*Snippet // all snippets, indexed by snippet index
- stats Statistics
-}
-
-func canonical(w string) string { return strings.ToLower(w) }
-
-// NewIndex creates a new index for the .go files
-// in the directories given by dirnames.
-//
-func NewIndex(dirnames <-chan string, fulltextIndex bool, throttle float64) *Index {
- var x Indexer
- th := NewThrottle(throttle, 100*time.Millisecond) // run at least 0.1s at a time
-
- // initialize Indexer
- // (use some reasonably sized maps to start)
- x.fset = token.NewFileSet()
- x.packages = make(map[string]*Pak, 256)
- x.words = make(map[string]*IndexResult, 8192)
-
- // index all files in the directories given by dirnames
- for dirname := range dirnames {
- list, err := fs.ReadDir(dirname)
- if err != nil {
- continue // ignore this directory
- }
- for _, f := range list {
- if !f.IsDir() {
- x.visitFile(dirname, f, fulltextIndex)
- }
- th.Throttle()
- }
- }
-
- if !fulltextIndex {
- // the file set, the current file, and the sources are
- // not needed after indexing if no text index is built -
- // help GC and clear them
- x.fset = nil
- x.sources.Reset()
- x.current = nil // contains reference to fset!
- }
-
- // for each word, reduce the RunLists into a LookupResult;
- // also collect the word with its canonical spelling in a
- // word list for later computation of alternative spellings
- words := make(map[string]*LookupResult)
- var wlist RunList
- for w, h := range x.words {
- decls := reduce(h.Decls)
- others := reduce(h.Others)
- words[w] = &LookupResult{
- Decls: decls,
- Others: others,
- }
- wlist = append(wlist, &wordPair{canonical(w), w})
- th.Throttle()
- }
- x.stats.Words = len(words)
-
- // reduce the word list {canonical(w), w} into
- // a list of AltWords runs {canonical(w), {w}}
- alist := wlist.reduce(lessWordPair, newAltWords)
-
- // convert alist into a map of alternative spellings
- alts := make(map[string]*AltWords)
- for i := 0; i < len(alist); i++ {
- a := alist[i].(*AltWords)
- alts[a.Canon] = a
- }
-
- // create text index
- var suffixes *suffixarray.Index
- if fulltextIndex {
- suffixes = suffixarray.New(x.sources.Bytes())
- }
-
- return &Index{x.fset, suffixes, words, alts, x.snippets, x.stats}
-}
-
-type fileIndex struct {
- Words map[string]*LookupResult
- Alts map[string]*AltWords
- Snippets []*Snippet
- Fulltext bool
-}
-
-func (x *fileIndex) Write(w io.Writer) error {
- return gob.NewEncoder(w).Encode(x)
-}
-
-func (x *fileIndex) Read(r io.Reader) error {
- return gob.NewDecoder(r).Decode(x)
-}
-
-// Write writes the index x to w.
-func (x *Index) Write(w io.Writer) error {
- fulltext := false
- if x.suffixes != nil {
- fulltext = true
- }
- fx := fileIndex{
- x.words,
- x.alts,
- x.snippets,
- fulltext,
- }
- if err := fx.Write(w); err != nil {
- return err
- }
- if fulltext {
- encode := func(x interface{}) error {
- return gob.NewEncoder(w).Encode(x)
- }
- if err := x.fset.Write(encode); err != nil {
- return err
- }
- if err := x.suffixes.Write(w); err != nil {
- return err
- }
- }
- return nil
-}
-
-// Read reads the index from r into x; x must not be nil.
-// If r does not also implement io.ByteReader, it will be wrapped in a bufio.Reader.
-func (x *Index) Read(r io.Reader) error {
- // We use the ability to read bytes as a plausible surrogate for buffering.
- if _, ok := r.(io.ByteReader); !ok {
- r = bufio.NewReader(r)
- }
- var fx fileIndex
- if err := fx.Read(r); err != nil {
- return err
- }
- x.words = fx.Words
- x.alts = fx.Alts
- x.snippets = fx.Snippets
- if fx.Fulltext {
- x.fset = token.NewFileSet()
- decode := func(x interface{}) error {
- return gob.NewDecoder(r).Decode(x)
- }
- if err := x.fset.Read(decode); err != nil {
- return err
- }
- x.suffixes = new(suffixarray.Index)
- if err := x.suffixes.Read(r); err != nil {
- return err
- }
- }
- return nil
-}
-
-// Stats() returns index statistics.
-func (x *Index) Stats() Statistics {
- return x.stats
-}
-
-func (x *Index) lookupWord(w string) (match *LookupResult, alt *AltWords) {
- match = x.words[w]
- alt = x.alts[canonical(w)]
- // remove current spelling from alternatives
- // (if there is no match, the alternatives do
- // not contain the current spelling)
- if match != nil && alt != nil {
- alt = alt.filter(w)
- }
- return
-}
-
-// isIdentifier reports whether s is a Go identifier.
-func isIdentifier(s string) bool {
- for i, ch := range s {
- if unicode.IsLetter(ch) || ch == ' ' || i > 0 && unicode.IsDigit(ch) {
- continue
- }
- return false
- }
- return len(s) > 0
-}
-
-// For a given query, which is either a single identifier or a qualified
-// identifier, Lookup returns a list of packages, a LookupResult, and a
-// list of alternative spellings, if any. Any and all results may be nil.
-// If the query syntax is wrong, an error is reported.
-func (x *Index) Lookup(query string) (paks HitList, match *LookupResult, alt *AltWords, err error) {
- ss := strings.Split(query, ".")
-
- // check query syntax
- for _, s := range ss {
- if !isIdentifier(s) {
- err = errors.New("all query parts must be identifiers")
- return
- }
- }
-
- // handle simple and qualified identifiers
- switch len(ss) {
- case 1:
- ident := ss[0]
- match, alt = x.lookupWord(ident)
- if match != nil {
- // found a match - filter packages with same name
- // for the list of packages called ident, if any
- paks = match.Others.filter(ident)
- }
-
- case 2:
- pakname, ident := ss[0], ss[1]
- match, alt = x.lookupWord(ident)
- if match != nil {
- // found a match - filter by package name
- // (no paks - package names are not qualified)
- decls := match.Decls.filter(pakname)
- others := match.Others.filter(pakname)
- match = &LookupResult{decls, others}
- }
-
- default:
- err = errors.New("query is not a (qualified) identifier")
- }
-
- return
-}
-
-func (x *Index) Snippet(i int) *Snippet {
- // handle illegal snippet indices gracefully
- if 0 <= i && i < len(x.snippets) {
- return x.snippets[i]
- }
- return nil
-}
-
-type positionList []struct {
- filename string
- line int
-}
-
-func (list positionList) Len() int { return len(list) }
-func (list positionList) Less(i, j int) bool { return list[i].filename < list[j].filename }
-func (list positionList) Swap(i, j int) { list[i], list[j] = list[j], list[i] }
-
-// unique returns the list sorted and with duplicate entries removed
-func unique(list []int) []int {
- sort.Ints(list)
- var last int
- i := 0
- for _, x := range list {
- if i == 0 || x != last {
- last = x
- list[i] = x
- i++
- }
- }
- return list[0:i]
-}
-
-// A FileLines value specifies a file and line numbers within that file.
-type FileLines struct {
- Filename string
- Lines []int
-}
-
-// LookupRegexp returns the number of matches and the matches where a regular
-// expression r is found in the full text index. At most n matches are
-// returned (thus found <= n).
-//
-func (x *Index) LookupRegexp(r *regexp.Regexp, n int) (found int, result []FileLines) {
- if x.suffixes == nil || n <= 0 {
- return
- }
- // n > 0
-
- var list positionList
- // FindAllIndex may returns matches that span across file boundaries.
- // Such matches are unlikely, buf after eliminating them we may end up
- // with fewer than n matches. If we don't have enough at the end, redo
- // the search with an increased value n1, but only if FindAllIndex
- // returned all the requested matches in the first place (if it
- // returned fewer than that there cannot be more).
- for n1 := n; found < n; n1 += n - found {
- found = 0
- matches := x.suffixes.FindAllIndex(r, n1)
- // compute files, exclude matches that span file boundaries,
- // and map offsets to file-local offsets
- list = make(positionList, len(matches))
- for _, m := range matches {
- // by construction, an offset corresponds to the Pos value
- // for the file set - use it to get the file and line
- p := token.Pos(m[0])
- if file := x.fset.File(p); file != nil {
- if base := file.Base(); base <= m[1] && m[1] <= base+file.Size() {
- // match [m[0], m[1]) is within the file boundaries
- list[found].filename = file.Name()
- list[found].line = file.Line(p)
- found++
- }
- }
- }
- if found == n || len(matches) < n1 {
- // found all matches or there's no chance to find more
- break
- }
- }
- list = list[0:found]
- sort.Sort(list) // sort by filename
-
- // collect matches belonging to the same file
- var last string
- var lines []int
- addLines := func() {
- if len(lines) > 0 {
- // remove duplicate lines
- result = append(result, FileLines{last, unique(lines)})
- lines = nil
- }
- }
- for _, m := range list {
- if m.filename != last {
- addLines()
- last = m.filename
- }
- lines = append(lines, m.line)
- }
- addLines()
-
- return
-}
diff --git a/src/cmd/godoc/linkify.go b/src/cmd/godoc/linkify.go
deleted file mode 100644
index 7213abb480..0000000000
--- a/src/cmd/godoc/linkify.go
+++ /dev/null
@@ -1,234 +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.
-
-// 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 main
-
-import (
- "fmt"
- "go/ast"
- "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.mode == identVal {
- 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 {
- mode identMode
- path, name string // package path, identifier name
-}
-
-// 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) (list []link) {
- modes := identModesFor(node)
-
- // NOTE: We are expecting ast.Inspect to call the
- // callback function in source text order.
- ast.Inspect(node, func(node ast.Node) bool {
- switch n := node.(type) {
- case *ast.Ident:
- m := modes[n]
- info := link{mode: m}
- switch m {
- case identUse:
- if n.Obj == nil && predeclared[n.Name] {
- info.path = builtinPkgPath
- }
- info.name = n.Name
- case identDef:
- // any declaration expect const or var - empty link
- case identVal:
- // const or var declaration
- info.name = n.Name
- }
- list = append(list, info)
- return false
- 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 {
- // x must be a package for 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.
- info := link{path: path}
- list = append(list, info)
- info.name = n.Sel.Name
- list = append(list, info)
- return false
- }
- }
- }
- }
- }
- return true
- })
-
- return
-}
-
-// The identMode describes how an identifier is "used" at its source location.
-type identMode int
-
-const (
- identUse identMode = iota // identifier is used (must be zero value for identMode)
- identDef // identifier is defined
- identVal // identifier is defined in a const or var declaration
-)
-
-// identModesFor returns a map providing the identMode for each identifier used by node.
-func identModesFor(node ast.Node) map[*ast.Ident]identMode {
- m := make(map[*ast.Ident]identMode)
-
- ast.Inspect(node, func(node ast.Node) bool {
- switch n := node.(type) {
- case *ast.Field:
- for _, n := range n.Names {
- m[n] = identDef
- }
- case *ast.ImportSpec:
- if name := n.Name; name != nil {
- m[name] = identDef
- }
- case *ast.ValueSpec:
- for _, n := range n.Names {
- m[n] = identVal
- }
- case *ast.TypeSpec:
- m[n.Name] = identDef
- case *ast.FuncDecl:
- m[n.Name] = identDef
- 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 {
- m[n] = identVal
- }
- }
- }
- }
- return true
- })
-
- return m
-}
-
-// The predeclared map represents the set of all predeclared identifiers.
-// TODO(gri) This information is also encoded in similar maps in go/doc,
-// but not exported. Consider exporting an accessor and using
-// it instead.
-var predeclared = map[string]bool{
- "bool": true,
- "byte": true,
- "complex64": true,
- "complex128": true,
- "error": true,
- "float32": true,
- "float64": true,
- "int": true,
- "int8": true,
- "int16": true,
- "int32": true,
- "int64": true,
- "rune": true,
- "string": true,
- "uint": true,
- "uint8": true,
- "uint16": true,
- "uint32": true,
- "uint64": true,
- "uintptr": true,
- "true": true,
- "false": true,
- "iota": true,
- "nil": true,
- "append": true,
- "cap": true,
- "close": true,
- "complex": true,
- "copy": true,
- "delete": true,
- "imag": true,
- "len": true,
- "make": true,
- "new": true,
- "panic": true,
- "print": true,
- "println": true,
- "real": true,
- "recover": true,
-}
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
deleted file mode 100644
index 81e739d20c..0000000000
--- a/src/cmd/godoc/main.go
+++ /dev/null
@@ -1,470 +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.
-
-// godoc: Go Documentation Server
-
-// Web server tree:
-//
-// http://godoc/ main landing page
-// http://godoc/doc/ serve from $GOROOT/doc - spec, mem, etc.
-// http://godoc/src/ serve files from $GOROOT/src; .go gets pretty-printed
-// http://godoc/cmd/ serve documentation about commands
-// http://godoc/pkg/ serve documentation about packages
-// (idea is if you say import "compress/zlib", you go to
-// http://godoc/pkg/compress/zlib)
-//
-// Command-line interface:
-//
-// godoc packagepath [name ...]
-//
-// godoc compress/zlib
-// - prints doc for package compress/zlib
-// godoc crypto/block Cipher NewCMAC
-// - prints doc for Cipher and NewCMAC in package crypto/block
-
-// +build !appengine
-
-package main
-
-import (
- "archive/zip"
- "bytes"
- "errors"
- _ "expvar" // to serve /debug/vars
- "flag"
- "fmt"
- "go/ast"
- "go/build"
- "go/printer"
- "io"
- "log"
- "net/http"
- _ "net/http/pprof" // to serve /debug/pprof/*
- "net/url"
- "os"
- pathpkg "path"
- "path/filepath"
- "regexp"
- "runtime"
- "strings"
-)
-
-const defaultAddr = ":6060" // default webserver address
-
-var (
- // file system to serve
- // (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico)
- zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty")
-
- // file-based index
- writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files")
-
- // network
- httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')")
- serverAddr = flag.String("server", "", "webserver address for command line searches")
-
- // 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")
- urlFlag = flag.String("url", "", "print HTML for named URL")
-
- // command-line searches
- query = flag.Bool("q", false, "arguments are considered search queries")
-)
-
-func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
- w.WriteHeader(http.StatusNotFound)
- servePage(w, Page{
- Title: "File " + relpath,
- Subtitle: relpath,
- Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path!
- })
-}
-
-func usage() {
- fmt.Fprintf(os.Stderr,
- "usage: godoc package [name ...]\n"+
- " godoc -http="+defaultAddr+"\n")
- flag.PrintDefaults()
- os.Exit(2)
-}
-
-func loggingHandler(h http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- log.Printf("%s\t%s", req.RemoteAddr, req.URL)
- h.ServeHTTP(w, req)
- })
-}
-
-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
-}
-
-// Does s look like a regular expression?
-func isRegexp(s string) bool {
- return strings.IndexAny(s, ".(|)*+?^$[]") >= 0
-}
-
-// Make a regular expression of the form
-// names[0]|names[1]|...names[len(names)-1].
-// Returns nil if the regular expression is illegal.
-func makeRx(names []string) (rx *regexp.Regexp) {
- if len(names) > 0 {
- s := ""
- for i, name := range names {
- if i > 0 {
- s += "|"
- }
- if isRegexp(name) {
- s += name
- } else {
- s += "^" + name + "$" // must match exactly
- }
- }
- rx, _ = regexp.Compile(s) // rx is nil if there's a compilation error
- }
- return
-}
-
-func main() {
- flag.Usage = usage
- flag.Parse()
-
- // Check usage: either server and no args, command line and args, or index creation mode
- if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex {
- usage()
- }
-
- if *tabwidth < 0 {
- log.Fatalf("negative tabwidth %d", *tabwidth)
- }
-
- // Determine file system to use.
- // TODO(gri) - fs and fsHttp should really be the same. Try to unify.
- // - fsHttp doesn't need to be set up in command-line mode,
- // same is true for the http handlers in initHandlers.
- if *zipfile == "" {
- // use file system of underlying OS
- fs.Bind("/", OS(*goroot), "/", bindReplace)
- if *templateDir != "" {
- fs.Bind("/lib/godoc", OS(*templateDir), "/", bindBefore)
- }
- } else {
- // use file system specified via .zip file (path separator must be '/')
- rc, err := zip.OpenReader(*zipfile)
- if err != nil {
- log.Fatalf("%s: %s\n", *zipfile, err)
- }
- defer rc.Close() // be nice (e.g., -writeIndex mode)
- fs.Bind("/", NewZipFS(rc, *zipfile), *goroot, bindReplace)
- }
-
- // Bind $GOPATH trees into Go root.
- for _, p := range filepath.SplitList(build.Default.GOPATH) {
- fs.Bind("/src/pkg", OS(p), "/src", bindAfter)
- }
-
- readTemplates()
- initHandlers()
-
- if *writeIndex {
- // Write search index and exit.
- if *indexFiles == "" {
- log.Fatal("no index file specified")
- }
-
- log.Println("initialize file systems")
- *verbose = true // want to see what happens
- initFSTree()
-
- *indexThrottle = 1
- updateIndex()
-
- log.Println("writing index file", *indexFiles)
- f, err := os.Create(*indexFiles)
- if err != nil {
- log.Fatal(err)
- }
- index, _ := searchIndex.get()
- err = index.(*Index).Write(f)
- if err != nil {
- log.Fatal(err)
- }
-
- log.Println("done")
- return
- }
-
- // Print content that would be served at the URL *urlFlag.
- if *urlFlag != "" {
- registerPublicHandlers(http.DefaultServeMux)
- initFSTree()
- updateMetadata()
- // Try up to 10 fetches, following redirects.
- urlstr := *urlFlag
- for i := 0; i < 10; i++ {
- // Prepare request.
- u, err := url.Parse(urlstr)
- if err != nil {
- log.Fatal(err)
- }
- req := &http.Request{
- URL: u,
- }
-
- // Invoke default HTTP handler to serve request
- // to our buffering httpWriter.
- w := &httpWriter{h: http.Header{}, code: 200}
- http.DefaultServeMux.ServeHTTP(w, req)
-
- // Return data, error, or follow redirect.
- switch w.code {
- case 200: // ok
- os.Stdout.Write(w.Bytes())
- return
- case 301, 302, 303, 307: // redirect
- redirect := w.h.Get("Location")
- if redirect == "" {
- log.Fatalf("HTTP %d without Location header", w.code)
- }
- urlstr = redirect
- default:
- log.Fatalf("HTTP error %d", w.code)
- }
- }
- log.Fatalf("too many redirects")
- }
-
- if *httpAddr != "" {
- // 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")
- }
- fs.Fprint(os.Stderr)
- handler = loggingHandler(handler)
- }
-
- registerPublicHandlers(http.DefaultServeMux)
- registerPlaygroundHandlers(http.DefaultServeMux)
-
- // Initialize default directory tree with corresponding timestamp.
- // (Do it in a goroutine so that launch is quick.)
- go initFSTree()
-
- // Immediately update metadata.
- updateMetadata()
- // Periodically refresh metadata.
- go refreshMetadataLoop()
-
- // Initialize search index.
- if *indexEnabled {
- go indexer()
- }
-
- // Start http server.
- if err := http.ListenAndServe(*httpAddr, handler); err != nil {
- log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
- }
-
- return
- }
-
- // Command line mode.
- if *html {
- packageText = packageHTML
- searchText = packageHTML
- }
-
- if *query {
- // 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
- }
-
- // Determine paths.
- //
- // 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.
- const target = "/target"
- const cmdPrefix = "cmd/"
- path := flag.Arg(0)
- var forceCmd bool
- var abspath, relpath string
- if filepath.IsAbs(path) {
- fs.Bind(target, OS(path), "/", bindReplace)
- abspath = target
- } else if build.IsLocalImport(path) {
- cwd, _ := os.Getwd() // ignore errors
- path = filepath.Join(cwd, path)
- fs.Bind(target, OS(path), "/", bindReplace)
- abspath = target
- } else if strings.HasPrefix(path, cmdPrefix) {
- path = strings.TrimPrefix(path, cmdPrefix)
- forceCmd = true
- } else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" {
- fs.Bind(target, OS(bp.Dir), "/", bindReplace)
- abspath = target
- relpath = bp.ImportPath
- } else {
- abspath = pathpkg.Join(pkgHandler.fsRoot, path)
- }
- if relpath == "" {
- relpath = abspath
- }
-
- var mode PageInfoMode
- if relpath == builtinPkgPath {
- // the fake built-in package contains unexported identifiers
- mode = noFiltering
- }
- if *srcMode {
- // only filter exports if we don't have explicit command-line filter arguments
- if flag.NArg() > 1 {
- mode |= noFiltering
- }
- mode |= showSource
- }
-
- // first, try as package unless forced as command
- var info *PageInfo
- if !forceCmd {
- info = pkgHandler.getPageInfo(abspath, relpath, mode)
- }
-
- // second, try as command unless the path is absolute
- // (the go command invokes godoc w/ absolute paths; don't override)
- var cinfo *PageInfo
- if !filepath.IsAbs(path) {
- abspath = pathpkg.Join(cmdHandler.fsRoot, path)
- cinfo = cmdHandler.getPageInfo(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 {
- fmt.Printf("use 'godoc %s%s' for documentation on the %s command \n\n", cmdPrefix, relpath, relpath)
- }
- }
-
- if info == nil {
- log.Fatalf("%s: no such directory or package", flag.Arg(0))
- }
- if info.Err != nil {
- log.Fatalf("%v", info.Err)
- }
-
- if info.PDoc != nil && info.PDoc.ImportPath == target {
- // Replace virtual /target with actual argument from command line.
- info.PDoc.ImportPath = flag.Arg(0)
- }
-
- // If we have more than one argument, use the remaining arguments for filtering.
- if flag.NArg() > 1 {
- args := flag.Args()[1:]
- rx := makeRx(args)
- if rx == nil {
- log.Fatalf("illegal regular expression from %v", args)
- }
-
- filter := func(s string) bool { return rx.MatchString(s) }
- switch {
- case info.PAst != nil:
- cmap := ast.NewCommentMap(info.FSet, info.PAst, info.PAst.Comments)
- ast.FilterFile(info.PAst, filter)
- // Special case: Don't use templates for printing
- // so we only get the filtered declarations without
- // package clause or extra whitespace.
- for i, d := range info.PAst.Decls {
- // determine the comments associated with d only
- comments := cmap.Filter(d).Comments()
- cn := &printer.CommentedNode{Node: d, Comments: comments}
- if i > 0 {
- fmt.Println()
- }
- if *html {
- var buf bytes.Buffer
- writeNode(&buf, info.FSet, cn)
- FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil)
- } else {
- writeNode(os.Stdout, info.FSet, cn)
- }
- fmt.Println()
- }
- return
-
- case info.PDoc != nil:
- info.PDoc.Filter(filter)
- }
- }
-
- if err := packageText.Execute(os.Stdout, info); err != nil {
- log.Printf("packageText.Execute: %s", err)
- }
-}
-
-// An httpWriter is an http.ResponseWriter writing to a bytes.Buffer.
-type httpWriter struct {
- bytes.Buffer
- h http.Header
- code int
-}
-
-func (w *httpWriter) Header() http.Header { return w.h }
-func (w *httpWriter) WriteHeader(code int) { w.code = code }
diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go
deleted file mode 100644
index 42a5d2d982..0000000000
--- a/src/cmd/godoc/parser.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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 main
-
-import (
- "go/ast"
- "go/parser"
- "go/token"
- pathpkg "path"
-)
-
-func parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) {
- src, err := ReadFile(fs, filename)
- if err != nil {
- return nil, err
- }
- return parser.ParseFile(fset, filename, src, mode)
-}
-
-func parseFiles(fset *token.FileSet, 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 := parseFile(fset, absname, parser.ParseComments)
- if err != nil {
- return nil, err
- }
- files[absname] = file
- }
-
- return files, nil
-}
diff --git a/src/cmd/godoc/play-appengine.go b/src/cmd/godoc/play-appengine.go
deleted file mode 100644
index 9e351d1a25..0000000000
--- a/src/cmd/godoc/play-appengine.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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.
-
-// App Engine godoc Playground functionality.
-
-// +build appengine
-
-package main
-
-import (
- "io"
- "net/http"
-
- "appengine"
- "appengine/urlfetch"
-)
-
-func bounceToPlayground(w http.ResponseWriter, req *http.Request) {
- c := appengine.NewContext(req)
- client := urlfetch.Client(c)
- url := playgroundBaseURL + req.URL.Path
- defer req.Body.Close()
- resp, err := client.Post(url, req.Header.Get("Content-type"), req.Body)
- if err != nil {
- http.Error(w, "Internal Server Error", 500)
- c.Errorf("making POST request: %v", err)
- return
- }
- defer resp.Body.Close()
- if _, err := io.Copy(w, resp.Body); err != nil {
- http.Error(w, "Internal Server Error", 500)
- c.Errorf("making POST request: %v", err)
- }
-}
diff --git a/src/cmd/godoc/play-local.go b/src/cmd/godoc/play-local.go
deleted file mode 100644
index 637ce5e1a5..0000000000
--- a/src/cmd/godoc/play-local.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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.
-
-// Stand-alone godoc Playground functionality.
-
-// +build !appengine
-
-package main
-
-import (
- "io"
- "net/http"
- "net/url"
-)
-
-var playgroundScheme, playgroundHost string
-
-func init() {
- u, err := url.Parse(playgroundBaseURL)
- if err != nil {
- panic(err)
- }
- playgroundScheme = u.Scheme
- playgroundHost = u.Host
-}
-
-// bounceToPlayground forwards the request to play.golang.org.
-func bounceToPlayground(w http.ResponseWriter, req *http.Request) {
- defer req.Body.Close()
- req.URL.Scheme = playgroundScheme
- req.URL.Host = playgroundHost
- resp, err := http.Post(req.URL.String(), req.Header.Get("Content-type"), req.Body)
- if err != nil {
- http.Error(w, err.Error(), 500)
- return
- }
- w.WriteHeader(resp.StatusCode)
- io.Copy(w, resp.Body)
- resp.Body.Close()
-}
diff --git a/src/cmd/godoc/play.go b/src/cmd/godoc/play.go
deleted file mode 100644
index 47a11f6c0b..0000000000
--- a/src/cmd/godoc/play.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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.
-
-// Common Playground functionality.
-
-package main
-
-import (
- "encoding/json"
- "fmt"
- "go/format"
- "net/http"
-)
-
-// The server that will service compile and share requests.
-const playgroundBaseURL = "http://play.golang.org"
-
-func registerPlaygroundHandlers(mux *http.ServeMux) {
- if *showPlayground {
- mux.HandleFunc("/compile", bounceToPlayground)
- mux.HandleFunc("/share", bounceToPlayground)
- } else {
- mux.HandleFunc("/compile", disabledHandler)
- mux.HandleFunc("/share", disabledHandler)
- }
- http.HandleFunc("/fmt", fmtHandler)
-}
-
-type fmtResponse struct {
- Body string
- Error string
-}
-
-// fmtHandler takes a Go program in its "body" form value, formats it with
-// standard gofmt formatting, and writes a fmtResponse as a JSON object.
-func fmtHandler(w http.ResponseWriter, r *http.Request) {
- resp := new(fmtResponse)
- body, err := format.Source([]byte(r.FormValue("body")))
- if err != nil {
- resp.Error = err.Error()
- } else {
- resp.Body = string(body)
- }
- json.NewEncoder(w).Encode(resp)
-}
-
-// disabledHandler serves a 501 "Not Implemented" response.
-func disabledHandler(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusNotImplemented)
- fmt.Fprint(w, "This functionality is not available via local godoc.")
-}
diff --git a/src/cmd/godoc/setup-godoc-app.bash b/src/cmd/godoc/setup-godoc-app.bash
deleted file mode 100755
index 792e0d450b..0000000000
--- a/src/cmd/godoc/setup-godoc-app.bash
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/usr/bin/env bash
-
-# 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 script creates a complete godoc app in $APPDIR.
-# It copies the cmd/godoc and src/pkg/go/... sources from GOROOT,
-# synthesizes an app.yaml file, and creates the .zip, index, and
-# configuration files.
-#
-# If an argument is provided it is assumed to be the app-engine godoc directory.
-# Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env"
-# is consulted to find the $GOROOT.
-#
-# The script creates a .zip file representing the $GOROOT file system
-# and computes the correspondig search index files. These files are then
-# copied to $APPDIR. A corresponding godoc configuration file is created
-# in $APPDIR/appconfig.go.
-
-ZIPFILE=godoc.zip
-INDEXFILE=godoc.index
-SPLITFILES=index.split.
-CONFIGFILE=godoc/appconfig.go
-
-error() {
- echo "error: $1"
- exit 2
-}
-
-getArgs() {
- if [ -z $GOROOT ]; then
- GOROOT=$(go env GOROOT)
- echo "GOROOT not set explicitly, using $GOROOT instead"
- fi
- if [ -z $APPDIR ]; then
- if [ $# == 0 ]; then
- error "APPDIR not set, and no argument provided"
- fi
- APPDIR=$1
- echo "APPDIR not set, using argument instead"
- fi
-
- # safety checks
- if [ ! -d $GOROOT ]; then
- error "$GOROOT is not a directory"
- fi
- if [ ! -x $GOROOT/bin/godoc ]; then
- error "$GOROOT/bin/godoc does not exist or is not executable"
- fi
- if [ -e $APPDIR ]; then
- error "$APPDIR exists; check and remove it before trying again"
- fi
-
- # reporting
- echo "GOROOT = $GOROOT"
- echo "APPDIR = $APPDIR"
-}
-
-copyGodoc() {
- echo "*** copy $GOROOT/src/cmd/godoc to $APPDIR/godoc"
- cp -r $GOROOT/src/cmd/godoc $APPDIR/godoc
-}
-
-copyGoPackages() {
- echo "*** copy $GOROOT/src/pkg/go to $APPDIR/newgo and rewrite imports"
- cp -r $GOROOT/src/pkg/go $APPDIR/newgo
- find $APPDIR/newgo -type d -name testdata | xargs rm -r
- gofiles=$(find $APPDIR -name '*.go')
- sed -i '' 's_^\(."\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles
- sed -i '' 's_^\(import "\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles
-}
-
-makeAppYaml() {
- echo "*** make $APPDIR/app.yaml"
- cat > $APPDIR/app.yaml <<EOF
-application: godoc
-version: 1
-runtime: go
-api_version: go1
-
-handlers:
-- url: /.*
- script: _go_app
-EOF
-}
-
-makeZipfile() {
- echo "*** make $APPDIR/$ZIPFILE"
- zip -q -r $APPDIR/$ZIPFILE $GOROOT -i \*.go -i \*.html -i \*.xml -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i \*.ico
-}
-
-makeIndexfile() {
- echo "*** make $APPDIR/$INDEXFILE"
- OUT=/tmp/godoc.out
- $GOROOT/bin/godoc -write_index -index_files=$APPDIR/$INDEXFILE -zip=$APPDIR/$ZIPFILE 2> $OUT
- if [ $? != 0 ]; then
- error "$GOROOT/bin/godoc failed - see $OUT for details"
- fi
-}
-
-splitIndexfile() {
- echo "*** split $APPDIR/$INDEXFILE"
- split -b8m $APPDIR/$INDEXFILE $APPDIR/$SPLITFILES
-}
-
-makeConfigfile() {
- echo "*** make $APPDIR/$CONFIGFILE"
- cat > $APPDIR/$CONFIGFILE <<EOF
-package main
-
-// GENERATED FILE - DO NOT MODIFY BY HAND.
-// (generated by $GOROOT/src/cmd/godoc/setup-godoc-app.bash)
-
-const (
- // .zip filename
- zipFilename = "$ZIPFILE"
-
- // goroot directory in .zip file
- zipGoroot = "$GOROOT"
-
- // glob pattern describing search index files
- // (if empty, the index is built at run-time)
- indexFilenames = "$SPLITFILES*"
-)
-EOF
-}
-
-getArgs "$@"
-set -e
-mkdir $APPDIR
-copyGodoc
-copyGoPackages
-makeAppYaml
-makeZipfile
-makeIndexfile
-splitIndexfile
-makeConfigfile
-
-echo "*** setup complete"
diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go
deleted file mode 100644
index b482b74879..0000000000
--- a/src/cmd/godoc/snippet.go
+++ /dev/null
@@ -1,112 +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.
-
-// This file contains the infrastructure to create a code
-// snippet for search results.
-//
-// Note: At the moment, this only creates HTML snippets.
-
-package main
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/token"
-)
-
-type Snippet struct {
- Line int
- Text string // HTML-escaped
-}
-
-func 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
- 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 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 newSnippet(fset, dd, id)
-}
-
-func 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 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) (s *Snippet) {
- switch d := decl.(type) {
- case *ast.GenDecl:
- s = genSnippet(fset, d, id)
- case *ast.FuncDecl:
- s = 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
-}
diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go
deleted file mode 100644
index c11f25d20b..0000000000
--- a/src/cmd/godoc/spec.go
+++ /dev/null
@@ -1,179 +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.
-
-package main
-
-// 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:
- 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:]
- }
-}
diff --git a/src/cmd/godoc/template.go b/src/cmd/godoc/template.go
deleted file mode 100644
index 7b9b9cfeb0..0000000000
--- a/src/cmd/godoc/template.go
+++ /dev/null
@@ -1,182 +0,0 @@
-// 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 main
-
-import (
- "bytes"
- "fmt"
- "log"
- "regexp"
- "strings"
- "text/template"
-)
-
-// Functions in this file panic on error, but the panic is recovered
-// to an error by 'code'.
-
-var templateFuncs = template.FuncMap{
- "code": 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 contents(name string) string {
- file, err := ReadFile(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 code(file string, arg ...interface{}) (s string, err error) {
- defer func() {
- if r := recover(); r != nil {
- err = fmt.Errorf("%v", r)
- }
- }()
-
- text := 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 = oneLine(file, text, arg[0])
- case 2:
- command = fmt.Sprintf("code %q %s %s", file, stringFor(arg[0]), stringFor(arg[1]))
- text = multipleLines(file, text, arg[0], arg[1])
- default:
- return "", fmt.Errorf("incorrect code invocation: code %q %q", file, 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 oneLine(file, text string, arg interface{}) string {
- lines := strings.SplitAfter(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 multipleLines(file, text string, arg1, arg2 interface{}) string {
- lines := strings.SplitAfter(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
-}
diff --git a/src/cmd/godoc/throttle.go b/src/cmd/godoc/throttle.go
deleted file mode 100644
index ac18b44e0e..0000000000
--- a/src/cmd/godoc/throttle.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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 main
-
-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)
-}
diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go
deleted file mode 100644
index 0cdb7ff7af..0000000000
--- a/src/cmd/godoc/utils.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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 support functionality for godoc.
-
-package main
-
-import (
- pathpkg "path"
- "sync"
- "time"
- "unicode/utf8"
-)
-
-// 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 returns true if 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 returns true if 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(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])
-}
diff --git a/src/cmd/godoc/zip.go b/src/cmd/godoc/zip.go
deleted file mode 100644
index 620eb4f3cc..0000000000
--- a/src/cmd/godoc/zip.go
+++ /dev/null
@@ -1,236 +0,0 @@
-// 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 provides an implementation of the FileSystem
-// interface based on the contents of a .zip file.
-//
-// Assumptions:
-//
-// - The file paths stored in the zip file must use a slash ('/') as path
-// separator; and they must be relative (i.e., they must not start with
-// a '/' - this is usually the case if the file was created w/o special
-// options).
-// - The zip file system treats the file paths found in the zip internally
-// like absolute paths w/o a leading '/'; i.e., the paths are considered
-// relative to the root of the file system.
-// - All path arguments to file system methods must be absolute paths.
-
-package main
-
-import (
- "archive/zip"
- "fmt"
- "io"
- "os"
- "path"
- "sort"
- "strings"
- "time"
-)
-
-// zipFI is the zip-file based implementation of FileInfo
-type zipFI struct {
- name string // directory-local name
- file *zip.File // nil for a directory
-}
-
-func (fi zipFI) Name() string {
- return fi.name
-}
-
-func (fi zipFI) Size() int64 {
- if f := fi.file; f != nil {
- return int64(f.UncompressedSize)
- }
- return 0 // directory
-}
-
-func (fi zipFI) ModTime() time.Time {
- if f := fi.file; f != nil {
- return f.ModTime()
- }
- return time.Time{} // directory has no modified time entry
-}
-
-func (fi zipFI) Mode() os.FileMode {
- if fi.file == nil {
- // Unix directories typically are executable, hence 555.
- return os.ModeDir | 0555
- }
- return 0444
-}
-
-func (fi zipFI) IsDir() bool {
- return fi.file == nil
-}
-
-func (fi zipFI) Sys() interface{} {
- return nil
-}
-
-// zipFS is the zip-file based implementation of FileSystem
-type zipFS struct {
- *zip.ReadCloser
- list zipList
- name string
-}
-
-func (fs *zipFS) String() string {
- return "zip(" + fs.name + ")"
-}
-
-func (fs *zipFS) Close() error {
- fs.list = nil
- return fs.ReadCloser.Close()
-}
-
-func zipPath(name string) string {
- name = path.Clean(name)
- if !path.IsAbs(name) {
- panic(fmt.Sprintf("stat: not an absolute path: %s", name))
- }
- return name[1:] // strip leading '/'
-}
-
-func (fs *zipFS) stat(abspath string) (int, zipFI, error) {
- i, exact := fs.list.lookup(abspath)
- if i < 0 {
- // abspath has leading '/' stripped - print it explicitly
- return -1, zipFI{}, fmt.Errorf("file not found: /%s", abspath)
- }
- _, name := path.Split(abspath)
- var file *zip.File
- if exact {
- file = fs.list[i] // exact match found - must be a file
- }
- return i, zipFI{name, file}, nil
-}
-
-func (fs *zipFS) Open(abspath string) (readSeekCloser, error) {
- _, fi, err := fs.stat(zipPath(abspath))
- if err != nil {
- return nil, err
- }
- if fi.IsDir() {
- return nil, fmt.Errorf("Open: %s is a directory", abspath)
- }
- r, err := fi.file.Open()
- if err != nil {
- return nil, err
- }
- return &zipSeek{fi.file, r}, nil
-}
-
-type zipSeek struct {
- file *zip.File
- io.ReadCloser
-}
-
-func (f *zipSeek) Seek(offset int64, whence int) (int64, error) {
- if whence == 0 && offset == 0 {
- r, err := f.file.Open()
- if err != nil {
- return 0, err
- }
- f.Close()
- f.ReadCloser = r
- return 0, nil
- }
- return 0, fmt.Errorf("unsupported Seek in %s", f.file.Name)
-}
-
-func (fs *zipFS) Lstat(abspath string) (os.FileInfo, error) {
- _, fi, err := fs.stat(zipPath(abspath))
- return fi, err
-}
-
-func (fs *zipFS) Stat(abspath string) (os.FileInfo, error) {
- _, fi, err := fs.stat(zipPath(abspath))
- return fi, err
-}
-
-func (fs *zipFS) ReadDir(abspath string) ([]os.FileInfo, error) {
- path := zipPath(abspath)
- i, fi, err := fs.stat(path)
- if err != nil {
- return nil, err
- }
- if !fi.IsDir() {
- return nil, fmt.Errorf("ReadDir: %s is not a directory", abspath)
- }
-
- var list []os.FileInfo
- dirname := path + "/"
- prevname := ""
- for _, e := range fs.list[i:] {
- if !strings.HasPrefix(e.Name, dirname) {
- break // not in the same directory anymore
- }
- name := e.Name[len(dirname):] // local name
- file := e
- if i := strings.IndexRune(name, '/'); i >= 0 {
- // We infer directories from files in subdirectories.
- // If we have x/y, return a directory entry for x.
- name = name[0:i] // keep local directory name only
- file = nil
- }
- // If we have x/y and x/z, don't return two directory entries for x.
- // TODO(gri): It should be possible to do this more efficiently
- // by determining the (fs.list) range of local directory entries
- // (via two binary searches).
- if name != prevname {
- list = append(list, zipFI{name, file})
- prevname = name
- }
- }
-
- return list, nil
-}
-
-func NewZipFS(rc *zip.ReadCloser, name string) FileSystem {
- list := make(zipList, len(rc.File))
- copy(list, rc.File) // sort a copy of rc.File
- sort.Sort(list)
- return &zipFS{rc, list, name}
-}
-
-type zipList []*zip.File
-
-// zipList implements sort.Interface
-func (z zipList) Len() int { return len(z) }
-func (z zipList) Less(i, j int) bool { return z[i].Name < z[j].Name }
-func (z zipList) Swap(i, j int) { z[i], z[j] = z[j], z[i] }
-
-// lookup returns the smallest index of an entry with an exact match
-// for name, or an inexact match starting with name/. If there is no
-// such entry, the result is -1, false.
-func (z zipList) lookup(name string) (index int, exact bool) {
- // look for exact match first (name comes before name/ in z)
- i := sort.Search(len(z), func(i int) bool {
- return name <= z[i].Name
- })
- if i >= len(z) {
- return -1, false
- }
- // 0 <= i < len(z)
- if z[i].Name == name {
- return i, true
- }
-
- // look for inexact match (must be in z[i:], if present)
- z = z[i:]
- name += "/"
- j := sort.Search(len(z), func(i int) bool {
- return name <= z[i].Name
- })
- if j >= len(z) {
- return -1, false
- }
- // 0 <= j < len(z)
- if strings.HasPrefix(z[j].Name, name) {
- return i + j, false
- }
-
- return -1, false
-}