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>
This commit is contained in:
Sina Siadat 2016-09-16 17:12:50 +04:30 committed by Brad Fitzpatrick
parent 34f7837286
commit 5128de7288
7 changed files with 116 additions and 11 deletions

View File

@ -79,11 +79,13 @@ func (p *Presentation) initFuncMap() {
"sanitize": sanitizeFunc, "sanitize": sanitizeFunc,
// support for URL attributes // support for URL attributes
"pkgLink": pkgLinkFunc, "pkgLink": pkgLinkFunc,
"srcLink": srcLinkFunc, "srcLink": srcLinkFunc,
"posLink_url": newPosLink_urlFunc(srcPosLinkFunc), "posLink_url": newPosLink_urlFunc(srcPosLinkFunc),
"docLink": docLinkFunc, "docLink": docLinkFunc,
"queryLink": queryLinkFunc, "queryLink": queryLinkFunc,
"srcBreadcrumb": srcBreadcrumbFunc,
"srcToPkgLink": srcToPkgLinkFunc,
// formatting of Examples // formatting of Examples
"example_html": p.example_htmlFunc, "example_html": p.example_htmlFunc,
@ -459,6 +461,51 @@ func pkgLinkFunc(path string) string {
return "pkg/" + path 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 { 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 // n must be an ast.Node or a *doc.Note
return func(info *PageInfo, n interface{}) string { return func(info *PageInfo, n interface{}) string {

View File

@ -290,3 +290,32 @@ func TestReplaceLeadingIndentation(t *testing.T) {
} }
} }
} }
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)
}
}
}

View File

@ -16,6 +16,7 @@ type Page struct {
Title string Title string
Tabtitle string Tabtitle string
Subtitle string Subtitle string
SrcPath string
Query string Query string
Body []byte Body []byte
Share bool Share bool

View File

@ -579,7 +579,8 @@ func (p *Presentation) serveTextFile(w http.ResponseWriter, r *http.Request, abs
fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath)) fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath))
p.ServePage(w, Page{ p.ServePage(w, Page{
Title: title + " " + relpath, Title: title,
SrcPath: relpath,
Tabtitle: relpath, Tabtitle: relpath,
Body: buf.Bytes(), Body: buf.Bytes(),
Share: allowShare(r), Share: allowShare(r),
@ -649,7 +650,8 @@ func (p *Presentation) serveDirectory(w http.ResponseWriter, r *http.Request, ab
} }
p.ServePage(w, Page{ p.ServePage(w, Page{
Title: "Directory " + relpath, Title: "Directory",
SrcPath: relpath,
Tabtitle: relpath, Tabtitle: relpath,
Body: applyTemplate(p.DirlistHTML, "dirlistHTML", list), Body: applyTemplate(p.DirlistHTML, "dirlistHTML", list),
Share: allowShare(r), Share: allowShare(r),

View File

@ -65,13 +65,23 @@ func main() {
<div id="page"{{if .Title}} class="wide"{{end}}> <div id="page"{{if .Title}} class="wide"{{end}}>
<div class="container"> <div class="container">
{{with .Title}} {{if or .Title .SrcPath}}
<h1>{{html .}}</h1> <h1>
{{html .Title}}
{{html .SrcPath | srcBreadcrumb}}
</h1>
{{end}} {{end}}
{{with .Subtitle}} {{with .Subtitle}}
<h2>{{html .}}</h2> <h2>{{html .}}</h2>
{{end}} {{end}}
{{with .SrcPath}}
<h2>
Documentation: {{html . | srcToPkgLink}}
</h2>
{{end}}
{{/* The Table of Contents is automatically inserted in this <div>. {{/* The Table of Contents is automatically inserted in this <div>.
Do not delete this <div>. */}} Do not delete this <div>. */}}
<div id="nav"></div> <div id="nav"></div>

View File

@ -529,13 +529,23 @@ func main() {
<div id="page"{{if .Title}} class="wide"{{end}}> <div id="page"{{if .Title}} class="wide"{{end}}>
<div class="container"> <div class="container">
{{with .Title}} {{if or .Title .SrcPath}}
<h1>{{html .}}</h1> <h1>
{{html .Title}}
{{html .SrcPath | srcBreadcrumb}}
</h1>
{{end}} {{end}}
{{with .Subtitle}} {{with .Subtitle}}
<h2>{{html .}}</h2> <h2>{{html .}}</h2>
{{end}} {{end}}
{{with .SrcPath}}
<h2>
Documentation: {{html . | srcToPkgLink}}
</h2>
{{end}}
{{/* The Table of Contents is automatically inserted in this <div>. {{/* The Table of Contents is automatically inserted in this <div>.
Do not delete this <div>. */}} Do not delete this <div>. */}}
<div id="nav"></div> <div id="nav"></div>
@ -2996,6 +3006,9 @@ h1 {
font-size: 28px; font-size: 28px;
line-height: 1; line-height: 1;
} }
h1 .text-muted {
color:#777;
}
h2 { h2 {
font-size: 20px; font-size: 20px;
background: #E0EBF5; background: #E0EBF5;

View File

@ -101,6 +101,9 @@ h1 {
font-size: 28px; font-size: 28px;
line-height: 1; line-height: 1;
} }
h1 .text-muted {
color:#777;
}
h2 { h2 {
font-size: 20px; font-size: 20px;
background: #E0EBF5; background: #E0EBF5;