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:
parent
34fe8ce027
commit
0f65b31aee
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user