diff --git a/godoc/godoc.go b/godoc/godoc.go index eee477c2f6..cfa8c2f3c3 100644 --- a/godoc/godoc.go +++ b/godoc/godoc.go @@ -22,6 +22,7 @@ import ( "os" pathpkg "path" "regexp" + "strconv" "strings" "text/template" "time" @@ -80,6 +81,7 @@ func (p *Presentation) initFuncMap() { "srcLink": srcLinkFunc, "posLink_url": newPosLink_urlFunc(srcPosLinkFunc), "docLink": docLinkFunc, + "queryLink": queryLinkFunc, // formatting of Examples "example_html": p.example_htmlFunc, @@ -96,6 +98,9 @@ func (p *Presentation) initFuncMap() { if p.URLForSrcPos != nil { p.funcMap["posLink_url"] = newPosLink_urlFunc(p.URLForSrcPos) } + if p.URLForSrcQuery != nil { + p.funcMap["queryLink"] = p.URLForSrcQuery + } } func filenameFunc(path string) string { @@ -298,6 +303,19 @@ func srcLinkFunc(s string) string { return pathpkg.Clean("/" + s) } +// queryLinkFunc returns a URL for a line in a source file with a highlighted +// query term. +// s is expected to be a path to a source file. +// query is expected to be a string that has already been appropriately escaped +// for use in a URL query. +func queryLinkFunc(s, query string, line int) string { + url := pathpkg.Clean("/"+s) + "?h=" + query + if line > 0 { + url += "#L" + strconv.Itoa(line) + } + return url +} + func docLinkFunc(s string, ident string) string { s = strings.TrimPrefix(s, "/src") return pathpkg.Clean("/"+s) + "/#" + ident diff --git a/godoc/godoc_test.go b/godoc/godoc_test.go new file mode 100644 index 0000000000..bf058d5786 --- /dev/null +++ b/godoc/godoc_test.go @@ -0,0 +1,90 @@ +// 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. + +package godoc + +import ( + "testing" +) + +func TestPkgLinkFunc(t *testing.T) { + for _, tc := range []struct { + path string + want string + }{ + {"/src/pkg/fmt", "pkg/fmt"}, + {"/fmt", "pkg/fmt"}, + } { + if got, want := pkgLinkFunc(tc.path), tc.want; got != want { + t.Errorf("pkgLinkFunc(%v) = %v; want %v", tc.path, got, want) + } + } +} + +func TestSrcPosLinkFunc(t *testing.T) { + for _, tc := range []struct { + src string + line int + low int + high int + want string + }{ + {"/src/pkg/fmt/print.go", 42, 30, 50, "/src/pkg/fmt/print.go?s=30:50#L32"}, + {"/src/pkg/fmt/print.go", 2, 1, 5, "/src/pkg/fmt/print.go?s=1:5#L1"}, + {"/src/pkg/fmt/print.go", 2, 0, 0, "/src/pkg/fmt/print.go#L2"}, + {"/src/pkg/fmt/print.go", 0, 0, 0, "/src/pkg/fmt/print.go"}, + {"/src/pkg/fmt/print.go", 0, 1, 5, "/src/pkg/fmt/print.go?s=1:5#L1"}, + } { + if got, want := srcPosLinkFunc(tc.src, tc.line, tc.low, tc.high), tc.want; got != want { + t.Errorf("srcLinkFunc(%v, %v, %v, %v) = %v; want %v", tc.src, tc.line, tc.low, tc.high, got, want) + } + } +} + +func TestSrcLinkFunc(t *testing.T) { + for _, tc := range []struct { + src string + want string + }{ + {"/src/pkg/fmt/print.go", "/src/pkg/fmt/print.go"}, + {"src/pkg/fmt/print.go", "/src/pkg/fmt/print.go"}, + } { + if got, want := srcLinkFunc(tc.src), tc.want; got != want { + t.Errorf("srcLinkFunc(%v) = %v; want %v", tc.src, got, want) + } + } +} + +func TestQueryLinkFunc(t *testing.T) { + for _, tc := range []struct { + src string + query string + line int + want string + }{ + {"/src/pkg/fmt/print.go", "Sprintf", 33, "/src/pkg/fmt/print.go?h=Sprintf#L33"}, + {"/src/pkg/fmt/print.go", "Sprintf", 0, "/src/pkg/fmt/print.go?h=Sprintf"}, + {"src/pkg/fmt/print.go", "EOF", 33, "/src/pkg/fmt/print.go?h=EOF#L33"}, + {"src/pkg/fmt/print.go", "a%3f+%26b", 1, "/src/pkg/fmt/print.go?h=a%3f+%26b#L1"}, + } { + if got, want := queryLinkFunc(tc.src, tc.query, tc.line), tc.want; got != want { + t.Errorf("queryLinkFunc(%v, %v, %v) = %v; want %v", tc.src, tc.query, tc.line, got, want) + } + } +} + +func TestDocLinkFunc(t *testing.T) { + for _, tc := range []struct { + src string + ident string + want string + }{ + {"/src/pkg/fmt", "Sprintf", "/pkg/fmt/#Sprintf"}, + {"/src/pkg/fmt", "EOF", "/pkg/fmt/#EOF"}, + } { + if got, want := docLinkFunc(tc.src, tc.ident), tc.want; got != want { + t.Errorf("docLinkFunc(%v, %v) = %v; want %v", tc.src, tc.ident, got, want) + } + } +} diff --git a/godoc/pres.go b/godoc/pres.go index 2c4b21f39f..4508fba0bb 100644 --- a/godoc/pres.go +++ b/godoc/pres.go @@ -67,6 +67,15 @@ type Presentation struct { // The source file argument has the form /src/pkg//. URLForSrcPos func(src string, line, low, high int) string + // URLForSrcQuery optionally specifies a function to create a URL given a + // source file, a query string, and a line from the source file (1-based). + // The source file argument has the form /src/pkg//. + // The query argument will be escaped for the purposes of embedding in a URL + // query parameter. + // Ideally, the returned URL will be for the specified line of the file with + // the query string highlighted. + URLForSrcQuery func(src, query string, line int) string + initFuncMapOnce sync.Once funcMap template.FuncMap templateFuncs template.FuncMap diff --git a/godoc/static/search.html b/godoc/static/search.html index 5e9093f6e3..01d9f61d39 100644 --- a/godoc/static/search.html +++ b/godoc/static/search.html @@ -50,10 +50,11 @@ {{$pkg_html := pkgLink .Pak.Path | html}}

package {{html .Pak.Name}}

{{range .Files}} - {{$src_html := srcLink .File.Path | html}} + {{$file := .File.Path}} {{range .Groups}} {{range .}} - {{$src_html}}:{{infoLine .}} + {{$line := infoLine .}} + {{$file}}:{{$line}} {{infoSnippet_html .}} {{end}} {{end}} @@ -66,8 +67,8 @@ {{$pkg_html := pkgLink .Pak.Path | html}}

package {{html .Pak.Name}}

{{range .Files}} - {{$src_html := srcLink .File.Path | html}} - {{$src_html}} + {{$file := .File.Path}} + {{$file}} {{range .Groups}} @@ -76,7 +77,8 @@ @@ -98,17 +100,17 @@

{{range .}} - {{infoLine .}} + {{$line := infoLine .}} + {{$line}} {{end}}
{{range .}} - {{$src_html := srcLink .Filename | html}} + {{$file := .Filename}}
- {{$src_html}}: + {{$file}}: {{len .Lines}} {{range .Lines}} - {{html .}} + {{html .}} {{end}} {{if not $.Complete}} ... diff --git a/godoc/static/static.go b/godoc/static/static.go index 98ec848904..bcfc5352f0 100644 --- a/godoc/static/static.go +++ b/godoc/static/static.go @@ -1413,10 +1413,11 @@ function PlaygroundOutput(el) { {{$pkg_html := pkgLink .Pak.Path | html}}

package {{html .Pak.Name}}

{{range .Files}} - {{$src_html := srcLink .File.Path | html}} + {{$file := .File.Path}} {{range .Groups}} {{range .}} - {{$src_html}}:{{infoLine .}} + {{$line := infoLine .}} + {{$file}}:{{$line}} {{infoSnippet_html .}} {{end}} {{end}} @@ -1429,8 +1430,8 @@ function PlaygroundOutput(el) { {{$pkg_html := pkgLink .Pak.Path | html}}

package {{html .Pak.Name}}

{{range .Files}} - {{$src_html := srcLink .File.Path | html}} - {{$src_html}} + {{$file := .File.Path}} + {{$file}} {{range .Groups}} @@ -1439,7 +1440,8 @@ function PlaygroundOutput(el) { @@ -1461,17 +1463,17 @@ function PlaygroundOutput(el) {

{{range .}} - {{infoLine .}} + {{$line := infoLine .}} + {{$line}} {{end}}
{{range .}} - {{$src_html := srcLink .Filename | html}} + {{$file := .Filename}}
- {{$src_html}}: + {{$file}}: {{len .Lines}} {{range .Lines}} - {{html .}} + {{html .}} {{end}} {{if not $.Complete}} ...