mirror of
https://github.com/golang/go
synced 2024-11-05 17:46:16 -07:00
godoc: update struct field anchor code
Now without regexps and allocations. And also match comments like: // Foo, if non-nil, ... The comma confused the old pattern. Updates golang/go#16753 Change-Id: I9016ee7b5933ea343950a39989952804c74a598b Reviewed-on: https://go-review.googlesource.com/33755 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Chris Broadfoot <cbro@golang.org>
This commit is contained in:
parent
0f86c627e2
commit
e5f9a3deee
@ -233,17 +233,48 @@ func addStructFieldIDAttributes(buf *bytes.Buffer, name string, st *ast.StructTy
|
||||
if st.Fields == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v := buf.Bytes()
|
||||
buf.Reset()
|
||||
|
||||
var scratch bytes.Buffer
|
||||
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) {
|
||||
scratch.Reset()
|
||||
var added bool
|
||||
foreachLine(buf.Bytes(), func(line []byte) {
|
||||
if !added && isLineForStructFieldID(line, fieldName) {
|
||||
added = true
|
||||
fmt.Fprintf(&scratch, `<span id="%s.%s"></span>`, name, fieldName)
|
||||
}
|
||||
scratch.Write(line)
|
||||
})
|
||||
buf.Reset()
|
||||
buf.Write(scratch.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// foreachLine calls fn for each line of in, where a line includes
|
||||
// the trailing "\n", except on the last line, if it doesn't exist.
|
||||
func foreachLine(in []byte, fn func(line []byte)) {
|
||||
for len(in) > 0 {
|
||||
nl := bytes.IndexByte(in, '\n')
|
||||
if nl == -1 {
|
||||
fn(in)
|
||||
return
|
||||
}
|
||||
fn(in[:nl+1])
|
||||
in = in[nl+1:]
|
||||
}
|
||||
}
|
||||
|
||||
// commentPrefix is the line prefix for comments after they've been HTMLified.
|
||||
var commentPrefix = []byte(`<span class="comment">// `)
|
||||
|
||||
// isLineForStructFieldID reports whether line is a line we should
|
||||
// add a <span id="#StructName.FieldName"> to. Only the fieldName is provided.
|
||||
func isLineForStructFieldID(line []byte, fieldName string) bool {
|
||||
line = bytes.TrimSpace(line)
|
||||
|
||||
// For fields with a doc string of the
|
||||
// conventional form, we put the new span into
|
||||
// the comment instead of the field.
|
||||
@ -261,21 +292,35 @@ func addStructFieldIDAttributes(buf *bytes.Buffer, name string, st *ast.StructTy
|
||||
//
|
||||
// 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
|
||||
// For comments
|
||||
if bytes.HasPrefix(line, commentPrefix) {
|
||||
if matchesIdentBoundary(line[len(commentPrefix):], fieldName) {
|
||||
return true
|
||||
}
|
||||
matched = true
|
||||
return []byte(`<span id="` + name + "." + fieldName + `">` + string(sub) + "</span>")
|
||||
})
|
||||
}
|
||||
return matchesIdentBoundary(line, fieldName)
|
||||
}
|
||||
|
||||
buf.Write(v)
|
||||
// matchesIdentBoundary reports whether line matches /^ident\b/.
|
||||
// A boundary is considered either none, or an ASCII non-alphanum.
|
||||
func matchesIdentBoundary(line []byte, ident string) bool {
|
||||
if len(line) < len(ident) {
|
||||
return false
|
||||
}
|
||||
if string(line[:len(ident)]) != ident {
|
||||
return false
|
||||
}
|
||||
rest := line[len(ident):]
|
||||
return len(rest) == 0 || !isASCIIWordChar(rest[0])
|
||||
}
|
||||
|
||||
// isASCIIWordChar reports whether b is an ASCII "word"
|
||||
// character. (Matching /\w/ in ASCII mode)
|
||||
func isASCIIWordChar(b byte) bool {
|
||||
return 'a' <= b && b <= 'z' ||
|
||||
'A' <= b && b <= 'Z' ||
|
||||
'0' <= b && b <= '0' ||
|
||||
b == '_'
|
||||
}
|
||||
|
||||
func comment_htmlFunc(comment string) string {
|
||||
|
@ -134,6 +134,9 @@ type T struct {
|
||||
|
||||
// Doc has a comment.
|
||||
Doc string
|
||||
|
||||
// Opt, if non-nil, is an option.
|
||||
Opt *int
|
||||
}
|
||||
`)
|
||||
fset := token.NewFileSet()
|
||||
@ -147,12 +150,15 @@ type T struct {
|
||||
}
|
||||
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 id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a>
|
||||
|
||||
<span class="comment"><span id="T.Doc">// Doc </span>has a comment.</span>
|
||||
<span id="T.Doc"></span><span class="comment">// Doc has a comment.</span>
|
||||
Doc <a href="/pkg/builtin/#string">string</a>
|
||||
|
||||
<span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span>
|
||||
Opt *<a href="/pkg/builtin/#int">int</a>
|
||||
}`
|
||||
if got != want {
|
||||
t.Errorf(" got: %q\nwant: %q\n", got, want)
|
||||
t.Errorf("got: %s\n\nwant: %s\n", got, want)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user