mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
- show recursive package directory structure in package pages
- removed some underbars in section headings for better looks - various minor tweaks R=rsc http://go/go-review/1018026
This commit is contained in:
parent
6e98b7f0b2
commit
be56b95770
6 changed files with 130 additions and 98 deletions
|
|
@ -10,7 +10,7 @@
|
||||||
code, .code {
|
code, .code {
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
color:#007000;
|
color: #007000;
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
|
|
@ -149,12 +149,19 @@ div#linkList li.navhead {
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
/* Styles used by go/printer Styler implementations. */
|
/* Styles used by godoc */
|
||||||
|
|
||||||
a.noline {
|
a.noline {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.layout {
|
||||||
|
border-width: 0px;
|
||||||
|
border-spacing: 0px;
|
||||||
|
border-width: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
span.comment {
|
span.comment {
|
||||||
color: #0000a0;
|
color: #0000a0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
<table class="layout">
|
||||||
<tr><td><a href="{Path|html}">{Name|html}</a></td></tr>
|
<tr><td colspan="2"><a href="/pkg/{Path|html}">{Name|html}</a></td></tr>
|
||||||
{.repeated section Subdirs}
|
{.repeated section Dirs}
|
||||||
<tr><td></td><td>{@|dir}</td></tr>
|
<tr><td width="25em"></td><td>{@|dir}</td></tr>
|
||||||
{.end}
|
{.end}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,14 @@
|
||||||
{.end}
|
{.end}
|
||||||
{.section Funcs}
|
{.section Funcs}
|
||||||
{.repeated section @}
|
{.repeated section @}
|
||||||
<h2>func <a href="{Decl|link}">{Name|html}</a></h2>
|
<h2>func <a href="{Decl|link}" class="noline">{Name|html}</a></h2>
|
||||||
<p><code>{Decl|html}</code></p>
|
<p><code>{Decl|html}</code></p>
|
||||||
{Doc|html-comment}
|
{Doc|html-comment}
|
||||||
{.end}
|
{.end}
|
||||||
{.end}
|
{.end}
|
||||||
{.section Types}
|
{.section Types}
|
||||||
{.repeated section @}
|
{.repeated section @}
|
||||||
<h2>type <a href="{Decl|link}">{Type.Name|html}</a></h2>
|
<h2>type <a href="{Decl|link}" class="noline">{Type.Name|html}</a></h2>
|
||||||
{Doc|html-comment}
|
{Doc|html-comment}
|
||||||
<p><pre>{Decl|html}</pre></p>
|
<p><pre>{Decl|html}</pre></p>
|
||||||
{.repeated section Consts}
|
{.repeated section Consts}
|
||||||
|
|
@ -53,12 +53,12 @@
|
||||||
<pre>{Decl|html}</pre>
|
<pre>{Decl|html}</pre>
|
||||||
{.end}
|
{.end}
|
||||||
{.repeated section Factories}
|
{.repeated section Factories}
|
||||||
<h3>func <a href="{Decl|link}">{Name|html}</a></h3>
|
<h3>func <a href="{Decl|link}" class="noline">{Name|html}</a></h3>
|
||||||
<p><code>{Decl|html}</code></p>
|
<p><code>{Decl|html}</code></p>
|
||||||
{Doc|html-comment}
|
{Doc|html-comment}
|
||||||
{.end}
|
{.end}
|
||||||
{.repeated section Methods}
|
{.repeated section Methods}
|
||||||
<h3>func ({Recv|html}) <a href="{Decl|link}">{Name|html}</a></h3>
|
<h3>func ({Recv|html}) <a href="{Decl|link}" class="noline">{Name|html}</a></h3>
|
||||||
<p><code>{Decl|html}</code></p>
|
<p><code>{Decl|html}</code></p>
|
||||||
{Doc|html-comment}
|
{Doc|html-comment}
|
||||||
{.end}
|
{.end}
|
||||||
|
|
@ -72,8 +72,10 @@
|
||||||
{.end}
|
{.end}
|
||||||
{.end}
|
{.end}
|
||||||
{.section Dirs}
|
{.section Dirs}
|
||||||
<h2>Subdirectories</h2>
|
{.section Dirs}
|
||||||
{.repeated section @}
|
<h2>Subdirectories</h2>
|
||||||
<a href="{Name|html}">{Name|html}</a><br />
|
{.repeated section @}
|
||||||
|
{@|dir}
|
||||||
|
{.end}
|
||||||
{.end}
|
{.end}
|
||||||
{.end}
|
{.end}
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ BUGS
|
||||||
{.end}
|
{.end}
|
||||||
{.end}
|
{.end}
|
||||||
{.section Dirs}
|
{.section Dirs}
|
||||||
|
{.section Dirs}
|
||||||
|
|
||||||
SUBDIRECTORIES
|
SUBDIRECTORIES
|
||||||
|
|
||||||
|
|
@ -76,3 +77,4 @@ SUBDIRECTORIES
|
||||||
{Name}
|
{Name}
|
||||||
{.end}
|
{.end}
|
||||||
{.end}
|
{.end}
|
||||||
|
{.end}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes";
|
"bytes";
|
||||||
"container/vector";
|
|
||||||
"flag";
|
"flag";
|
||||||
"fmt";
|
"fmt";
|
||||||
"go/ast";
|
"go/ast";
|
||||||
|
|
@ -20,7 +19,6 @@ import (
|
||||||
"log";
|
"log";
|
||||||
"os";
|
"os";
|
||||||
pathutil "path";
|
pathutil "path";
|
||||||
"sort";
|
|
||||||
"strings";
|
"strings";
|
||||||
"sync";
|
"sync";
|
||||||
"template";
|
"template";
|
||||||
|
|
@ -128,54 +126,100 @@ func htmlEscape(s string) string {
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Directory trees
|
// Package directories
|
||||||
|
|
||||||
type Directory struct {
|
type Directory struct {
|
||||||
Path string; // including Name
|
Path string; // relative to *pkgroot, includes Name
|
||||||
Name string;
|
Name string;
|
||||||
Subdirs []*Directory
|
Dirs []*Directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func newDirTree0(path, name string) *Directory {
|
func newDirTree(path, name string, depth int) *Directory {
|
||||||
list, _ := io.ReadDir(path); // ignore errors
|
if depth <= 0 {
|
||||||
// determine number of subdirectories n
|
// return a dummy directory so that the parent directory
|
||||||
n := 0;
|
// doesn't get discarded just because we reached the max
|
||||||
|
// directory depth
|
||||||
|
return &Directory{path, name, nil};
|
||||||
|
}
|
||||||
|
|
||||||
|
fullpath := pathutil.Join(*pkgroot, path);
|
||||||
|
list, _ := io.ReadDir(fullpath); // ignore errors
|
||||||
|
|
||||||
|
// determine number of subdirectories and package files
|
||||||
|
ndirs := 0;
|
||||||
|
nfiles := 0;
|
||||||
for _, d := range list {
|
for _, d := range list {
|
||||||
if isPkgDir(d) {
|
switch {
|
||||||
n++;
|
case isPkgDir(d):
|
||||||
|
ndirs++;
|
||||||
|
case isPkgFile(d):
|
||||||
|
nfiles++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// create Directory node
|
|
||||||
var subdirs []*Directory;
|
// create subdirectory tree
|
||||||
if n > 0 {
|
var dirs []*Directory;
|
||||||
subdirs = make([]*Directory, n);
|
if ndirs > 0 {
|
||||||
|
dirs = make([]*Directory, ndirs);
|
||||||
i := 0;
|
i := 0;
|
||||||
for _, d := range list {
|
for _, d := range list {
|
||||||
if isPkgDir(d) {
|
if isPkgDir(d) {
|
||||||
subdirs[i] = newDirTree0(pathutil.Join(path, d.Name), d.Name);
|
dd := newDirTree(pathutil.Join(path, d.Name), d.Name, depth-1);
|
||||||
i++;
|
if dd != nil {
|
||||||
|
dirs[i] = dd;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dirs = dirs[0:i];
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(path, "src/") {
|
|
||||||
path = path[len("src/") : len(path)];
|
// if there are no package files and no subdirectories
|
||||||
|
// (with package files), ignore the directory
|
||||||
|
if nfiles == 0 && len(dirs) == 0 {
|
||||||
|
return nil;
|
||||||
}
|
}
|
||||||
return &Directory{path, name, subdirs};
|
|
||||||
|
return &Directory{path, name, dirs};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func newDirTree(root string) *Directory {
|
// newDirectory creates a new package directory tree with at most depth
|
||||||
d, err := os.Lstat(root);
|
// levels, anchored at root which is relative to Pkg. The result tree
|
||||||
if err != nil {
|
// only contains directories that contain package files or that contain
|
||||||
log.Stderrf("%v", err);
|
// subdirectories containing package files (transitively).
|
||||||
|
//
|
||||||
|
func newDirectory(root string, depth int) *Directory {
|
||||||
|
fullpath := pathutil.Join(*pkgroot, root);
|
||||||
|
d, err := os.Lstat(fullpath);
|
||||||
|
if err != nil || !isPkgDir(d) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
if !isPkgDir(d) {
|
return newDirTree(root, d.Name, depth);
|
||||||
log.Stderrf("not a package directory: %s", d.Name);
|
}
|
||||||
return nil;
|
|
||||||
|
|
||||||
|
// lookup looks for the *Directory for a given path, relative to dir.
|
||||||
|
func (dir *Directory) lookup(path string) *Directory {
|
||||||
|
path = pathutil.Clean(path); // no trailing '/'
|
||||||
|
|
||||||
|
if dir == nil || path == "" || path == "." {
|
||||||
|
return dir;
|
||||||
}
|
}
|
||||||
return newDirTree0(root, d.Name);
|
|
||||||
|
dpath, dname := pathutil.Split(path);
|
||||||
|
if dpath == "" {
|
||||||
|
// directory-local name
|
||||||
|
for _, d := range dir.Dirs {
|
||||||
|
if dname == d.Name {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir.lookup(dpath).lookup(dname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -610,20 +654,6 @@ func serveFile(c *http.Conn, r *http.Request) {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Packages
|
// Packages
|
||||||
|
|
||||||
// TODO if we don't plan to use the directory information, simplify to []string
|
|
||||||
type dirList []*os.Dir
|
|
||||||
|
|
||||||
func (d dirList) Len() int {
|
|
||||||
return len(d);
|
|
||||||
}
|
|
||||||
func (d dirList) Less(i, j int) bool {
|
|
||||||
return d[i].Name < d[j].Name;
|
|
||||||
}
|
|
||||||
func (d dirList) Swap(i, j int) {
|
|
||||||
d[i], d[j] = d[j], d[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func pkgName(filename string) string {
|
func pkgName(filename string) string {
|
||||||
file, err := parse(filename, parser.PackageClauseOnly);
|
file, err := parse(filename, parser.PackageClauseOnly);
|
||||||
if err != nil || file == nil {
|
if err != nil || file == nil {
|
||||||
|
|
@ -635,7 +665,7 @@ func pkgName(filename string) string {
|
||||||
|
|
||||||
type PageInfo struct {
|
type PageInfo struct {
|
||||||
PDoc *doc.PackageDoc; // nil if no package found
|
PDoc *doc.PackageDoc; // nil if no package found
|
||||||
Dirs dirList; // nil if no subdirectories found
|
Dirs *Directory; // nil if no directory information found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -651,10 +681,7 @@ func getPageInfo(path string) PageInfo {
|
||||||
// the package name is the directory name within its parent
|
// the package name is the directory name within its parent
|
||||||
_, pkgname := pathutil.Split(dirname);
|
_, pkgname := pathutil.Split(dirname);
|
||||||
|
|
||||||
// filter function to select the desired .go files and
|
// filter function to select the desired .go files
|
||||||
// collect subdirectories
|
|
||||||
var subdirlist vector.Vector;
|
|
||||||
subdirlist.Init(0);
|
|
||||||
filter := func(d *os.Dir) bool {
|
filter := func(d *os.Dir) bool {
|
||||||
if isPkgFile(d) {
|
if isPkgFile(d) {
|
||||||
// Some directories contain main packages: Only accept
|
// Some directories contain main packages: Only accept
|
||||||
|
|
@ -663,9 +690,6 @@ func getPageInfo(path string) PageInfo {
|
||||||
// found" errors.
|
// found" errors.
|
||||||
return pkgName(dirname + "/" + d.Name) == pkgname;
|
return pkgName(dirname + "/" + d.Name) == pkgname;
|
||||||
}
|
}
|
||||||
if isPkgDir(d) {
|
|
||||||
subdirlist.Push(d);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -673,17 +697,7 @@ func getPageInfo(path string) PageInfo {
|
||||||
pkg, err := parser.ParsePackage(dirname, filter, parser.ParseComments);
|
pkg, err := parser.ParsePackage(dirname, filter, parser.ParseComments);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: parse errors should be shown instead of an empty directory
|
// TODO: parse errors should be shown instead of an empty directory
|
||||||
log.Stderr(err);
|
log.Stderrf("parser.parsePackage: %s", err);
|
||||||
}
|
|
||||||
|
|
||||||
// convert and sort subdirectory list, if any
|
|
||||||
var subdirs dirList;
|
|
||||||
if subdirlist.Len() > 0 {
|
|
||||||
subdirs = make(dirList, subdirlist.Len());
|
|
||||||
for i := 0; i < subdirlist.Len(); i++ {
|
|
||||||
subdirs[i] = subdirlist.At(i).(*os.Dir);
|
|
||||||
}
|
|
||||||
sort.Sort(subdirs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute package documentation
|
// compute package documentation
|
||||||
|
|
@ -693,7 +707,20 @@ func getPageInfo(path string) PageInfo {
|
||||||
pdoc = doc.NewPackageDoc(pkg, pathutil.Clean(path)); // no trailing '/' in importpath
|
pdoc = doc.NewPackageDoc(pkg, pathutil.Clean(path)); // no trailing '/' in importpath
|
||||||
}
|
}
|
||||||
|
|
||||||
return PageInfo{pdoc, subdirs};
|
// get directory information
|
||||||
|
var dir *Directory;
|
||||||
|
if tree, _ := pkgTree.get(); tree != 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 beet computed)
|
||||||
|
dir = tree.(*Directory).lookup(pathutil.Clean(path));
|
||||||
|
} else {
|
||||||
|
// no directory tree present (either early after startup
|
||||||
|
// or command-line mode); compute one level for this page
|
||||||
|
dir = newDirectory(path, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageInfo{pdoc, dir};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -734,21 +761,6 @@ func servePkg(c *http.Conn, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Directory tree
|
|
||||||
|
|
||||||
// TODO(gri): Temporary - integrate with package serving.
|
|
||||||
|
|
||||||
func serveTree(c *http.Conn, r *http.Request) {
|
|
||||||
dir, _ := pkgTree.get();
|
|
||||||
|
|
||||||
var buf bytes.Buffer;
|
|
||||||
dirFmt(&buf, dir, "");
|
|
||||||
|
|
||||||
servePage(c, "Package tree", "", buf.Bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Search
|
// Search
|
||||||
|
|
||||||
|
|
@ -795,7 +807,6 @@ func search(c *http.Conn, r *http.Request) {
|
||||||
|
|
||||||
func registerPublicHandlers(mux *http.ServeMux) {
|
func registerPublicHandlers(mux *http.ServeMux) {
|
||||||
mux.Handle(Pkg, http.HandlerFunc(servePkg));
|
mux.Handle(Pkg, http.HandlerFunc(servePkg));
|
||||||
mux.Handle("/tree", http.HandlerFunc(serveTree)); // TODO(gri): integrate with package serving
|
|
||||||
mux.Handle("/search", http.HandlerFunc(search));
|
mux.Handle("/search", http.HandlerFunc(search));
|
||||||
mux.Handle("/", http.HandlerFunc(serveFile));
|
mux.Handle("/", http.HandlerFunc(serveFile));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,13 +97,19 @@ func exec(c *http.Conn, args []string) (status int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Maximum package directory depth, adjust as needed.
|
||||||
|
const maxPkgDirDepth = 16;
|
||||||
|
|
||||||
func dosync(c *http.Conn, r *http.Request) {
|
func dosync(c *http.Conn, r *http.Request) {
|
||||||
args := []string{"/bin/sh", "-c", *syncCmd};
|
args := []string{"/bin/sh", "-c", *syncCmd};
|
||||||
switch exec(c, args) {
|
switch exec(c, args) {
|
||||||
case 0:
|
case 0:
|
||||||
// sync succeeded and some files have changed;
|
// sync succeeded and some files have changed;
|
||||||
// update package tree
|
// update package tree.
|
||||||
pkgTree.set(newDirTree(*pkgroot));
|
// TODO(gri): The directory tree may be temporarily out-of-sync.
|
||||||
|
// Consider keeping separate time stamps so the web-
|
||||||
|
// page can indicate this discrepancy.
|
||||||
|
pkgTree.set(newDirectory(".", maxPkgDirDepth));
|
||||||
fallthrough;
|
fallthrough;
|
||||||
case 1:
|
case 1:
|
||||||
// sync failed because no files changed;
|
// sync failed because no files changed;
|
||||||
|
|
@ -156,6 +162,7 @@ func main() {
|
||||||
readTemplates();
|
readTemplates();
|
||||||
|
|
||||||
if *httpaddr != "" {
|
if *httpaddr != "" {
|
||||||
|
// Http server mode.
|
||||||
var handler http.Handler = http.DefaultServeMux;
|
var handler http.Handler = http.DefaultServeMux;
|
||||||
if *verbose {
|
if *verbose {
|
||||||
log.Stderrf("Go Documentation Server\n");
|
log.Stderrf("Go Documentation Server\n");
|
||||||
|
|
@ -171,8 +178,14 @@ func main() {
|
||||||
http.Handle("/debug/sync", http.HandlerFunc(dosync));
|
http.Handle("/debug/sync", http.HandlerFunc(dosync));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute package tree with corresponding timestamp.
|
// Initialize package tree with corresponding timestamp.
|
||||||
pkgTree.set(newDirTree(*pkgroot));
|
// Do it in two steps:
|
||||||
|
// 1) set timestamp right away so that the indexer is kicked on
|
||||||
|
pkgTree.set(nil);
|
||||||
|
// 2) compute initial package tree in a goroutine so that launch is quick
|
||||||
|
go func() {
|
||||||
|
pkgTree.set(newDirectory(".", maxPkgDirDepth));
|
||||||
|
}();
|
||||||
|
|
||||||
// Start sync goroutine, if enabled.
|
// Start sync goroutine, if enabled.
|
||||||
if *syncCmd != "" && *syncMin > 0 {
|
if *syncCmd != "" && *syncMin > 0 {
|
||||||
|
|
@ -206,9 +219,6 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command line mode.
|
// Command line mode.
|
||||||
// No package tree; set it to nil so we have a reasonable time stamp.
|
|
||||||
pkgTree.set(nil);
|
|
||||||
|
|
||||||
if *html {
|
if *html {
|
||||||
packageText = packageHtml;
|
packageText = packageHtml;
|
||||||
parseerrorText = parseerrorHtml;
|
parseerrorText = parseerrorHtml;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue