godoc: add links to docs in text and dir pages
Fixes golang/go#17125
Change-Id: I22dd0561cd1c8eb30524797b6c0488d08a65285b
Reviewed-on: https://go-review.googlesource.com/29279
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/godoc/godoc.go b/godoc/godoc.go
index e7868ee..a9e8b3b 100644
--- a/godoc/godoc.go
+++ b/godoc/godoc.go
@@ -79,11 +79,13 @@
"sanitize": sanitizeFunc,
// support for URL attributes
- "pkgLink": pkgLinkFunc,
- "srcLink": srcLinkFunc,
- "posLink_url": newPosLink_urlFunc(srcPosLinkFunc),
- "docLink": docLinkFunc,
- "queryLink": queryLinkFunc,
+ "pkgLink": pkgLinkFunc,
+ "srcLink": srcLinkFunc,
+ "posLink_url": newPosLink_urlFunc(srcPosLinkFunc),
+ "docLink": docLinkFunc,
+ "queryLink": queryLinkFunc,
+ "srcBreadcrumb": srcBreadcrumbFunc,
+ "srcToPkgLink": srcToPkgLinkFunc,
// formatting of Examples
"example_html": p.example_htmlFunc,
@@ -459,6 +461,51 @@
return "pkg/" + path
}
+// srcToPkgLinkFunc builds an <a> tag linking to
+// the package documentation of relpath.
+func srcToPkgLinkFunc(relpath string) string {
+ relpath = pkgLinkFunc(relpath)
+ if relpath == "pkg/" {
+ return `<a href="/pkg">Index</a>`
+ }
+ if i := strings.LastIndex(relpath, "/"); i != -1 {
+ // Remove filename after last slash.
+ relpath = relpath[:i]
+ }
+ return fmt.Sprintf(`<a href="/%s">%s</a>`, relpath, relpath[len("pkg/"):])
+}
+
+// srcBreadcrumbFun converts each segment of relpath to a HTML <a>.
+// Each segment links to its corresponding src directories.
+func srcBreadcrumbFunc(relpath string) string {
+ segments := strings.Split(relpath, "/")
+ var buf bytes.Buffer
+ var selectedSegment string
+ var selectedIndex int
+
+ if strings.HasSuffix(relpath, "/") {
+ // relpath is a directory ending with a "/".
+ // Selected segment is the segment before the last slash.
+ selectedIndex = len(segments) - 2
+ selectedSegment = segments[selectedIndex] + "/"
+ } else {
+ selectedIndex = len(segments) - 1
+ selectedSegment = segments[selectedIndex]
+ }
+
+ for i := range segments[:selectedIndex] {
+ buf.WriteString(fmt.Sprintf(`<a href="/%s">%s</a>/`,
+ strings.Join(segments[:i+1], "/"),
+ segments[i],
+ ))
+ }
+
+ buf.WriteString(`<span class="text-muted">`)
+ buf.WriteString(selectedSegment)
+ buf.WriteString(`</span>`)
+ return buf.String()
+}
+
func newPosLink_urlFunc(srcPosLinkFunc func(s string, line, low, high int) string) func(info *PageInfo, n interface{}) string {
// n must be an ast.Node or a *doc.Note
return func(info *PageInfo, n interface{}) string {
diff --git a/godoc/godoc_test.go b/godoc/godoc_test.go
index ef5790e..dca1c95 100644
--- a/godoc/godoc_test.go
+++ b/godoc/godoc_test.go
@@ -290,3 +290,32 @@
}
}
}
+
+func TestSrcBreadcrumbFunc(t *testing.T) {
+ for _, tc := range []struct {
+ path string
+ want string
+ }{
+ {"src/", `<span class="text-muted">src/</span>`},
+ {"src/fmt/", `<a href="/src">src</a>/<span class="text-muted">fmt/</span>`},
+ {"src/fmt/print.go", `<a href="/src">src</a>/<a href="/src/fmt">fmt</a>/<span class="text-muted">print.go</span>`},
+ } {
+ if got := srcBreadcrumbFunc(tc.path); got != tc.want {
+ t.Errorf("srcBreadcrumbFunc(%v) = %v; want %v", tc.path, got, tc.want)
+ }
+ }
+}
+
+func TestSrcToPkgLinkFunc(t *testing.T) {
+ for _, tc := range []struct {
+ path string
+ want string
+ }{
+ {"src/", `<a href="/pkg">Index</a>`},
+ {"src/fmt/", `<a href="/pkg/fmt">fmt</a>`},
+ } {
+ if got := srcToPkgLinkFunc(tc.path); got != tc.want {
+ t.Errorf("srcToPkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
+ }
+ }
+}
diff --git a/godoc/page.go b/godoc/page.go
index 35d6cfb..0c7bf00 100644
--- a/godoc/page.go
+++ b/godoc/page.go
@@ -16,6 +16,7 @@
Title string
Tabtitle string
Subtitle string
+ SrcPath string
Query string
Body []byte
Share bool
diff --git a/godoc/server.go b/godoc/server.go
index ffe5997..c9b4056 100644
--- a/godoc/server.go
+++ b/godoc/server.go
@@ -579,7 +579,8 @@
fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath))
p.ServePage(w, Page{
- Title: title + " " + relpath,
+ Title: title,
+ SrcPath: relpath,
Tabtitle: relpath,
Body: buf.Bytes(),
Share: allowShare(r),
@@ -649,7 +650,8 @@
}
p.ServePage(w, Page{
- Title: "Directory " + relpath,
+ Title: "Directory",
+ SrcPath: relpath,
Tabtitle: relpath,
Body: applyTemplate(p.DirlistHTML, "dirlistHTML", list),
Share: allowShare(r),
diff --git a/godoc/static/godoc.html b/godoc/static/godoc.html
index a700982..92b10aa 100644
--- a/godoc/static/godoc.html
+++ b/godoc/static/godoc.html
@@ -65,13 +65,23 @@
<div id="page"{{if .Title}} class="wide"{{end}}>
<div class="container">
-{{with .Title}}
- <h1>{{html .}}</h1>
+{{if or .Title .SrcPath}}
+ <h1>
+ {{html .Title}}
+ {{html .SrcPath | srcBreadcrumb}}
+ </h1>
{{end}}
+
{{with .Subtitle}}
<h2>{{html .}}</h2>
{{end}}
+{{with .SrcPath}}
+ <h2>
+ Documentation: {{html . | srcToPkgLink}}
+ </h2>
+{{end}}
+
{{/* The Table of Contents is automatically inserted in this <div>.
Do not delete this <div>. */}}
<div id="nav"></div>
diff --git a/godoc/static/static.go b/godoc/static/static.go
index c8915765..862ea10 100644
--- a/godoc/static/static.go
+++ b/godoc/static/static.go
@@ -529,13 +529,23 @@
<div id="page"{{if .Title}} class="wide"{{end}}>
<div class="container">
-{{with .Title}}
- <h1>{{html .}}</h1>
+{{if or .Title .SrcPath}}
+ <h1>
+ {{html .Title}}
+ {{html .SrcPath | srcBreadcrumb}}
+ </h1>
{{end}}
+
{{with .Subtitle}}
<h2>{{html .}}</h2>
{{end}}
+{{with .SrcPath}}
+ <h2>
+ Documentation: {{html . | srcToPkgLink}}
+ </h2>
+{{end}}
+
{{/* The Table of Contents is automatically inserted in this <div>.
Do not delete this <div>. */}}
<div id="nav"></div>
@@ -2996,6 +3006,9 @@
font-size: 28px;
line-height: 1;
}
+h1 .text-muted {
+ color:#777;
+}
h2 {
font-size: 20px;
background: #E0EBF5;
diff --git a/godoc/static/style.css b/godoc/static/style.css
index e89ac29..25933a3 100644
--- a/godoc/static/style.css
+++ b/godoc/static/style.css
@@ -101,6 +101,9 @@
font-size: 28px;
line-height: 1;
}
+h1 .text-muted {
+ color:#777;
+}
h2 {
font-size: 20px;
background: #E0EBF5;