mirror of
https://github.com/golang/go
synced 2024-11-22 00:24:41 -07:00
- properly align package synopses
(this was surprisingly hard to get right in HTML) - show modification times in source directory listings - various tweaks R=rsc http://go/go-review/1024024
This commit is contained in:
parent
40a7db3ee9
commit
202ede1240
@ -184,7 +184,6 @@ a.noline {
|
||||
table.layout {
|
||||
border-width: 0px;
|
||||
border-spacing: 0px;
|
||||
border-width: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,10 @@
|
||||
<table class="layout">
|
||||
<tr>
|
||||
<th align="left">File</th>
|
||||
<th width="100" align="right">Size</th>
|
||||
<td width="25"> </td>
|
||||
<th align="right">Bytes</th>
|
||||
<td width="25"> </td>
|
||||
<th align="left">Modified</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href=".." class="noline">..</a></td>
|
||||
@ -16,7 +19,10 @@
|
||||
{.repeated section @}
|
||||
<tr>
|
||||
<td align="left"><a href="{Name|html}" class="noline">{Name|html}</a></td>
|
||||
<td></td>
|
||||
<td align="right">{Size|html}</td>
|
||||
<td></td>
|
||||
<td align="left">{Mtime_ns|time}</td>
|
||||
</tr>
|
||||
{.end}
|
||||
</table>
|
||||
|
@ -1,6 +0,0 @@
|
||||
<table class="layout">
|
||||
<tr><td colspan="2"><a href="{Path|path}">{Name|html}</a></td><td width="10"></td><td>{Text|html}</td></tr>
|
||||
{.repeated section Dirs}
|
||||
<tr><td width="25"></td><td>{@|dir}</td></tr>
|
||||
{.end}
|
||||
</table>
|
@ -97,7 +97,7 @@
|
||||
<li class="navhead">Programming</li>
|
||||
<li><a href="/cmd" class="noline">Command documentation</a></li>
|
||||
<li><a href="/pkg" class="noline">Package documentation</a></li>
|
||||
<li><a href="/src" class="noline">Sources</a></li>
|
||||
<li><a href="/src" class="noline">Source files</a></li>
|
||||
|
||||
<li class="blank"> </li>
|
||||
<li class="navhead">Help</li>
|
||||
@ -113,7 +113,7 @@
|
||||
|
||||
<li class="blank"> </li>
|
||||
<li class="navhead">Last update</li>
|
||||
<li>{Timestamp|html}</li>
|
||||
<li>{Timestamp|time}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
@ -76,10 +76,22 @@
|
||||
{.end}
|
||||
{.end}
|
||||
{.section Dirs}
|
||||
{.section Dirs}
|
||||
<h2>Subdirectories</h2>
|
||||
{.repeated section @}
|
||||
{@|dir}
|
||||
{.end}
|
||||
<h2>Subdirectories</h2>
|
||||
<p>
|
||||
<table class="layout">
|
||||
<tr>
|
||||
<th align="left" colspan="{MaxHeight|html}">Name</th>
|
||||
<td width="25"> </td>
|
||||
<th align="left">Synopsis</th>
|
||||
</tr>
|
||||
{.repeated section List}
|
||||
<tr>
|
||||
{Depth|padding}
|
||||
<td align="left" colspan="{Height|html}"><a href="{Path|html}" class="noline">{Name|html}<a></td>
|
||||
<td></td>
|
||||
<td align="left">{Synopsis|html}</td>
|
||||
</tr>
|
||||
{.end}
|
||||
</table>
|
||||
</p>
|
||||
{.end}
|
||||
|
@ -77,7 +77,7 @@ BUGS
|
||||
|
||||
SUBDIRECTORIES
|
||||
|
||||
{.repeated section @}
|
||||
{.repeated section List}
|
||||
{Name}
|
||||
{.end}
|
||||
{.end}
|
||||
|
@ -152,19 +152,20 @@ func firstSentence(s string) string {
|
||||
// Package directories
|
||||
|
||||
type Directory struct {
|
||||
Depth int;
|
||||
Path string; // includes Name
|
||||
Name string;
|
||||
Text string; // package documentation, if any
|
||||
Dirs []*Directory;
|
||||
Text string; // package documentation, if any
|
||||
Dirs []*Directory; // subdirectories
|
||||
}
|
||||
|
||||
|
||||
func newDirTree(path, name string, depth int) *Directory {
|
||||
if depth <= 0 {
|
||||
func newDirTree(path, name string, depth, maxDepth int) *Directory {
|
||||
if depth >= maxDepth {
|
||||
// return a dummy directory so that the parent directory
|
||||
// doesn't get discarded just because we reached the max
|
||||
// directory depth
|
||||
return &Directory{path, name, "", nil};
|
||||
return &Directory{depth, path, name, "", nil};
|
||||
}
|
||||
|
||||
list, _ := io.ReadDir(path); // ignore errors
|
||||
@ -183,7 +184,14 @@ func newDirTree(path, name string, depth int) *Directory {
|
||||
// no package documentation yet; take the first found
|
||||
file, err := parser.ParseFile(pathutil.Join(path, d.Name), nil,
|
||||
parser.ParseComments | parser.PackageClauseOnly);
|
||||
if err == nil && file.Name.Value == name && file.Doc != nil {
|
||||
if err == nil &&
|
||||
// Also accept fakePkgName, so we get synopses for commmands.
|
||||
// Note: This may lead to incorrect results if there is a
|
||||
// (left-over) "documentation" package somewhere in a package
|
||||
// directory of different name, but this is very unlikely and
|
||||
// against current conventions.
|
||||
(file.Name.Value == name || file.Name.Value == fakePkgName) &&
|
||||
file.Doc != nil {
|
||||
// found documentation; extract a synopsys
|
||||
text = firstSentence(doc.CommentText(file.Doc));
|
||||
}
|
||||
@ -198,7 +206,7 @@ func newDirTree(path, name string, depth int) *Directory {
|
||||
i := 0;
|
||||
for _, d := range list {
|
||||
if isPkgDir(d) {
|
||||
dd := newDirTree(pathutil.Join(path, d.Name), d.Name, depth-1);
|
||||
dd := newDirTree(pathutil.Join(path, d.Name), d.Name, depth+1, maxDepth);
|
||||
if dd != nil {
|
||||
dirs[i] = dd;
|
||||
i++;
|
||||
@ -214,21 +222,43 @@ func newDirTree(path, name string, depth int) *Directory {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return &Directory{path, name, text, dirs};
|
||||
return &Directory{depth, path, name, text, dirs};
|
||||
}
|
||||
|
||||
|
||||
// newDirectory creates a new package directory tree with at most depth
|
||||
// newDirectory creates a new package directory tree with at most maxDepth
|
||||
// levels, anchored at root which is relative to goroot. The result tree
|
||||
// only contains directories that contain package files or that contain
|
||||
// subdirectories containing package files (transitively).
|
||||
//
|
||||
func newDirectory(root string, depth int) *Directory {
|
||||
func newDirectory(root string, maxDepth int) *Directory {
|
||||
d, err := os.Lstat(root);
|
||||
if err != nil || !isPkgDir(d) {
|
||||
return nil;
|
||||
}
|
||||
return newDirTree(root, d.Name, depth);
|
||||
return newDirTree(root, d.Name, 0, maxDepth);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -255,6 +285,93 @@ func (dir *Directory) lookup(path string) *Directory {
|
||||
}
|
||||
|
||||
|
||||
// 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; // includes Name, relative to DirList root
|
||||
Name string;
|
||||
Synopsis string;
|
||||
}
|
||||
|
||||
|
||||
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 := d.Path;
|
||||
if strings.HasPrefix(d.Path, root.Path) {
|
||||
path = d.Path[len(root.Path):len(d.Path)];
|
||||
}
|
||||
// remove trailing '/' if any - path must be relative
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:len(path)];
|
||||
}
|
||||
p.Path = path;
|
||||
p.Name = d.Name;
|
||||
p.Synopsis = d.Text;
|
||||
i++;
|
||||
}
|
||||
|
||||
return &DirList{maxHeight, list};
|
||||
}
|
||||
|
||||
|
||||
func listing(dirs []*os.Dir) *DirList {
|
||||
list := make([]DirEntry, len(dirs)+1);
|
||||
list[0] = DirEntry{0, 1, "..", "..", ""};
|
||||
for i, d := range dirs {
|
||||
p := &list[i+1];
|
||||
p.Depth = 0;
|
||||
p.Height = 1;
|
||||
p.Path = d.Name;
|
||||
p.Name = d.Name;
|
||||
}
|
||||
return &DirList{1, list};
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Parsing
|
||||
|
||||
@ -438,15 +555,6 @@ func textFmt(w io.Writer, x interface{}, format string) {
|
||||
}
|
||||
|
||||
|
||||
// Template formatter for "dir" format.
|
||||
func dirFmt(w io.Writer, x interface{}, format string) {
|
||||
_ = x.(*Directory); // die quickly if x has the wrong type
|
||||
if err := dirsHtml.Execute(x, w); err != nil {
|
||||
log.Stderrf("dirsHtml.Execute: %s", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func removePrefix(s, prefix string) string {
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
return s[len(prefix):len(s)];
|
||||
@ -525,16 +633,32 @@ func infoSnippetFmt(w io.Writer, x interface{}, format string) {
|
||||
}
|
||||
|
||||
|
||||
// Template formatter for "padding" format.
|
||||
func paddingFmt(w io.Writer, x interface{}, format string) {
|
||||
for i := x.(int); i > 0; i-- {
|
||||
fmt.Fprint(w, `<td width="25"></td>`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Template formatter for "time" format.
|
||||
func timeFmt(w io.Writer, x interface{}, format string) {
|
||||
// note: os.Dir.Mtime_ns is in uint64 in ns!
|
||||
template.HtmlEscape(w, strings.Bytes(time.SecondsToLocalTime(int64(x.(uint64) / 1e9)).String()));
|
||||
}
|
||||
|
||||
|
||||
var fmap = template.FormatterMap{
|
||||
"": textFmt,
|
||||
"html": htmlFmt,
|
||||
"html-comment": htmlCommentFmt,
|
||||
"dir": dirFmt,
|
||||
"path": pathFmt,
|
||||
"link": linkFmt,
|
||||
"infoClass": infoClassFmt,
|
||||
"infoLine": infoLineFmt,
|
||||
"infoSnippet": infoSnippetFmt,
|
||||
"padding": paddingFmt,
|
||||
"time": timeFmt,
|
||||
}
|
||||
|
||||
|
||||
@ -553,8 +677,7 @@ func readTemplate(name string) *template.Template {
|
||||
|
||||
|
||||
var (
|
||||
dirListHtml,
|
||||
dirsHtml,
|
||||
dirlistHtml,
|
||||
godocHtml,
|
||||
packageHtml,
|
||||
packageText,
|
||||
@ -566,8 +689,7 @@ var (
|
||||
func readTemplates() {
|
||||
// have to delay until after flags processing,
|
||||
// so that main has chdir'ed to goroot.
|
||||
dirListHtml = readTemplate("dirlist.html");
|
||||
dirsHtml = readTemplate("dirs.html");
|
||||
dirlistHtml = readTemplate("dirlist.html");
|
||||
godocHtml = readTemplate("godoc.html");
|
||||
packageHtml = readTemplate("package.html");
|
||||
packageText = readTemplate("package.txt");
|
||||
@ -583,7 +705,7 @@ func readTemplates() {
|
||||
func servePage(c *http.Conn, title, query string, content []byte) {
|
||||
type Data struct {
|
||||
Title string;
|
||||
Timestamp string;
|
||||
Timestamp uint64; // int64 to be compatible with os.Dir.Mtime_ns
|
||||
Query string;
|
||||
Content []byte;
|
||||
}
|
||||
@ -591,7 +713,7 @@ func servePage(c *http.Conn, title, query string, content []byte) {
|
||||
_, ts := fsTree.get();
|
||||
d := Data{
|
||||
Title: title,
|
||||
Timestamp: time.SecondsToLocalTime(ts).String(),
|
||||
Timestamp: uint64(ts)*1e9, // timestamp in ns
|
||||
Query: query,
|
||||
Content: content,
|
||||
};
|
||||
@ -756,8 +878,8 @@ func serveDirectory(c *http.Conn, r *http.Request, path string) {
|
||||
}
|
||||
|
||||
var buf bytes.Buffer;
|
||||
if err := dirListHtml.Execute(list, &buf); err != nil {
|
||||
log.Stderrf("dirListHtml.Execute: %s", err);
|
||||
if err := dirlistHtml.Execute(list, &buf); err != nil {
|
||||
log.Stderrf("dirlistHtml.Execute: %s", err);
|
||||
}
|
||||
|
||||
servePage(c, "Directory " + path, "", buf.Bytes());
|
||||
@ -818,7 +940,7 @@ const fakePkgName = "documentation"
|
||||
|
||||
type PageInfo struct {
|
||||
PDoc *doc.PackageDoc; // nil if no package found
|
||||
Dirs *Directory; // nil if no directory information found
|
||||
Dirs *DirList; // nil if no directory information found
|
||||
IsPkg bool; // false if this is not documenting a real package
|
||||
}
|
||||
|
||||
@ -885,7 +1007,7 @@ func (h *httpHandler) getPageInfo(path string) PageInfo {
|
||||
dir = newDirectory(dirname, 1);
|
||||
}
|
||||
|
||||
return PageInfo{pdoc, dir, h.isPkg};
|
||||
return PageInfo{pdoc, dir.listing(true), h.isPkg};
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user