]> Cypherpunks repositories - gostls13.git/commitdiff
go/doc/comment: parse and print explicit links
authorRuss Cox <rsc@golang.org>
Sun, 3 Apr 2022 20:16:46 +0000 (16:16 -0400)
committerRuss Cox <rsc@golang.org>
Mon, 11 Apr 2022 16:31:43 +0000 (16:31 +0000)
[This CL is part of a sequence implementing the proposal #51082.
The design doc is at https://go.dev/s/godocfmt-design.]

Implement parsing and printing of explicit links, like:

Visit the [Go home page].

[Go home page]: https://go.dev

For #51082.

Change-Id: If8104e45558314dae0346df614b03d5664421cf1
Reviewed-on: https://go-review.googlesource.com/c/go/+/397282
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/go/doc/comment/parse.go
src/go/doc/comment/print.go
src/go/doc/comment/testdata/link2.txt [new file with mode: 0644]
src/go/doc/comment/testdata/link3.txt [new file with mode: 0644]
src/go/doc/comment/testdata/link4.txt [new file with mode: 0644]
src/go/doc/comment/testdata/link5.txt [new file with mode: 0644]
src/go/doc/comment/testdata/link6.txt [new file with mode: 0644]
src/go/doc/comment/testdata/link7.txt [new file with mode: 0644]
src/go/doc/comment/text.go

index af7e1931d919c8d7aa7d91cd172b5ff33dec8ed8..920b446c7e9880940335d7ed024440de20f0c67d 100644 (file)
@@ -303,7 +303,9 @@ func (p *Parser) Parse(text string) *Doc {
                if line != "" {
                        var b Block
                        b, lines = d.paragraph(lines)
-                       d.Content = append(d.Content, b)
+                       if b != nil {
+                               d.Content = append(d.Content, b)
+                       }
                } else {
                        lines = lines[1:]
                }
@@ -434,7 +436,7 @@ func isOldHeading(line string, all []string, off int) bool {
        return true
 }
 
-// parargraph returns a paragraph block built from the
+// paragraph returns a paragraph block built from the
 // unindented text at the start of lines, along with the remainder of the lines.
 // If there is no unindented text at the start of lines,
 // then paragraph returns a nil Block.
@@ -452,9 +454,53 @@ func (d *parseDoc) paragraph(lines []string) (b Block, rest []string) {
                return nil, rest
        }
 
+       // Is this a block of known links? Handle.
+       var defs []*LinkDef
+       for _, line := range lines {
+               def, ok := parseLink(line)
+               if !ok {
+                       goto NoDefs
+               }
+               defs = append(defs, def)
+       }
+       for _, def := range defs {
+               d.Links = append(d.Links, def)
+               if d.links[def.Text] == nil {
+                       d.links[def.Text] = def
+               }
+       }
+       return nil, rest
+NoDefs:
+
        return &Paragraph{Text: []Text{Plain(strings.Join(lines, "\n"))}}, rest
 }
 
+// parseLink parses a single link definition line:
+//     [text]: url
+// It returns the link definition and whether the line was well formed.
+func parseLink(line string) (*LinkDef, bool) {
+       if line == "" || line[0] != '[' {
+               return nil, false
+       }
+       i := strings.Index(line, "]:")
+       if i < 0 || i+3 >= len(line) || (line[i+2] != ' ' && line[i+2] != '\t') {
+               return nil, false
+       }
+
+       text := line[1:i]
+       url := strings.TrimSpace(line[i+3:])
+       j := strings.Index(url, "://")
+       if j < 0 || !isScheme(url[:j]) {
+               return nil, false
+       }
+
+       // Line has right form and has valid scheme://.
+       // That's good enough for us - we are not as picky
+       // about the characters beyond the :// as we are
+       // when extracting inline URLs from text.
+       return &LinkDef{Text: text, URL: url}, true
+}
+
 // parseLinkedText parses text that is allowed to contain explicit links,
 // such as [math.Sin] or [Go home page], into a slice of Text items.
 //
index 4f316fb75c9dc5ad9df65aeb32db8c001d5ffc42..2ef8d7375deda9fb8412c0c9a7f98cbddc4a1e37 100644 (file)
@@ -121,6 +121,29 @@ func (p *Printer) Comment(d *Doc) []byte {
                cp.block(&out, x)
        }
 
+       // Print one block containing all the link definitions that were used,
+       // and then a second block containing all the unused ones.
+       // This makes it easy to clean up the unused ones: gofmt and
+       // delete the final block. And it's a nice visual signal without
+       // affecting the way the comment formats for users.
+       for i := 0; i < 2; i++ {
+               used := i == 0
+               first := true
+               for _, def := range d.Links {
+                       if def.Used == used {
+                               if first {
+                                       out.WriteString("\n")
+                                       first = false
+                               }
+                               out.WriteString("[")
+                               out.WriteString(def.Text)
+                               out.WriteString("]: ")
+                               out.WriteString(def.URL)
+                               out.WriteString("\n")
+                       }
+               }
+       }
+
        return out.Bytes()
 }
 
@@ -154,7 +177,13 @@ func (p *commentPrinter) text(out *bytes.Buffer, indent string, x []Text) {
                case Italic:
                        p.indent(out, indent, string(t))
                case *Link:
-                       p.text(out, indent, t.Text)
+                       if t.Auto {
+                               p.text(out, indent, t.Text)
+                       } else {
+                               out.WriteString("[")
+                               p.text(out, indent, t.Text)
+                               out.WriteString("]")
+                       }
                case *DocLink:
                        out.WriteString("[")
                        p.text(out, indent, t.Text)
diff --git a/src/go/doc/comment/testdata/link2.txt b/src/go/doc/comment/testdata/link2.txt
new file mode 100644 (file)
index 0000000..a19835c
--- /dev/null
@@ -0,0 +1,29 @@
+-- input --
+The Go home page is https://go.dev/.
+It used to be https://golang.org.
+https:// is not a link.
+Nor is https://
+https://☺ is not a link.
+https://:80 is not a link.
+
+-- gofmt --
+The Go home page is https://go.dev/.
+It used to be https://golang.org.
+https:// is not a link.
+Nor is https://
+https://☺ is not a link.
+https://:80 is not a link.
+
+-- text --
+The Go home page is https://go.dev/. It used to be https://golang.org. https:// is not a link. Nor is https:// https://☺ is not a link. https://:80 is not a link.
+
+-- markdown --
+The Go home page is [https://go.dev/](https://go.dev/). It used to be [https://golang.org](https://golang.org). https:// is not a link. Nor is https:// https://☺ is not a link. https://:80 is not a link.
+
+-- html --
+<p>The Go home page is <a href="https://go.dev/">https://go.dev/</a>.
+It used to be <a href="https://golang.org">https://golang.org</a>.
+https:// is not a link.
+Nor is https://
+https://☺ is not a link.
+https://:80 is not a link.
diff --git a/src/go/doc/comment/testdata/link3.txt b/src/go/doc/comment/testdata/link3.txt
new file mode 100644 (file)
index 0000000..5a115b5
--- /dev/null
@@ -0,0 +1,14 @@
+-- input --
+Doc text.
+
+[Go home page]: https://go.dev
+-- gofmt --
+Doc text.
+
+[Go home page]: https://go.dev
+-- text --
+Doc text.
+-- markdown --
+Doc text.
+-- html --
+<p>Doc text.
diff --git a/src/go/doc/comment/testdata/link4.txt b/src/go/doc/comment/testdata/link4.txt
new file mode 100644 (file)
index 0000000..75f194c
--- /dev/null
@@ -0,0 +1,77 @@
+-- input --
+These are not links.
+
+[x
+
+[x]:
+
+[x]:https://go.dev
+
+[x]https://go.dev
+
+[x]: surprise://go.dev
+
+[x]: surprise!
+
+But this is, with a tab (although it's unused).
+
+[z]:   https://go.dev
+-- gofmt --
+These are not links.
+
+[x
+
+[x]:
+
+[x]:https://go.dev
+
+[x]https://go.dev
+
+[x]: surprise://go.dev
+
+[x]: surprise!
+
+But this is, with a tab (although it's unused).
+
+[z]: https://go.dev
+-- text --
+These are not links.
+
+[x
+
+[x]:
+
+[x]:https://go.dev
+
+[x]https://go.dev
+
+[x]: surprise://go.dev
+
+[x]: surprise!
+
+But this is, with a tab (although it's unused).
+-- markdown --
+These are not links.
+
+\[x
+
+\[x]:
+
+\[x]:[https://go.dev](https://go.dev)
+
+\[x][https://go.dev](https://go.dev)
+
+\[x]: surprise://go.dev
+
+\[x]: surprise!
+
+But this is, with a tab (although it's unused).
+-- html --
+<p>These are not links.
+<p>[x
+<p>[x]:
+<p>[x]:<a href="https://go.dev">https://go.dev</a>
+<p>[x]<a href="https://go.dev">https://go.dev</a>
+<p>[x]: surprise://go.dev
+<p>[x]: surprise!
+<p>But this is, with a tab (although it&apos;s unused).
diff --git a/src/go/doc/comment/testdata/link5.txt b/src/go/doc/comment/testdata/link5.txt
new file mode 100644 (file)
index 0000000..b4fb588
--- /dev/null
@@ -0,0 +1,36 @@
+-- input --
+See the [Go home page] and the [pkg
+site].
+
+[Go home page]: https://go.dev/
+[pkg site]: https://pkg.go.dev
+[Go home page]: https://duplicate.ignored
+
+They're really great!
+
+-- gofmt --
+See the [Go home page] and the [pkg
+site].
+
+They're really great!
+
+[Go home page]: https://go.dev/
+[pkg site]: https://pkg.go.dev
+
+[Go home page]: https://duplicate.ignored
+
+-- text --
+See the Go home page and the pkg site.
+
+They're really great!
+
+[Go home page]: https://go.dev/
+[pkg site]: https://pkg.go.dev
+-- markdown --
+See the [Go home page](https://go.dev/) and the [pkg site](https://pkg.go.dev).
+
+They're really great!
+-- html --
+<p>See the <a href="https://go.dev/">Go home page</a> and the <a href="https://pkg.go.dev">pkg
+site</a>.
+<p>They&apos;re really great!
diff --git a/src/go/doc/comment/testdata/link6.txt b/src/go/doc/comment/testdata/link6.txt
new file mode 100644 (file)
index 0000000..579b35d
--- /dev/null
@@ -0,0 +1,47 @@
+-- input --
+URLs with punctuation are hard.
+We don't want to consume the end-of-sentence punctuation.
+
+For example, https://en.wikipedia.org/wiki/John_Adams_(miniseries).
+And https://example.com/[foo]/bar{.
+And https://example.com/(foo)/bar!
+And https://example.com/{foo}/bar{.
+And https://example.com/)baz{foo}.
+
+[And https://example.com/].
+
+-- gofmt --
+URLs with punctuation are hard.
+We don't want to consume the end-of-sentence punctuation.
+
+For example, https://en.wikipedia.org/wiki/John_Adams_(miniseries).
+And https://example.com/[foo]/bar{.
+And https://example.com/(foo)/bar!
+And https://example.com/{foo}/bar{.
+And https://example.com/)baz{foo}.
+
+[And https://example.com/].
+
+-- text --
+URLs with punctuation are hard. We don't want to consume the end-of-sentence punctuation.
+
+For example, https://en.wikipedia.org/wiki/John_Adams_(miniseries). And https://example.com/[foo]/bar{. And https://example.com/(foo)/bar! And https://example.com/{foo}/bar{. And https://example.com/)baz{foo}.
+
+[And https://example.com/].
+
+-- markdown --
+URLs with punctuation are hard. We don't want to consume the end-of-sentence punctuation.
+
+For example, [https://en.wikipedia.org/wiki/John\_Adams\_(miniseries)](https://en.wikipedia.org/wiki/John_Adams_(miniseries)). And [https://example.com/\[foo]/bar](https://example.com/[foo]/bar){. And [https://example.com/(foo)/bar](https://example.com/(foo)/bar)! And [https://example.com/{foo}/bar](https://example.com/{foo}/bar){. And [https://example.com/](https://example.com/))baz{foo}.
+
+\[And [https://example.com/](https://example.com/)].
+
+-- html --
+<p>URLs with punctuation are hard.
+We don&apos;t want to consume the end-of-sentence punctuation.
+<p>For example, <a href="https://en.wikipedia.org/wiki/John_Adams_(miniseries)">https://en.wikipedia.org/wiki/John_Adams_(miniseries)</a>.
+And <a href="https://example.com/[foo]/bar">https://example.com/[foo]/bar</a>{.
+And <a href="https://example.com/(foo)/bar">https://example.com/(foo)/bar</a>!
+And <a href="https://example.com/{foo}/bar">https://example.com/{foo}/bar</a>{.
+And <a href="https://example.com/">https://example.com/</a>)baz{foo}.
+<p>[And <a href="https://example.com/">https://example.com/</a>].
diff --git a/src/go/doc/comment/testdata/link7.txt b/src/go/doc/comment/testdata/link7.txt
new file mode 100644 (file)
index 0000000..89a8b31
--- /dev/null
@@ -0,0 +1,25 @@
+-- input --
+[math] is a package but this is not a doc link.
+
+[io] is a doc link.
+
+[math]: https://example.com
+-- gofmt --
+[math] is a package but this is not a doc link.
+
+[io] is a doc link.
+
+[math]: https://example.com
+-- text --
+math is a package but this is not a doc link.
+
+io is a doc link.
+
+[math]: https://example.com
+-- markdown --
+[math](https://example.com) is a package but this is not a doc link.
+
+[io](/io) is a doc link.
+-- html --
+<p><a href="https://example.com">math</a> is a package but this is not a doc link.
+<p><a href="/io">io</a> is a doc link.
index 531675d5a44e6115156aa4270abceb16b062c6a6..e9941bc957161bd179ed860655ffd3ed0eaf44f7 100644 (file)
@@ -29,6 +29,21 @@ func (p *Printer) Text(d *Doc) []byte {
                }
                tp.block(&out, x)
        }
+       anyUsed := false
+       for _, def := range d.Links {
+               if def.Used {
+                       anyUsed = true
+                       break
+               }
+       }
+       if anyUsed {
+               writeNL(&out)
+               for _, def := range d.Links {
+                       if def.Used {
+                               fmt.Fprintf(&out, "[%s]: %s\n", def.Text, def.URL)
+                       }
+               }
+       }
        return out.Bytes()
 }