1
0
mirror of https://github.com/golang/go synced 2024-11-18 12:44:49 -07:00

godoc: make struct fields linkable in HTML mode

This adds <span id="StructName.FieldName"> elements around
field names, starting at the comment if present, so people
can link to /pkg/somepkg/#SomeStruct.SomeField.

Fixes golang/go#16753

Change-Id: I4a8b30605d18e9e33e3d42f273a95067ac491438
Reviewed-on: https://go-review.googlesource.com/33690
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Brad Fitzpatrick 2016-11-29 23:53:36 +00:00
parent 34fe8ce027
commit 0f65b31aee
2 changed files with 121 additions and 0 deletions

View File

@ -190,6 +190,9 @@ func (p *Presentation) node_htmlFunc(info *PageInfo, node interface{}, linkify b
var buf2 bytes.Buffer
if n, _ := node.(ast.Node); n != nil && linkify && p.DeclLinks {
LinkifyText(&buf2, buf1.Bytes(), n)
if st, name := isStructTypeDecl(n); st != nil {
addStructFieldIDAttributes(&buf2, name, st)
}
} else {
FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
}
@ -197,6 +200,84 @@ func (p *Presentation) node_htmlFunc(info *PageInfo, node interface{}, linkify b
return buf2.String()
}
// isStructTypeDecl checks whether n is a struct declaration.
// It either returns a non-nil StructType and its name, or zero values.
func isStructTypeDecl(n ast.Node) (st *ast.StructType, name string) {
gd, ok := n.(*ast.GenDecl)
if !ok || gd.Tok != token.TYPE {
return nil, ""
}
if gd.Lparen > 0 {
// Parenthesized type. Who does that, anyway?
// TODO: Reportedly gri does. Fix this to handle that too.
return nil, ""
}
if len(gd.Specs) != 1 {
return nil, ""
}
ts, ok := gd.Specs[0].(*ast.TypeSpec)
if !ok {
return nil, ""
}
st, ok = ts.Type.(*ast.StructType)
if !ok {
return nil, ""
}
return st, ts.Name.Name
}
// addStructFieldIDAttributes modifies the contents of buf such that
// all struct fields of the named struct have <span id='name.Field'>
// in them, so people can link to /#Struct.Field.
func addStructFieldIDAttributes(buf *bytes.Buffer, name string, st *ast.StructType) {
if st.Fields == nil {
return
}
v := buf.Bytes()
buf.Reset()
for _, f := range st.Fields.List {
if len(f.Names) == 0 {
continue
}
fieldName := f.Names[0].Name
commentStart := []byte("// " + fieldName + " ")
if bytes.Contains(v, commentStart) {
// For fields with a doc string of the
// conventional form, we put the new span into
// the comment instead of the field.
// The "conventional" form is a complete sentence
// per https://golang.org/s/style#comment-sentences like:
//
// // Foo is an optional Fooer to foo the foos.
// Foo Fooer
//
// In this case, we want the #StructName.Foo
// link to make the browser go to the comment
// line "Foo is an optional Fooer" instead of
// the "Foo Fooer" line, which could otherwise
// obscure the docs above the browser's "fold".
//
// TODO: do this better, so it works for all
// comments, including unconventional ones.
v = bytes.Replace(v, commentStart, []byte(`<span id="`+name+"."+fieldName+`">// `+fieldName+" </span>"), 1)
} else {
rx := regexp.MustCompile(`(?m)^\s*` + fieldName + `\b`)
var matched bool
v = rx.ReplaceAllFunc(v, func(sub []byte) []byte {
if matched {
return sub
}
matched = true
return []byte(`<span id="` + name + "." + fieldName + `">` + string(sub) + "</span>")
})
}
}
buf.Write(v)
}
func comment_htmlFunc(comment string) string {
var buf bytes.Buffer
// TODO(gri) Provide list of words (e.g. function parameters)

View File

@ -5,6 +5,9 @@
package godoc
import (
"go/ast"
"go/parser"
"go/token"
"testing"
)
@ -116,3 +119,40 @@ func TestSanitizeFunc(t *testing.T) {
}
}
}
// Test that we add <span id="StructName.FieldName"> elements
// to the HTML of struct fields.
func TestStructFieldsIDAttributes(t *testing.T) {
p := &Presentation{
DeclLinks: true,
}
src := []byte(`
package foo
type T struct {
NoDoc string
// Doc has a comment.
Doc string
}
`)
fset := token.NewFileSet()
af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
genDecl := af.Decls[0].(*ast.GenDecl)
pi := &PageInfo{
FSet: fset,
}
got := p.node_htmlFunc(pi, genDecl, true)
want := `type T struct {
<span id="T.NoDoc">NoDoc</span> <a href="/pkg/builtin/#string">string</a>
<span class="comment"><span id="T.Doc">// Doc </span>has a comment.</span>
Doc <a href="/pkg/builtin/#string">string</a>
}`
if got != want {
t.Errorf(" got: %q\nwant: %q\n", got, want)
}
}