Skip to content

Commit 4ab9b60

Browse files
committed
use vendored versions of Go 1.19's go/printer and go/doc/comment
These two packages affect the formatting of gofmt, and by extension gofumpt as well. For example, most changes in formatting to gofmt are done in go/printer, as it implements most of the formatting logic. This tight coupling between gofmt and std packages like go/printer is not a problem for gofmt, as gofmt is released with Go itself. It is very unlikely that any user would build a version of gofmt with a different version of those std packages. gofumpt is a separate Go module, so when users `go install` it, they may currently be using Go 1.18.x or 1.19.x. This can cause problems, as choosing a different major version can easily lead to different behavior. To get the same tight coupling that gofmt has, vendor the two libraries which affect formatting. go/doc/comment is also in this bag since it affects how gofmt reformats godoc comments. Note that this vendoring is not ideal, as it adds code duplication. This shouldn't bloat the size of gofumpt binaries, as they simply swap out the original packages for the vendored ones. For users of the mvdan.cc/gofumpt/format library such as gopls, they may get duplication if they use packages like go/printer elsewhere. This is a small amount of unfortunate bloat, but I don't see a better way to ensure that each version of gofumpt behaves in a consistent way. Note that we're not vendoring std packages for parsing Go code like go/parser or go/token, and neither are we vendoring go/ast. If a project uses new Go syntax, such as generics, they will still need to build gofumpt with a new enough version of Go. The copied code comes from Go 1.19.4 and will be updated regularly. The copying was done manually this time, skipping test files and rewriting imports as necessary. In the future, we an automate it. Also note that we ran gofumpt on the copied code. Fixes #244.
1 parent 021729b commit 4ab9b60

17 files changed

+6291
-3
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ For example:
1515
gofumpt -l -w .
1616

1717
Some of the Go source files in this repository belong to the Go project.
18+
The project includes copies of `go/printer` and `go/doc/comment` as of Go 1.19
19+
to ensure consistent formatting independent of what Go version is being used.
1820
The [added formatting rules](#Added-rules) are implemented in the `format` package.
1921

2022
Note that vendor directories are skipped unless given as explicit arguments.

format/format.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"bytes"
1111
"fmt"
1212
"go/ast"
13-
"go/format"
1413
"go/parser"
1514
"go/token"
1615
"os"
@@ -26,6 +25,7 @@ import (
2625
"golang.org/x/mod/semver"
2726
"golang.org/x/tools/go/ast/astutil"
2827

28+
"mvdan.cc/gofumpt/go1.19/go/format"
2929
"mvdan.cc/gofumpt/internal/version"
3030
)
3131

go1.19/go/doc/comment/doc.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
/*
6+
Package comment implements parsing and reformatting of Go doc comments,
7+
(documentation comments), which are comments that immediately precede
8+
a top-level declaration of a package, const, func, type, or var.
9+
10+
Go doc comment syntax is a simplified subset of Markdown that supports
11+
links, headings, paragraphs, lists (without nesting), and preformatted text blocks.
12+
The details of the syntax are documented at https://go.dev/doc/comment.
13+
14+
To parse the text associated with a doc comment (after removing comment markers),
15+
use a [Parser]:
16+
17+
var p comment.Parser
18+
doc := p.Parse(text)
19+
20+
The result is a [*Doc].
21+
To reformat it as a doc comment, HTML, Markdown, or plain text,
22+
use a [Printer]:
23+
24+
var pr comment.Printer
25+
os.Stdout.Write(pr.Text(doc))
26+
27+
The [Parser] and [Printer] types are structs whose fields can be
28+
modified to customize the operations.
29+
For details, see the documentation for those types.
30+
31+
Use cases that need additional control over reformatting can
32+
implement their own logic by inspecting the parsed syntax itself.
33+
See the documentation for [Doc], [Block], [Text] for an overview
34+
and links to additional types.
35+
*/
36+
package comment

go1.19/go/doc/comment/html.go

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package comment
6+
7+
import (
8+
"bytes"
9+
"fmt"
10+
"strconv"
11+
)
12+
13+
// An htmlPrinter holds the state needed for printing a Doc as HTML.
14+
type htmlPrinter struct {
15+
*Printer
16+
tight bool
17+
}
18+
19+
// HTML returns an HTML formatting of the Doc.
20+
// See the [Printer] documentation for ways to customize the HTML output.
21+
func (p *Printer) HTML(d *Doc) []byte {
22+
hp := &htmlPrinter{Printer: p}
23+
var out bytes.Buffer
24+
for _, x := range d.Content {
25+
hp.block(&out, x)
26+
}
27+
return out.Bytes()
28+
}
29+
30+
// block prints the block x to out.
31+
func (p *htmlPrinter) block(out *bytes.Buffer, x Block) {
32+
switch x := x.(type) {
33+
default:
34+
fmt.Fprintf(out, "?%T", x)
35+
36+
case *Paragraph:
37+
if !p.tight {
38+
out.WriteString("<p>")
39+
}
40+
p.text(out, x.Text)
41+
out.WriteString("\n")
42+
43+
case *Heading:
44+
out.WriteString("<h")
45+
h := strconv.Itoa(p.headingLevel())
46+
out.WriteString(h)
47+
if id := p.headingID(x); id != "" {
48+
out.WriteString(` id="`)
49+
p.escape(out, id)
50+
out.WriteString(`"`)
51+
}
52+
out.WriteString(">")
53+
p.text(out, x.Text)
54+
out.WriteString("</h")
55+
out.WriteString(h)
56+
out.WriteString(">\n")
57+
58+
case *Code:
59+
out.WriteString("<pre>")
60+
p.escape(out, x.Text)
61+
out.WriteString("</pre>\n")
62+
63+
case *List:
64+
kind := "ol>\n"
65+
if x.Items[0].Number == "" {
66+
kind = "ul>\n"
67+
}
68+
out.WriteString("<")
69+
out.WriteString(kind)
70+
next := "1"
71+
for _, item := range x.Items {
72+
out.WriteString("<li")
73+
if n := item.Number; n != "" {
74+
if n != next {
75+
out.WriteString(` value="`)
76+
out.WriteString(n)
77+
out.WriteString(`"`)
78+
next = n
79+
}
80+
next = inc(next)
81+
}
82+
out.WriteString(">")
83+
p.tight = !x.BlankBetween()
84+
for _, blk := range item.Content {
85+
p.block(out, blk)
86+
}
87+
p.tight = false
88+
}
89+
out.WriteString("</")
90+
out.WriteString(kind)
91+
}
92+
}
93+
94+
// inc increments the decimal string s.
95+
// For example, inc("1199") == "1200".
96+
func inc(s string) string {
97+
b := []byte(s)
98+
for i := len(b) - 1; i >= 0; i-- {
99+
if b[i] < '9' {
100+
b[i]++
101+
return string(b)
102+
}
103+
b[i] = '0'
104+
}
105+
return "1" + string(b)
106+
}
107+
108+
// text prints the text sequence x to out.
109+
func (p *htmlPrinter) text(out *bytes.Buffer, x []Text) {
110+
for _, t := range x {
111+
switch t := t.(type) {
112+
case Plain:
113+
p.escape(out, string(t))
114+
case Italic:
115+
out.WriteString("<i>")
116+
p.escape(out, string(t))
117+
out.WriteString("</i>")
118+
case *Link:
119+
out.WriteString(`<a href="`)
120+
p.escape(out, t.URL)
121+
out.WriteString(`">`)
122+
p.text(out, t.Text)
123+
out.WriteString("</a>")
124+
case *DocLink:
125+
url := p.docLinkURL(t)
126+
if url != "" {
127+
out.WriteString(`<a href="`)
128+
p.escape(out, url)
129+
out.WriteString(`">`)
130+
}
131+
p.text(out, t.Text)
132+
if url != "" {
133+
out.WriteString("</a>")
134+
}
135+
}
136+
}
137+
}
138+
139+
// escape prints s to out as plain text,
140+
// escaping < & " ' and > to avoid being misinterpreted
141+
// in larger HTML constructs.
142+
func (p *htmlPrinter) escape(out *bytes.Buffer, s string) {
143+
start := 0
144+
for i := 0; i < len(s); i++ {
145+
switch s[i] {
146+
case '<':
147+
out.WriteString(s[start:i])
148+
out.WriteString("&lt;")
149+
start = i + 1
150+
case '&':
151+
out.WriteString(s[start:i])
152+
out.WriteString("&amp;")
153+
start = i + 1
154+
case '"':
155+
out.WriteString(s[start:i])
156+
out.WriteString("&quot;")
157+
start = i + 1
158+
case '\'':
159+
out.WriteString(s[start:i])
160+
out.WriteString("&apos;")
161+
start = i + 1
162+
case '>':
163+
out.WriteString(s[start:i])
164+
out.WriteString("&gt;")
165+
start = i + 1
166+
}
167+
}
168+
out.WriteString(s[start:])
169+
}

0 commit comments

Comments
 (0)