diff --git a/godoc/godoc.go b/godoc/godoc.go
index b8a9d0d79..8bda89ad9 100644
--- a/godoc/godoc.go
+++ b/godoc/godoc.go
@@ -233,49 +233,94 @@ 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) {
- // 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(`// `+fieldName+" "), 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(`` + string(sub) + "")
- })
+ scratch.Reset()
+ var added bool
+ foreachLine(buf.Bytes(), func(line []byte) {
+ if !added && isLineForStructFieldID(line, fieldName) {
+ added = true
+ fmt.Fprintf(&scratch, ``, 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(`