0 && isSpace(src[i-1]) {
168 | i--
169 | }
170 | return append(res, src[i:]...), nil
171 | }
172 |
173 | // isSpace reports whether the byte is a space character.
174 | // isSpace defines a space as being among the following bytes: ' ', '\t', '\n' and '\r'.
175 | func isSpace(b byte) bool {
176 | return b == ' ' || b == '\t' || b == '\n' || b == '\r'
177 | }
178 |
--------------------------------------------------------------------------------
/internal/govendor/diff/diff.go:
--------------------------------------------------------------------------------
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 diff
6 |
7 | import (
8 | "bytes"
9 | "fmt"
10 | "sort"
11 | "strings"
12 | )
13 |
14 | // A pair is a pair of values tracked for both the x and y side of a diff.
15 | // It is typically a pair of line indexes.
16 | type pair struct{ x, y int }
17 |
18 | // Diff returns an anchored diff of the two texts old and new
19 | // in the “unified diff” format. If old and new are identical,
20 | // Diff returns a nil slice (no output).
21 | //
22 | // Unix diff implementations typically look for a diff with
23 | // the smallest number of lines inserted and removed,
24 | // which can in the worst case take time quadratic in the
25 | // number of lines in the texts. As a result, many implementations
26 | // either can be made to run for a long time or cut off the search
27 | // after a predetermined amount of work.
28 | //
29 | // In contrast, this implementation looks for a diff with the
30 | // smallest number of “unique” lines inserted and removed,
31 | // where unique means a line that appears just once in both old and new.
32 | // We call this an “anchored diff” because the unique lines anchor
33 | // the chosen matching regions. An anchored diff is usually clearer
34 | // than a standard diff, because the algorithm does not try to
35 | // reuse unrelated blank lines or closing braces.
36 | // The algorithm also guarantees to run in O(n log n) time
37 | // instead of the standard O(n²) time.
38 | //
39 | // Some systems call this approach a “patience diff,” named for
40 | // the “patience sorting” algorithm, itself named for a solitaire card game.
41 | // We avoid that name for two reasons. First, the name has been used
42 | // for a few different variants of the algorithm, so it is imprecise.
43 | // Second, the name is frequently interpreted as meaning that you have
44 | // to wait longer (to be patient) for the diff, meaning that it is a slower algorithm,
45 | // when in fact the algorithm is faster than the standard one.
46 | func Diff(oldName string, old []byte, newName string, new []byte) []byte {
47 | if bytes.Equal(old, new) {
48 | return nil
49 | }
50 | x := lines(old)
51 | y := lines(new)
52 |
53 | // Print diff header.
54 | var out bytes.Buffer
55 | fmt.Fprintf(&out, "diff %s %s\n", oldName, newName)
56 | fmt.Fprintf(&out, "--- %s\n", oldName)
57 | fmt.Fprintf(&out, "+++ %s\n", newName)
58 |
59 | // Loop over matches to consider,
60 | // expanding each match to include surrounding lines,
61 | // and then printing diff chunks.
62 | // To avoid setup/teardown cases outside the loop,
63 | // tgs returns a leading {0,0} and trailing {len(x), len(y)} pair
64 | // in the sequence of matches.
65 | var (
66 | done pair // printed up to x[:done.x] and y[:done.y]
67 | chunk pair // start lines of current chunk
68 | count pair // number of lines from each side in current chunk
69 | ctext []string // lines for current chunk
70 | )
71 | for _, m := range tgs(x, y) {
72 | if m.x < done.x {
73 | // Already handled scanning forward from earlier match.
74 | continue
75 | }
76 |
77 | // Expand matching lines as far as possible,
78 | // establishing that x[start.x:end.x] == y[start.y:end.y].
79 | // Note that on the first (or last) iteration we may (or definitely do)
80 | // have an empty match: start.x==end.x and start.y==end.y.
81 | start := m
82 | for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] {
83 | start.x--
84 | start.y--
85 | }
86 | end := m
87 | for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] {
88 | end.x++
89 | end.y++
90 | }
91 |
92 | // Emit the mismatched lines before start into this chunk.
93 | // (No effect on first sentinel iteration, when start = {0,0}.)
94 | for _, s := range x[done.x:start.x] {
95 | ctext = append(ctext, "-"+s)
96 | count.x++
97 | }
98 | for _, s := range y[done.y:start.y] {
99 | ctext = append(ctext, "+"+s)
100 | count.y++
101 | }
102 |
103 | // If we're not at EOF and have too few common lines,
104 | // the chunk includes all the common lines and continues.
105 | const C = 3 // number of context lines
106 | if (end.x < len(x) || end.y < len(y)) &&
107 | (end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) {
108 | for _, s := range x[start.x:end.x] {
109 | ctext = append(ctext, " "+s)
110 | count.x++
111 | count.y++
112 | }
113 | done = end
114 | continue
115 | }
116 |
117 | // End chunk with common lines for context.
118 | if len(ctext) > 0 {
119 | n := end.x - start.x
120 | if n > C {
121 | n = C
122 | }
123 | for _, s := range x[start.x : start.x+n] {
124 | ctext = append(ctext, " "+s)
125 | count.x++
126 | count.y++
127 | }
128 | done = pair{start.x + n, start.y + n}
129 |
130 | // Format and emit chunk.
131 | // Convert line numbers to 1-indexed.
132 | // Special case: empty file shows up as 0,0 not 1,0.
133 | if count.x > 0 {
134 | chunk.x++
135 | }
136 | if count.y > 0 {
137 | chunk.y++
138 | }
139 | fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", chunk.x, count.x, chunk.y, count.y)
140 | for _, s := range ctext {
141 | out.WriteString(s)
142 | }
143 | count.x = 0
144 | count.y = 0
145 | ctext = ctext[:0]
146 | }
147 |
148 | // If we reached EOF, we're done.
149 | if end.x >= len(x) && end.y >= len(y) {
150 | break
151 | }
152 |
153 | // Otherwise start a new chunk.
154 | chunk = pair{end.x - C, end.y - C}
155 | for _, s := range x[chunk.x:end.x] {
156 | ctext = append(ctext, " "+s)
157 | count.x++
158 | count.y++
159 | }
160 | done = end
161 | }
162 |
163 | return out.Bytes()
164 | }
165 |
166 | // lines returns the lines in the file x, including newlines.
167 | // If the file does not end in a newline, one is supplied
168 | // along with a warning about the missing newline.
169 | func lines(x []byte) []string {
170 | l := strings.SplitAfter(string(x), "\n")
171 | if l[len(l)-1] == "" {
172 | l = l[:len(l)-1]
173 | } else {
174 | // Treat last line as having a message about the missing newline attached,
175 | // using the same text as BSD/GNU diff (including the leading backslash).
176 | l[len(l)-1] += "\n\\ No newline at end of file\n"
177 | }
178 | return l
179 | }
180 |
181 | // tgs returns the pairs of indexes of the longest common subsequence
182 | // of unique lines in x and y, where a unique line is one that appears
183 | // once in x and once in y.
184 | //
185 | // The longest common subsequence algorithm is as described in
186 | // Thomas G. Szymanski, “A Special Case of the Maximal Common
187 | // Subsequence Problem,” Princeton TR #170 (January 1975),
188 | // available at https://research.swtch.com/tgs170.pdf.
189 | func tgs(x, y []string) []pair {
190 | // Count the number of times each string appears in a and b.
191 | // We only care about 0, 1, many, counted as 0, -1, -2
192 | // for the x side and 0, -4, -8 for the y side.
193 | // Using negative numbers now lets us distinguish positive line numbers later.
194 | m := make(map[string]int)
195 | for _, s := range x {
196 | if c := m[s]; c > -2 {
197 | m[s] = c - 1
198 | }
199 | }
200 | for _, s := range y {
201 | if c := m[s]; c > -8 {
202 | m[s] = c - 4
203 | }
204 | }
205 |
206 | // Now unique strings can be identified by m[s] = -1+-4.
207 | //
208 | // Gather the indexes of those strings in x and y, building:
209 | // xi[i] = increasing indexes of unique strings in x.
210 | // yi[i] = increasing indexes of unique strings in y.
211 | // inv[i] = index j such that x[xi[i]] = y[yi[j]].
212 | var xi, yi, inv []int
213 | for i, s := range y {
214 | if m[s] == -1+-4 {
215 | m[s] = len(yi)
216 | yi = append(yi, i)
217 | }
218 | }
219 | for i, s := range x {
220 | if j, ok := m[s]; ok && j >= 0 {
221 | xi = append(xi, i)
222 | inv = append(inv, j)
223 | }
224 | }
225 |
226 | // Apply Algorithm A from Szymanski's paper.
227 | // In those terms, A = J = inv and B = [0, n).
228 | // We add sentinel pairs {0,0}, and {len(x),len(y)}
229 | // to the returned sequence, to help the processing loop.
230 | J := inv
231 | n := len(xi)
232 | T := make([]int, n)
233 | L := make([]int, n)
234 | for i := range T {
235 | T[i] = n + 1
236 | }
237 | for i := 0; i < n; i++ {
238 | k := sort.Search(n, func(k int) bool {
239 | return T[k] >= J[i]
240 | })
241 | T[k] = J[i]
242 | L[i] = k + 1
243 | }
244 | k := 0
245 | for _, v := range L {
246 | if k < v {
247 | k = v
248 | }
249 | }
250 | seq := make([]pair, 2+k)
251 | seq[1+k] = pair{len(x), len(y)} // sentinel at end
252 | lastj := n
253 | for i := n - 1; i >= 0; i-- {
254 | if L[i] == k && J[i] < lastj {
255 | seq[k] = pair{xi[i], yi[J[i]]}
256 | k--
257 | }
258 | }
259 | seq[0] = pair{0, 0} // sentinel at start
260 | return seq
261 | }
262 |
--------------------------------------------------------------------------------
/internal/govendor/go/doc/comment/doc.go:
--------------------------------------------------------------------------------
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
37 |
--------------------------------------------------------------------------------
/internal/govendor/go/doc/comment/html.go:
--------------------------------------------------------------------------------
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("")
39 | }
40 | p.text(out, x.Text)
41 | out.WriteString("\n")
42 |
43 | case *Heading:
44 | out.WriteString("")
53 | p.text(out, x.Text)
54 | out.WriteString("\n")
57 |
58 | case *Code:
59 | out.WriteString("
")
60 | p.escape(out, x.Text)
61 | out.WriteString("
\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("")
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("")
116 | p.escape(out, string(t))
117 | out.WriteString("")
118 | case *Link:
119 | out.WriteString(``)
122 | p.text(out, t.Text)
123 | out.WriteString("")
124 | case *DocLink:
125 | url := p.docLinkURL(t)
126 | if url != "" {
127 | out.WriteString(``)
130 | }
131 | p.text(out, t.Text)
132 | if url != "" {
133 | out.WriteString("")
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("<")
149 | start = i + 1
150 | case '&':
151 | out.WriteString(s[start:i])
152 | out.WriteString("&")
153 | start = i + 1
154 | case '"':
155 | out.WriteString(s[start:i])
156 | out.WriteString(""")
157 | start = i + 1
158 | case '\'':
159 | out.WriteString(s[start:i])
160 | out.WriteString("'")
161 | start = i + 1
162 | case '>':
163 | out.WriteString(s[start:i])
164 | out.WriteString(">")
165 | start = i + 1
166 | }
167 | }
168 | out.WriteString(s[start:])
169 | }
170 |
--------------------------------------------------------------------------------
/internal/govendor/go/doc/comment/markdown.go:
--------------------------------------------------------------------------------
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 | "strings"
11 | )
12 |
13 | // An mdPrinter holds the state needed for printing a Doc as Markdown.
14 | type mdPrinter struct {
15 | *Printer
16 | headingPrefix string
17 | raw bytes.Buffer
18 | }
19 |
20 | // Markdown returns a Markdown formatting of the Doc.
21 | // See the [Printer] documentation for ways to customize the Markdown output.
22 | func (p *Printer) Markdown(d *Doc) []byte {
23 | mp := &mdPrinter{
24 | Printer: p,
25 | headingPrefix: strings.Repeat("#", p.headingLevel()) + " ",
26 | }
27 |
28 | var out bytes.Buffer
29 | for i, x := range d.Content {
30 | if i > 0 {
31 | out.WriteByte('\n')
32 | }
33 | mp.block(&out, x)
34 | }
35 | return out.Bytes()
36 | }
37 |
38 | // block prints the block x to out.
39 | func (p *mdPrinter) block(out *bytes.Buffer, x Block) {
40 | switch x := x.(type) {
41 | default:
42 | fmt.Fprintf(out, "?%T", x)
43 |
44 | case *Paragraph:
45 | p.text(out, x.Text)
46 | out.WriteString("\n")
47 |
48 | case *Heading:
49 | out.WriteString(p.headingPrefix)
50 | p.text(out, x.Text)
51 | if id := p.headingID(x); id != "" {
52 | out.WriteString(" {#")
53 | out.WriteString(id)
54 | out.WriteString("}")
55 | }
56 | out.WriteString("\n")
57 |
58 | case *Code:
59 | md := x.Text
60 | for md != "" {
61 | var line string
62 | line, md, _ = strings.Cut(md, "\n")
63 | if line != "" {
64 | out.WriteString("\t")
65 | out.WriteString(line)
66 | }
67 | out.WriteString("\n")
68 | }
69 |
70 | case *List:
71 | loose := x.BlankBetween()
72 | for i, item := range x.Items {
73 | if i > 0 && loose {
74 | out.WriteString("\n")
75 | }
76 | if n := item.Number; n != "" {
77 | out.WriteString(" ")
78 | out.WriteString(n)
79 | out.WriteString(". ")
80 | } else {
81 | out.WriteString(" - ") // SP SP - SP
82 | }
83 | for i, blk := range item.Content {
84 | const fourSpace = " "
85 | if i > 0 {
86 | out.WriteString("\n" + fourSpace)
87 | }
88 | p.text(out, blk.(*Paragraph).Text)
89 | out.WriteString("\n")
90 | }
91 | }
92 | }
93 | }
94 |
95 | // text prints the text sequence x to out.
96 | func (p *mdPrinter) text(out *bytes.Buffer, x []Text) {
97 | p.raw.Reset()
98 | p.rawText(&p.raw, x)
99 | line := bytes.TrimSpace(p.raw.Bytes())
100 | if len(line) == 0 {
101 | return
102 | }
103 | switch line[0] {
104 | case '+', '-', '*', '#':
105 | // Escape what would be the start of an unordered list or heading.
106 | out.WriteByte('\\')
107 | case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
108 | i := 1
109 | for i < len(line) && '0' <= line[i] && line[i] <= '9' {
110 | i++
111 | }
112 | if i < len(line) && (line[i] == '.' || line[i] == ')') {
113 | // Escape what would be the start of an ordered list.
114 | out.Write(line[:i])
115 | out.WriteByte('\\')
116 | line = line[i:]
117 | }
118 | }
119 | out.Write(line)
120 | }
121 |
122 | // rawText prints the text sequence x to out,
123 | // without worrying about escaping characters
124 | // that have special meaning at the start of a Markdown line.
125 | func (p *mdPrinter) rawText(out *bytes.Buffer, x []Text) {
126 | for _, t := range x {
127 | switch t := t.(type) {
128 | case Plain:
129 | p.escape(out, string(t))
130 | case Italic:
131 | out.WriteString("*")
132 | p.escape(out, string(t))
133 | out.WriteString("*")
134 | case *Link:
135 | out.WriteString("[")
136 | p.rawText(out, t.Text)
137 | out.WriteString("](")
138 | out.WriteString(t.URL)
139 | out.WriteString(")")
140 | case *DocLink:
141 | url := p.docLinkURL(t)
142 | if url != "" {
143 | out.WriteString("[")
144 | }
145 | p.rawText(out, t.Text)
146 | if url != "" {
147 | out.WriteString("](")
148 | url = strings.ReplaceAll(url, "(", "%28")
149 | url = strings.ReplaceAll(url, ")", "%29")
150 | out.WriteString(url)
151 | out.WriteString(")")
152 | }
153 | }
154 | }
155 | }
156 |
157 | // escape prints s to out as plain text,
158 | // escaping special characters to avoid being misinterpreted
159 | // as Markdown markup sequences.
160 | func (p *mdPrinter) escape(out *bytes.Buffer, s string) {
161 | start := 0
162 | for i := 0; i < len(s); i++ {
163 | switch s[i] {
164 | case '\n':
165 | // Turn all \n into spaces, for a few reasons:
166 | // - Avoid introducing paragraph breaks accidentally.
167 | // - Avoid the need to reindent after the newline.
168 | // - Avoid problems with Markdown renderers treating
169 | // every mid-paragraph newline as a
.
170 | out.WriteString(s[start:i])
171 | out.WriteByte(' ')
172 | start = i + 1
173 | continue
174 | case '`', '_', '*', '[', '<', '\\':
175 | // Not all of these need to be escaped all the time,
176 | // but is valid and easy to do so.
177 | // We assume the Markdown is being passed to a
178 | // Markdown renderer, not edited by a person,
179 | // so it's fine to have escapes that are not strictly
180 | // necessary in some cases.
181 | out.WriteString(s[start:i])
182 | out.WriteByte('\\')
183 | out.WriteByte(s[i])
184 | start = i + 1
185 | }
186 | }
187 | out.WriteString(s[start:])
188 | }
189 |
--------------------------------------------------------------------------------
/internal/govendor/go/doc/comment/print.go:
--------------------------------------------------------------------------------
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 | "strings"
11 | )
12 |
13 | // A Printer is a doc comment printer.
14 | // The fields in the struct can be filled in before calling
15 | // any of the printing methods
16 | // in order to customize the details of the printing process.
17 | type Printer struct {
18 | // HeadingLevel is the nesting level used for
19 | // HTML and Markdown headings.
20 | // If HeadingLevel is zero, it defaults to level 3,
21 | // meaning to use and ###.
22 | HeadingLevel int
23 |
24 | // HeadingID is a function that computes the heading ID
25 | // (anchor tag) to use for the heading h when generating
26 | // HTML and Markdown. If HeadingID returns an empty string,
27 | // then the heading ID is omitted.
28 | // If HeadingID is nil, h.DefaultID is used.
29 | HeadingID func(h *Heading) string
30 |
31 | // DocLinkURL is a function that computes the URL for the given DocLink.
32 | // If DocLinkURL is nil, then link.DefaultURL(p.DocLinkBaseURL) is used.
33 | DocLinkURL func(link *DocLink) string
34 |
35 | // DocLinkBaseURL is used when DocLinkURL is nil,
36 | // passed to [DocLink.DefaultURL] to construct a DocLink's URL.
37 | // See that method's documentation for details.
38 | DocLinkBaseURL string
39 |
40 | // TextPrefix is a prefix to print at the start of every line
41 | // when generating text output using the Text method.
42 | TextPrefix string
43 |
44 | // TextCodePrefix is the prefix to print at the start of each
45 | // preformatted (code block) line when generating text output,
46 | // instead of (not in addition to) TextPrefix.
47 | // If TextCodePrefix is the empty string, it defaults to TextPrefix+"\t".
48 | TextCodePrefix string
49 |
50 | // TextWidth is the maximum width text line to generate,
51 | // measured in Unicode code points,
52 | // excluding TextPrefix and the newline character.
53 | // If TextWidth is zero, it defaults to 80 minus the number of code points in TextPrefix.
54 | // If TextWidth is negative, there is no limit.
55 | TextWidth int
56 | }
57 |
58 | func (p *Printer) headingLevel() int {
59 | if p.HeadingLevel <= 0 {
60 | return 3
61 | }
62 | return p.HeadingLevel
63 | }
64 |
65 | func (p *Printer) headingID(h *Heading) string {
66 | if p.HeadingID == nil {
67 | return h.DefaultID()
68 | }
69 | return p.HeadingID(h)
70 | }
71 |
72 | func (p *Printer) docLinkURL(link *DocLink) string {
73 | if p.DocLinkURL != nil {
74 | return p.DocLinkURL(link)
75 | }
76 | return link.DefaultURL(p.DocLinkBaseURL)
77 | }
78 |
79 | // DefaultURL constructs and returns the documentation URL for l,
80 | // using baseURL as a prefix for links to other packages.
81 | //
82 | // The possible forms returned by DefaultURL are:
83 | // - baseURL/ImportPath, for a link to another package
84 | // - baseURL/ImportPath#Name, for a link to a const, func, type, or var in another package
85 | // - baseURL/ImportPath#Recv.Name, for a link to a method in another package
86 | // - #Name, for a link to a const, func, type, or var in this package
87 | // - #Recv.Name, for a link to a method in this package
88 | //
89 | // If baseURL ends in a trailing slash, then DefaultURL inserts
90 | // a slash between ImportPath and # in the anchored forms.
91 | // For example, here are some baseURL values and URLs they can generate:
92 | //
93 | // "/pkg/" → "/pkg/math/#Sqrt"
94 | // "/pkg" → "/pkg/math#Sqrt"
95 | // "/" → "/math/#Sqrt"
96 | // "" → "/math#Sqrt"
97 | func (l *DocLink) DefaultURL(baseURL string) string {
98 | if l.ImportPath != "" {
99 | slash := ""
100 | if strings.HasSuffix(baseURL, "/") {
101 | slash = "/"
102 | } else {
103 | baseURL += "/"
104 | }
105 | switch {
106 | case l.Name == "":
107 | return baseURL + l.ImportPath + slash
108 | case l.Recv != "":
109 | return baseURL + l.ImportPath + slash + "#" + l.Recv + "." + l.Name
110 | default:
111 | return baseURL + l.ImportPath + slash + "#" + l.Name
112 | }
113 | }
114 | if l.Recv != "" {
115 | return "#" + l.Recv + "." + l.Name
116 | }
117 | return "#" + l.Name
118 | }
119 |
120 | // DefaultID returns the default anchor ID for the heading h.
121 | //
122 | // The default anchor ID is constructed by converting every
123 | // rune that is not alphanumeric ASCII to an underscore
124 | // and then adding the prefix “hdr-”.
125 | // For example, if the heading text is “Go Doc Comments”,
126 | // the default ID is “hdr-Go_Doc_Comments”.
127 | func (h *Heading) DefaultID() string {
128 | // Note: The “hdr-” prefix is important to avoid DOM clobbering attacks.
129 | // See https://pkg.go.dev/github.com/google/safehtml#Identifier.
130 | var out strings.Builder
131 | var p textPrinter
132 | p.oneLongLine(&out, h.Text)
133 | s := strings.TrimSpace(out.String())
134 | if s == "" {
135 | return ""
136 | }
137 | out.Reset()
138 | out.WriteString("hdr-")
139 | for _, r := range s {
140 | if r < 0x80 && isIdentASCII(byte(r)) {
141 | out.WriteByte(byte(r))
142 | } else {
143 | out.WriteByte('_')
144 | }
145 | }
146 | return out.String()
147 | }
148 |
149 | type commentPrinter struct {
150 | *Printer
151 | }
152 |
153 | // Comment returns the standard Go formatting of the [Doc],
154 | // without any comment markers.
155 | func (p *Printer) Comment(d *Doc) []byte {
156 | cp := &commentPrinter{Printer: p}
157 | var out bytes.Buffer
158 | for i, x := range d.Content {
159 | if i > 0 && blankBefore(x) {
160 | out.WriteString("\n")
161 | }
162 | cp.block(&out, x)
163 | }
164 |
165 | // Print one block containing all the link definitions that were used,
166 | // and then a second block containing all the unused ones.
167 | // This makes it easy to clean up the unused ones: gofmt and
168 | // delete the final block. And it's a nice visual signal without
169 | // affecting the way the comment formats for users.
170 | for i := 0; i < 2; i++ {
171 | used := i == 0
172 | first := true
173 | for _, def := range d.Links {
174 | if def.Used == used {
175 | if first {
176 | out.WriteString("\n")
177 | first = false
178 | }
179 | out.WriteString("[")
180 | out.WriteString(def.Text)
181 | out.WriteString("]: ")
182 | out.WriteString(def.URL)
183 | out.WriteString("\n")
184 | }
185 | }
186 | }
187 |
188 | return out.Bytes()
189 | }
190 |
191 | // blankBefore reports whether the block x requires a blank line before it.
192 | // All blocks do, except for Lists that return false from x.BlankBefore().
193 | func blankBefore(x Block) bool {
194 | if x, ok := x.(*List); ok {
195 | return x.BlankBefore()
196 | }
197 | return true
198 | }
199 |
200 | // block prints the block x to out.
201 | func (p *commentPrinter) block(out *bytes.Buffer, x Block) {
202 | switch x := x.(type) {
203 | default:
204 | fmt.Fprintf(out, "?%T", x)
205 |
206 | case *Paragraph:
207 | p.text(out, "", x.Text)
208 | out.WriteString("\n")
209 |
210 | case *Heading:
211 | out.WriteString("# ")
212 | p.text(out, "", x.Text)
213 | out.WriteString("\n")
214 |
215 | case *Code:
216 | md := x.Text
217 | for md != "" {
218 | var line string
219 | line, md, _ = strings.Cut(md, "\n")
220 | if line != "" {
221 | out.WriteString("\t")
222 | out.WriteString(line)
223 | }
224 | out.WriteString("\n")
225 | }
226 |
227 | case *List:
228 | loose := x.BlankBetween()
229 | for i, item := range x.Items {
230 | if i > 0 && loose {
231 | out.WriteString("\n")
232 | }
233 | out.WriteString(" ")
234 | if item.Number == "" {
235 | out.WriteString(" - ")
236 | } else {
237 | out.WriteString(item.Number)
238 | out.WriteString(". ")
239 | }
240 | for i, blk := range item.Content {
241 | const fourSpace = " "
242 | if i > 0 {
243 | out.WriteString("\n" + fourSpace)
244 | }
245 | p.text(out, fourSpace, blk.(*Paragraph).Text)
246 | out.WriteString("\n")
247 | }
248 | }
249 | }
250 | }
251 |
252 | // text prints the text sequence x to out.
253 | func (p *commentPrinter) text(out *bytes.Buffer, indent string, x []Text) {
254 | for _, t := range x {
255 | switch t := t.(type) {
256 | case Plain:
257 | p.indent(out, indent, string(t))
258 | case Italic:
259 | p.indent(out, indent, string(t))
260 | case *Link:
261 | if t.Auto {
262 | p.text(out, indent, t.Text)
263 | } else {
264 | out.WriteString("[")
265 | p.text(out, indent, t.Text)
266 | out.WriteString("]")
267 | }
268 | case *DocLink:
269 | out.WriteString("[")
270 | p.text(out, indent, t.Text)
271 | out.WriteString("]")
272 | }
273 | }
274 | }
275 |
276 | // indent prints s to out, indenting with the indent string
277 | // after each newline in s.
278 | func (p *commentPrinter) indent(out *bytes.Buffer, indent, s string) {
279 | for s != "" {
280 | line, rest, ok := strings.Cut(s, "\n")
281 | out.WriteString(line)
282 | if ok {
283 | out.WriteString("\n")
284 | out.WriteString(indent)
285 | }
286 | s = rest
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/internal/govendor/go/doc/comment/std.go:
--------------------------------------------------------------------------------
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 | // Code generated by 'go generate' DO NOT EDIT.
6 | //disabled go:generate ./mkstd.sh
7 |
8 | package comment
9 |
10 | var stdPkgs = []string{
11 | "bufio",
12 | "bytes",
13 | "cmp",
14 | "context",
15 | "crypto",
16 | "embed",
17 | "encoding",
18 | "errors",
19 | "expvar",
20 | "flag",
21 | "fmt",
22 | "hash",
23 | "html",
24 | "image",
25 | "io",
26 | "iter",
27 | "log",
28 | "maps",
29 | "math",
30 | "mime",
31 | "net",
32 | "os",
33 | "path",
34 | "plugin",
35 | "reflect",
36 | "regexp",
37 | "runtime",
38 | "slices",
39 | "sort",
40 | "strconv",
41 | "strings",
42 | "structs",
43 | "sync",
44 | "syscall",
45 | "testing",
46 | "time",
47 | "unicode",
48 | "unique",
49 | "unsafe",
50 | "weak",
51 | }
52 |
--------------------------------------------------------------------------------
/internal/govendor/go/doc/comment/text.go:
--------------------------------------------------------------------------------
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 | "sort"
11 | "strings"
12 | "unicode/utf8"
13 | )
14 |
15 | // A textPrinter holds the state needed for printing a Doc as plain text.
16 | type textPrinter struct {
17 | *Printer
18 | long strings.Builder
19 | prefix string
20 | codePrefix string
21 | width int
22 | }
23 |
24 | // Text returns a textual formatting of the [Doc].
25 | // See the [Printer] documentation for ways to customize the text output.
26 | func (p *Printer) Text(d *Doc) []byte {
27 | tp := &textPrinter{
28 | Printer: p,
29 | prefix: p.TextPrefix,
30 | codePrefix: p.TextCodePrefix,
31 | width: p.TextWidth,
32 | }
33 | if tp.codePrefix == "" {
34 | tp.codePrefix = p.TextPrefix + "\t"
35 | }
36 | if tp.width == 0 {
37 | tp.width = 80 - utf8.RuneCountInString(tp.prefix)
38 | }
39 |
40 | var out bytes.Buffer
41 | for i, x := range d.Content {
42 | if i > 0 && blankBefore(x) {
43 | out.WriteString(tp.prefix)
44 | writeNL(&out)
45 | }
46 | tp.block(&out, x)
47 | }
48 | anyUsed := false
49 | for _, def := range d.Links {
50 | if def.Used {
51 | anyUsed = true
52 | break
53 | }
54 | }
55 | if anyUsed {
56 | writeNL(&out)
57 | for _, def := range d.Links {
58 | if def.Used {
59 | fmt.Fprintf(&out, "[%s]: %s\n", def.Text, def.URL)
60 | }
61 | }
62 | }
63 | return out.Bytes()
64 | }
65 |
66 | // writeNL calls out.WriteByte('\n')
67 | // but first trims trailing spaces on the previous line.
68 | func writeNL(out *bytes.Buffer) {
69 | // Trim trailing spaces.
70 | data := out.Bytes()
71 | n := 0
72 | for n < len(data) && (data[len(data)-n-1] == ' ' || data[len(data)-n-1] == '\t') {
73 | n++
74 | }
75 | if n > 0 {
76 | out.Truncate(len(data) - n)
77 | }
78 | out.WriteByte('\n')
79 | }
80 |
81 | // block prints the block x to out.
82 | func (p *textPrinter) block(out *bytes.Buffer, x Block) {
83 | switch x := x.(type) {
84 | default:
85 | fmt.Fprintf(out, "?%T\n", x)
86 |
87 | case *Paragraph:
88 | out.WriteString(p.prefix)
89 | p.text(out, "", x.Text)
90 |
91 | case *Heading:
92 | out.WriteString(p.prefix)
93 | out.WriteString("# ")
94 | p.text(out, "", x.Text)
95 |
96 | case *Code:
97 | text := x.Text
98 | for text != "" {
99 | var line string
100 | line, text, _ = strings.Cut(text, "\n")
101 | if line != "" {
102 | out.WriteString(p.codePrefix)
103 | out.WriteString(line)
104 | }
105 | writeNL(out)
106 | }
107 |
108 | case *List:
109 | loose := x.BlankBetween()
110 | for i, item := range x.Items {
111 | if i > 0 && loose {
112 | out.WriteString(p.prefix)
113 | writeNL(out)
114 | }
115 | out.WriteString(p.prefix)
116 | out.WriteString(" ")
117 | if item.Number == "" {
118 | out.WriteString(" - ")
119 | } else {
120 | out.WriteString(item.Number)
121 | out.WriteString(". ")
122 | }
123 | for i, blk := range item.Content {
124 | const fourSpace = " "
125 | if i > 0 {
126 | writeNL(out)
127 | out.WriteString(p.prefix)
128 | out.WriteString(fourSpace)
129 | }
130 | p.text(out, fourSpace, blk.(*Paragraph).Text)
131 | }
132 | }
133 | }
134 | }
135 |
136 | // text prints the text sequence x to out.
137 | func (p *textPrinter) text(out *bytes.Buffer, indent string, x []Text) {
138 | p.oneLongLine(&p.long, x)
139 | words := strings.Fields(p.long.String())
140 | p.long.Reset()
141 |
142 | var seq []int
143 | if p.width < 0 || len(words) == 0 {
144 | seq = []int{0, len(words)} // one long line
145 | } else {
146 | seq = wrap(words, p.width-utf8.RuneCountInString(indent))
147 | }
148 | for i := 0; i+1 < len(seq); i++ {
149 | if i > 0 {
150 | out.WriteString(p.prefix)
151 | out.WriteString(indent)
152 | }
153 | for j, w := range words[seq[i]:seq[i+1]] {
154 | if j > 0 {
155 | out.WriteString(" ")
156 | }
157 | out.WriteString(w)
158 | }
159 | writeNL(out)
160 | }
161 | }
162 |
163 | // oneLongLine prints the text sequence x to out as one long line,
164 | // without worrying about line wrapping.
165 | // Explicit links have the [ ] dropped to improve readability.
166 | func (p *textPrinter) oneLongLine(out *strings.Builder, x []Text) {
167 | for _, t := range x {
168 | switch t := t.(type) {
169 | case Plain:
170 | out.WriteString(string(t))
171 | case Italic:
172 | out.WriteString(string(t))
173 | case *Link:
174 | p.oneLongLine(out, t.Text)
175 | case *DocLink:
176 | p.oneLongLine(out, t.Text)
177 | }
178 | }
179 | }
180 |
181 | // wrap wraps words into lines of at most max runes,
182 | // minimizing the sum of the squares of the leftover lengths
183 | // at the end of each line (except the last, of course),
184 | // with a preference for ending lines at punctuation (.,:;).
185 | //
186 | // The returned slice gives the indexes of the first words
187 | // on each line in the wrapped text with a final entry of len(words).
188 | // Thus the lines are words[seq[0]:seq[1]], words[seq[1]:seq[2]],
189 | // ..., words[seq[len(seq)-2]:seq[len(seq)-1]].
190 | //
191 | // The implementation runs in O(n log n) time, where n = len(words),
192 | // using the algorithm described in D. S. Hirschberg and L. L. Larmore,
193 | // “[The least weight subsequence problem],” FOCS 1985, pp. 137-143.
194 | //
195 | // [The least weight subsequence problem]: https://doi.org/10.1109/SFCS.1985.60
196 | func wrap(words []string, max int) (seq []int) {
197 | // The algorithm requires that our scoring function be concave,
198 | // meaning that for all i₀ ≤ i₁ < j₀ ≤ j₁,
199 | // weight(i₀, j₀) + weight(i₁, j₁) ≤ weight(i₀, j₁) + weight(i₁, j₀).
200 | //
201 | // Our weights are two-element pairs [hi, lo]
202 | // ordered by elementwise comparison.
203 | // The hi entry counts the weight for lines that are longer than max,
204 | // and the lo entry counts the weight for lines that are not.
205 | // This forces the algorithm to first minimize the number of lines
206 | // that are longer than max, which correspond to lines with
207 | // single very long words. Having done that, it can move on to
208 | // minimizing the lo score, which is more interesting.
209 | //
210 | // The lo score is the sum for each line of the square of the
211 | // number of spaces remaining at the end of the line and a
212 | // penalty of 64 given out for not ending the line in a
213 | // punctuation character (.,:;).
214 | // The penalty is somewhat arbitrarily chosen by trying
215 | // different amounts and judging how nice the wrapped text looks.
216 | // Roughly speaking, using 64 means that we are willing to
217 | // end a line with eight blank spaces in order to end at a
218 | // punctuation character, even if the next word would fit in
219 | // those spaces.
220 | //
221 | // We care about ending in punctuation characters because
222 | // it makes the text easier to skim if not too many sentences
223 | // or phrases begin with a single word on the previous line.
224 |
225 | // A score is the score (also called weight) for a given line.
226 | // add and cmp add and compare scores.
227 | type score struct {
228 | hi int64
229 | lo int64
230 | }
231 | add := func(s, t score) score { return score{s.hi + t.hi, s.lo + t.lo} }
232 | cmp := func(s, t score) int {
233 | switch {
234 | case s.hi < t.hi:
235 | return -1
236 | case s.hi > t.hi:
237 | return +1
238 | case s.lo < t.lo:
239 | return -1
240 | case s.lo > t.lo:
241 | return +1
242 | }
243 | return 0
244 | }
245 |
246 | // total[j] is the total number of runes
247 | // (including separating spaces) in words[:j].
248 | total := make([]int, len(words)+1)
249 | total[0] = 0
250 | for i, s := range words {
251 | total[1+i] = total[i] + utf8.RuneCountInString(s) + 1
252 | }
253 |
254 | // weight returns weight(i, j).
255 | weight := func(i, j int) score {
256 | // On the last line, there is zero weight for being too short.
257 | n := total[j] - 1 - total[i]
258 | if j == len(words) && n <= max {
259 | return score{0, 0}
260 | }
261 |
262 | // Otherwise the weight is the penalty plus the square of the number of
263 | // characters remaining on the line or by which the line goes over.
264 | // In the latter case, that value goes in the hi part of the score.
265 | // (See note above.)
266 | p := wrapPenalty(words[j-1])
267 | v := int64(max-n) * int64(max-n)
268 | if n > max {
269 | return score{v, p}
270 | }
271 | return score{0, v + p}
272 | }
273 |
274 | // The rest of this function is “The Basic Algorithm” from
275 | // Hirschberg and Larmore's conference paper,
276 | // using the same names as in the paper.
277 | f := []score{{0, 0}}
278 | g := func(i, j int) score { return add(f[i], weight(i, j)) }
279 |
280 | bridge := func(a, b, c int) bool {
281 | k := c + sort.Search(len(words)+1-c, func(k int) bool {
282 | k += c
283 | return cmp(g(a, k), g(b, k)) > 0
284 | })
285 | if k > len(words) {
286 | return true
287 | }
288 | return cmp(g(c, k), g(b, k)) <= 0
289 | }
290 |
291 | // d is a one-ended deque implemented as a slice.
292 | d := make([]int, 1, len(words))
293 | d[0] = 0
294 | bestleft := make([]int, 1, len(words))
295 | bestleft[0] = -1
296 | for m := 1; m < len(words); m++ {
297 | f = append(f, g(d[0], m))
298 | bestleft = append(bestleft, d[0])
299 | for len(d) > 1 && cmp(g(d[1], m+1), g(d[0], m+1)) <= 0 {
300 | d = d[1:] // “Retire”
301 | }
302 | for len(d) > 1 && bridge(d[len(d)-2], d[len(d)-1], m) {
303 | d = d[:len(d)-1] // “Fire”
304 | }
305 | if cmp(g(m, len(words)), g(d[len(d)-1], len(words))) < 0 {
306 | d = append(d, m) // “Hire”
307 | // The next few lines are not in the paper but are necessary
308 | // to handle two-word inputs correctly. It appears to be
309 | // just a bug in the paper's pseudocode.
310 | if len(d) == 2 && cmp(g(d[1], m+1), g(d[0], m+1)) <= 0 {
311 | d = d[1:]
312 | }
313 | }
314 | }
315 | bestleft = append(bestleft, d[0])
316 |
317 | // Recover least weight sequence from bestleft.
318 | n := 1
319 | for m := len(words); m > 0; m = bestleft[m] {
320 | n++
321 | }
322 | seq = make([]int, n)
323 | for m := len(words); m > 0; m = bestleft[m] {
324 | n--
325 | seq[n] = m
326 | }
327 | return seq
328 | }
329 |
330 | // wrapPenalty is the penalty for inserting a line break after word s.
331 | func wrapPenalty(s string) int64 {
332 | switch s[len(s)-1] {
333 | case '.', ',', ':', ';':
334 | return 0
335 | }
336 | return 64
337 | }
338 |
--------------------------------------------------------------------------------
/internal/govendor/go/format/format.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 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 format implements standard formatting of Go source.
6 | //
7 | // Note that formatting of Go source code changes over time, so tools relying on
8 | // consistent formatting should execute a specific version of the gofmt binary
9 | // instead of using this package. That way, the formatting will be stable, and
10 | // the tools won't need to be recompiled each time gofmt changes.
11 | //
12 | // For example, pre-submit checks that use this package directly would behave
13 | // differently depending on what Go version each developer uses, causing the
14 | // check to be inherently fragile.
15 | package format
16 |
17 | import (
18 | "bytes"
19 | "fmt"
20 | "go/ast"
21 | "go/parser"
22 | "go/token"
23 | "io"
24 |
25 | "mvdan.cc/gofumpt/internal/govendor/go/printer"
26 | )
27 |
28 | // Keep these in sync with cmd/gofmt/gofmt.go.
29 | const (
30 | tabWidth = 8
31 | printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers
32 |
33 | // printerNormalizeNumbers means to canonicalize number literal prefixes
34 | // and exponents while printing. See https://golang.org/doc/go1.13#gofmt.
35 | //
36 | // This value is defined in mvdan.cc/gofumpt/internal/govendor/go/printer specifically for mvdan.cc/gofumpt/internal/govendor/go/format and cmd/gofmt.
37 | printerNormalizeNumbers = 1 << 30
38 | )
39 |
40 | var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth}
41 |
42 | const parserMode = parser.ParseComments | parser.SkipObjectResolution
43 |
44 | // Node formats node in canonical gofmt style and writes the result to dst.
45 | //
46 | // The node type must be *[ast.File], *[printer.CommentedNode], [][ast.Decl],
47 | // [][ast.Stmt], or assignment-compatible to [ast.Expr], [ast.Decl], [ast.Spec],
48 | // or [ast.Stmt]. Node does not modify node. Imports are not sorted for
49 | // nodes representing partial source files (for instance, if the node is
50 | // not an *[ast.File] or a *[printer.CommentedNode] not wrapping an *[ast.File]).
51 | //
52 | // The function may return early (before the entire result is written)
53 | // and return a formatting error, for instance due to an incorrect AST.
54 | func Node(dst io.Writer, fset *token.FileSet, node any) error {
55 | // Determine if we have a complete source file (file != nil).
56 | var file *ast.File
57 | var cnode *printer.CommentedNode
58 | switch n := node.(type) {
59 | case *ast.File:
60 | file = n
61 | case *printer.CommentedNode:
62 | if f, ok := n.Node.(*ast.File); ok {
63 | file = f
64 | cnode = n
65 | }
66 | }
67 |
68 | // Sort imports if necessary.
69 | if file != nil && hasUnsortedImports(file) {
70 | // Make a copy of the AST because ast.SortImports is destructive.
71 | // TODO(gri) Do this more efficiently.
72 | var buf bytes.Buffer
73 | err := config.Fprint(&buf, fset, file)
74 | if err != nil {
75 | return err
76 | }
77 | file, err = parser.ParseFile(fset, "", buf.Bytes(), parserMode)
78 | if err != nil {
79 | // We should never get here. If we do, provide good diagnostic.
80 | return fmt.Errorf("format.Node internal error (%s)", err)
81 | }
82 | ast.SortImports(fset, file)
83 |
84 | // Use new file with sorted imports.
85 | node = file
86 | if cnode != nil {
87 | node = &printer.CommentedNode{Node: file, Comments: cnode.Comments}
88 | }
89 | }
90 |
91 | return config.Fprint(dst, fset, node)
92 | }
93 |
94 | // Source formats src in canonical gofmt style and returns the result
95 | // or an (I/O or syntax) error. src is expected to be a syntactically
96 | // correct Go source file, or a list of Go declarations or statements.
97 | //
98 | // If src is a partial source file, the leading and trailing space of src
99 | // is applied to the result (such that it has the same leading and trailing
100 | // space as src), and the result is indented by the same amount as the first
101 | // line of src containing code. Imports are not sorted for partial source files.
102 | func Source(src []byte) ([]byte, error) {
103 | fset := token.NewFileSet()
104 | file, sourceAdj, indentAdj, err := parse(fset, "", src, true)
105 | if err != nil {
106 | return nil, err
107 | }
108 |
109 | if sourceAdj == nil {
110 | // Complete source file.
111 | // TODO(gri) consider doing this always.
112 | ast.SortImports(fset, file)
113 | }
114 |
115 | return format(fset, file, sourceAdj, indentAdj, src, config)
116 | }
117 |
118 | func hasUnsortedImports(file *ast.File) bool {
119 | for _, d := range file.Decls {
120 | d, ok := d.(*ast.GenDecl)
121 | if !ok || d.Tok != token.IMPORT {
122 | // Not an import declaration, so we're done.
123 | // Imports are always first.
124 | return false
125 | }
126 | if d.Lparen.IsValid() {
127 | // For now assume all grouped imports are unsorted.
128 | // TODO(gri) Should check if they are sorted already.
129 | return true
130 | }
131 | // Ungrouped imports are sorted by default.
132 | }
133 | return false
134 | }
135 |
--------------------------------------------------------------------------------
/internal/govendor/go/format/internal.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 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 | // TODO(gri): This file and the file src/cmd/gofmt/internal.go are
6 | // the same (but for this comment and the package name). Do not modify
7 | // one without the other. Determine if we can factor out functionality
8 | // in a public API. See also #11844 for context.
9 |
10 | package format
11 |
12 | import (
13 | "bytes"
14 | "go/ast"
15 | "go/parser"
16 | "go/token"
17 | "strings"
18 |
19 | "mvdan.cc/gofumpt/internal/govendor/go/printer"
20 | )
21 |
22 | // parse parses src, which was read from the named file,
23 | // as a Go source file, declaration, or statement list.
24 | func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) (
25 | file *ast.File,
26 | sourceAdj func(src []byte, indent int) []byte,
27 | indentAdj int,
28 | err error,
29 | ) {
30 | // Try as whole source file.
31 | file, err = parser.ParseFile(fset, filename, src, parserMode)
32 | // If there's no error, return. If the error is that the source file didn't begin with a
33 | // package line and source fragments are ok, fall through to
34 | // try as a source fragment. Stop and return on any other error.
35 | if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") {
36 | return
37 | }
38 |
39 | // If this is a declaration list, make it a source file
40 | // by inserting a package clause.
41 | // Insert using a ';', not a newline, so that the line numbers
42 | // in psrc match the ones in src.
43 | psrc := append([]byte("package p;"), src...)
44 | file, err = parser.ParseFile(fset, filename, psrc, parserMode)
45 | if err == nil {
46 | sourceAdj = func(src []byte, indent int) []byte {
47 | // Remove the package clause.
48 | // Gofmt has turned the ';' into a '\n'.
49 | src = src[indent+len("package p\n"):]
50 | return bytes.TrimSpace(src)
51 | }
52 | return
53 | }
54 | // If the error is that the source file didn't begin with a
55 | // declaration, fall through to try as a statement list.
56 | // Stop and return on any other error.
57 | if !strings.Contains(err.Error(), "expected declaration") {
58 | return
59 | }
60 |
61 | // If this is a statement list, make it a source file
62 | // by inserting a package clause and turning the list
63 | // into a function body. This handles expressions too.
64 | // Insert using a ';', not a newline, so that the line numbers
65 | // in fsrc match the ones in src. Add an extra '\n' before the '}'
66 | // to make sure comments are flushed before the '}'.
67 | fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '\n', '}')
68 | file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
69 | if err == nil {
70 | sourceAdj = func(src []byte, indent int) []byte {
71 | // Cap adjusted indent to zero.
72 | if indent < 0 {
73 | indent = 0
74 | }
75 | // Remove the wrapping.
76 | // Gofmt has turned the "; " into a "\n\n".
77 | // There will be two non-blank lines with indent, hence 2*indent.
78 | src = src[2*indent+len("package p\n\nfunc _() {"):]
79 | // Remove only the "}\n" suffix: remaining whitespaces will be trimmed anyway
80 | src = src[:len(src)-len("}\n")]
81 | return bytes.TrimSpace(src)
82 | }
83 | // Gofmt has also indented the function body one level.
84 | // Adjust that with indentAdj.
85 | indentAdj = -1
86 | }
87 |
88 | // Succeeded, or out of options.
89 | return
90 | }
91 |
92 | // format formats the given package file originally obtained from src
93 | // and adjusts the result based on the original source via sourceAdj
94 | // and indentAdj.
95 | func format(
96 | fset *token.FileSet,
97 | file *ast.File,
98 | sourceAdj func(src []byte, indent int) []byte,
99 | indentAdj int,
100 | src []byte,
101 | cfg printer.Config,
102 | ) ([]byte, error) {
103 | if sourceAdj == nil {
104 | // Complete source file.
105 | var buf bytes.Buffer
106 | err := cfg.Fprint(&buf, fset, file)
107 | if err != nil {
108 | return nil, err
109 | }
110 | return buf.Bytes(), nil
111 | }
112 |
113 | // Partial source file.
114 | // Determine and prepend leading space.
115 | i, j := 0, 0
116 | for j < len(src) && isSpace(src[j]) {
117 | if src[j] == '\n' {
118 | i = j + 1 // byte offset of last line in leading space
119 | }
120 | j++
121 | }
122 | var res []byte
123 | res = append(res, src[:i]...)
124 |
125 | // Determine and prepend indentation of first code line.
126 | // Spaces are ignored unless there are no tabs,
127 | // in which case spaces count as one tab.
128 | indent := 0
129 | hasSpace := false
130 | for _, b := range src[i:j] {
131 | switch b {
132 | case ' ':
133 | hasSpace = true
134 | case '\t':
135 | indent++
136 | }
137 | }
138 | if indent == 0 && hasSpace {
139 | indent = 1
140 | }
141 | for i := 0; i < indent; i++ {
142 | res = append(res, '\t')
143 | }
144 |
145 | // Format the source.
146 | // Write it without any leading and trailing space.
147 | cfg.Indent = indent + indentAdj
148 | var buf bytes.Buffer
149 | err := cfg.Fprint(&buf, fset, file)
150 | if err != nil {
151 | return nil, err
152 | }
153 | out := sourceAdj(buf.Bytes(), cfg.Indent)
154 |
155 | // If the adjusted output is empty, the source
156 | // was empty but (possibly) for white space.
157 | // The result is the incoming source.
158 | if len(out) == 0 {
159 | return src, nil
160 | }
161 |
162 | // Otherwise, append output to leading space.
163 | res = append(res, out...)
164 |
165 | // Determine and append trailing space.
166 | i = len(src)
167 | for i > 0 && isSpace(src[i-1]) {
168 | i--
169 | }
170 | return append(res, src[i:]...), nil
171 | }
172 |
173 | // isSpace reports whether the byte is a space character.
174 | // isSpace defines a space as being among the following bytes: ' ', '\t', '\n' and '\r'.
175 | func isSpace(b byte) bool {
176 | return b == ' ' || b == '\t' || b == '\n' || b == '\r'
177 | }
178 |
--------------------------------------------------------------------------------
/internal/govendor/go/printer/comment.go:
--------------------------------------------------------------------------------
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 printer
6 |
7 | import (
8 | "go/ast"
9 | "strings"
10 |
11 | "mvdan.cc/gofumpt/internal/govendor/go/doc/comment"
12 | )
13 |
14 | // formatDocComment reformats the doc comment list,
15 | // returning the canonical formatting.
16 | func formatDocComment(list []*ast.Comment) []*ast.Comment {
17 | // Extract comment text (removing comment markers).
18 | var kind, text string
19 | var directives []*ast.Comment
20 | if len(list) == 1 && strings.HasPrefix(list[0].Text, "/*") {
21 | kind = "/*"
22 | text = list[0].Text
23 | if !strings.Contains(text, "\n") || allStars(text) {
24 | // Single-line /* .. */ comment in doc comment position,
25 | // or multiline old-style comment like
26 | // /*
27 | // * Comment
28 | // * text here.
29 | // */
30 | // Should not happen, since it will not work well as a
31 | // doc comment, but if it does, just ignore:
32 | // reformatting it will only make the situation worse.
33 | return list
34 | }
35 | text = text[2 : len(text)-2] // cut /* and */
36 | } else if strings.HasPrefix(list[0].Text, "//") {
37 | kind = "//"
38 | var b strings.Builder
39 | for _, c := range list {
40 | after, found := strings.CutPrefix(c.Text, "//")
41 | if !found {
42 | return list
43 | }
44 | // Accumulate //go:build etc lines separately.
45 | if isDirective(after) {
46 | directives = append(directives, c)
47 | continue
48 | }
49 | b.WriteString(strings.TrimPrefix(after, " "))
50 | b.WriteString("\n")
51 | }
52 | text = b.String()
53 | } else {
54 | // Not sure what this is, so leave alone.
55 | return list
56 | }
57 |
58 | if text == "" {
59 | return list
60 | }
61 |
62 | // Parse comment and reformat as text.
63 | var p comment.Parser
64 | d := p.Parse(text)
65 |
66 | var pr comment.Printer
67 | text = string(pr.Comment(d))
68 |
69 | // For /* */ comment, return one big comment with text inside.
70 | slash := list[0].Slash
71 | if kind == "/*" {
72 | c := &ast.Comment{
73 | Slash: slash,
74 | Text: "/*\n" + text + "*/",
75 | }
76 | return []*ast.Comment{c}
77 | }
78 |
79 | // For // comment, return sequence of // lines.
80 | var out []*ast.Comment
81 | for text != "" {
82 | var line string
83 | line, text, _ = strings.Cut(text, "\n")
84 | if line == "" {
85 | line = "//"
86 | } else if strings.HasPrefix(line, "\t") {
87 | line = "//" + line
88 | } else {
89 | line = "// " + line
90 | }
91 | out = append(out, &ast.Comment{
92 | Slash: slash,
93 | Text: line,
94 | })
95 | }
96 | if len(directives) > 0 {
97 | out = append(out, &ast.Comment{
98 | Slash: slash,
99 | Text: "//",
100 | })
101 | for _, c := range directives {
102 | out = append(out, &ast.Comment{
103 | Slash: slash,
104 | Text: c.Text,
105 | })
106 | }
107 | }
108 | return out
109 | }
110 |
111 | // isDirective reports whether c is a comment directive.
112 | // See go.dev/issue/37974.
113 | // This code is also in go/ast.
114 | func isDirective(c string) bool {
115 | // "//line " is a line directive.
116 | // "//extern " is for gccgo.
117 | // "//export " is for cgo.
118 | // (The // has been removed.)
119 | if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") {
120 | return true
121 | }
122 |
123 | // "//[a-z0-9]+:[a-z0-9]"
124 | // (The // has been removed.)
125 | colon := strings.Index(c, ":")
126 | if colon <= 0 || colon+1 >= len(c) {
127 | return false
128 | }
129 | for i := 0; i <= colon+1; i++ {
130 | if i == colon {
131 | continue
132 | }
133 | b := c[i]
134 | if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') {
135 | return false
136 | }
137 | }
138 | return true
139 | }
140 |
141 | // allStars reports whether text is the interior of an
142 | // old-style /* */ comment with a star at the start of each line.
143 | func allStars(text string) bool {
144 | for i := 0; i < len(text); i++ {
145 | if text[i] == '\n' {
146 | j := i + 1
147 | for j < len(text) && (text[j] == ' ' || text[j] == '\t') {
148 | j++
149 | }
150 | if j < len(text) && text[j] != '*' {
151 | return false
152 | }
153 | }
154 | }
155 | return true
156 | }
157 |
--------------------------------------------------------------------------------
/internal/govendor/go/printer/gobuild.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 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 printer
6 |
7 | import (
8 | "go/build/constraint"
9 | "slices"
10 | "text/tabwriter"
11 | )
12 |
13 | func (p *printer) fixGoBuildLines() {
14 | if len(p.goBuild)+len(p.plusBuild) == 0 {
15 | return
16 | }
17 |
18 | // Find latest possible placement of //go:build and // +build comments.
19 | // That's just after the last blank line before we find a non-comment.
20 | // (We'll add another blank line after our comment block.)
21 | // When we start dropping // +build comments, we can skip over /* */ comments too.
22 | // Note that we are processing tabwriter input, so every comment
23 | // begins and ends with a tabwriter.Escape byte.
24 | // And some newlines have turned into \f bytes.
25 | insert := 0
26 | for pos := 0; ; {
27 | // Skip leading space at beginning of line.
28 | blank := true
29 | for pos < len(p.output) && (p.output[pos] == ' ' || p.output[pos] == '\t') {
30 | pos++
31 | }
32 | // Skip over // comment if any.
33 | if pos+3 < len(p.output) && p.output[pos] == tabwriter.Escape && p.output[pos+1] == '/' && p.output[pos+2] == '/' {
34 | blank = false
35 | for pos < len(p.output) && !isNL(p.output[pos]) {
36 | pos++
37 | }
38 | }
39 | // Skip over \n at end of line.
40 | if pos >= len(p.output) || !isNL(p.output[pos]) {
41 | break
42 | }
43 | pos++
44 |
45 | if blank {
46 | insert = pos
47 | }
48 | }
49 |
50 | // If there is a //go:build comment before the place we identified,
51 | // use that point instead. (Earlier in the file is always fine.)
52 | if len(p.goBuild) > 0 && p.goBuild[0] < insert {
53 | insert = p.goBuild[0]
54 | } else if len(p.plusBuild) > 0 && p.plusBuild[0] < insert {
55 | insert = p.plusBuild[0]
56 | }
57 |
58 | var x constraint.Expr
59 | switch len(p.goBuild) {
60 | case 0:
61 | // Synthesize //go:build expression from // +build lines.
62 | for _, pos := range p.plusBuild {
63 | y, err := constraint.Parse(p.commentTextAt(pos))
64 | if err != nil {
65 | x = nil
66 | break
67 | }
68 | if x == nil {
69 | x = y
70 | } else {
71 | x = &constraint.AndExpr{X: x, Y: y}
72 | }
73 | }
74 | case 1:
75 | // Parse //go:build expression.
76 | x, _ = constraint.Parse(p.commentTextAt(p.goBuild[0]))
77 | }
78 |
79 | var block []byte
80 | if x == nil {
81 | // Don't have a valid //go:build expression to treat as truth.
82 | // Bring all the lines together but leave them alone.
83 | // Note that these are already tabwriter-escaped.
84 | for _, pos := range p.goBuild {
85 | block = append(block, p.lineAt(pos)...)
86 | }
87 | for _, pos := range p.plusBuild {
88 | block = append(block, p.lineAt(pos)...)
89 | }
90 | } else {
91 | block = append(block, tabwriter.Escape)
92 | block = append(block, "//go:build "...)
93 | block = append(block, x.String()...)
94 | block = append(block, tabwriter.Escape, '\n')
95 | if len(p.plusBuild) > 0 {
96 | lines, err := constraint.PlusBuildLines(x)
97 | if err != nil {
98 | lines = []string{"// +build error: " + err.Error()}
99 | }
100 | for _, line := range lines {
101 | block = append(block, tabwriter.Escape)
102 | block = append(block, line...)
103 | block = append(block, tabwriter.Escape, '\n')
104 | }
105 | }
106 | }
107 | block = append(block, '\n')
108 |
109 | // Build sorted list of lines to delete from remainder of output.
110 | toDelete := append(p.goBuild, p.plusBuild...)
111 | slices.Sort(toDelete)
112 |
113 | // Collect output after insertion point, with lines deleted, into after.
114 | var after []byte
115 | start := insert
116 | for _, end := range toDelete {
117 | if end < start {
118 | continue
119 | }
120 | after = appendLines(after, p.output[start:end])
121 | start = end + len(p.lineAt(end))
122 | }
123 | after = appendLines(after, p.output[start:])
124 | if n := len(after); n >= 2 && isNL(after[n-1]) && isNL(after[n-2]) {
125 | after = after[:n-1]
126 | }
127 |
128 | p.output = p.output[:insert]
129 | p.output = append(p.output, block...)
130 | p.output = append(p.output, after...)
131 | }
132 |
133 | // appendLines is like append(x, y...)
134 | // but it avoids creating doubled blank lines,
135 | // which would not be gofmt-standard output.
136 | // It assumes that only whole blocks of lines are being appended,
137 | // not line fragments.
138 | func appendLines(x, y []byte) []byte {
139 | if len(y) > 0 && isNL(y[0]) && // y starts in blank line
140 | (len(x) == 0 || len(x) >= 2 && isNL(x[len(x)-1]) && isNL(x[len(x)-2])) { // x is empty or ends in blank line
141 | y = y[1:] // delete y's leading blank line
142 | }
143 | return append(x, y...)
144 | }
145 |
146 | func (p *printer) lineAt(start int) []byte {
147 | pos := start
148 | for pos < len(p.output) && !isNL(p.output[pos]) {
149 | pos++
150 | }
151 | if pos < len(p.output) {
152 | pos++
153 | }
154 | return p.output[start:pos]
155 | }
156 |
157 | func (p *printer) commentTextAt(start int) string {
158 | if start < len(p.output) && p.output[start] == tabwriter.Escape {
159 | start++
160 | }
161 | pos := start
162 | for pos < len(p.output) && p.output[pos] != tabwriter.Escape && !isNL(p.output[pos]) {
163 | pos++
164 | }
165 | return string(p.output[start:pos])
166 | }
167 |
168 | func isNL(b byte) bool {
169 | return b == '\n' || b == '\f'
170 | }
171 |
--------------------------------------------------------------------------------
/internal/govendor/version.txt:
--------------------------------------------------------------------------------
1 | go1.24.0
2 |
--------------------------------------------------------------------------------
/internal/version/version.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, Daniel Martí
2 | // See LICENSE for licensing information
3 |
4 | package version
5 |
6 | import (
7 | "encoding/json"
8 | "fmt"
9 | "os"
10 | "runtime"
11 | "runtime/debug"
12 | "time"
13 |
14 | "golang.org/x/mod/module"
15 | )
16 |
17 | // Note that this is not a main package, so a "var version" will not work with
18 | // our go-cross script which uses -ldflags=main.version=xxx.
19 |
20 | const ourModulePath = "mvdan.cc/gofumpt"
21 |
22 | const fallbackVersion = "(devel)" // to match the default from runtime/debug
23 |
24 | func findModule(info *debug.BuildInfo, modulePath string) *debug.Module {
25 | if info.Main.Path == modulePath {
26 | return &info.Main
27 | }
28 | for _, dep := range info.Deps {
29 | if dep.Path == modulePath {
30 | return dep
31 | }
32 | }
33 | return nil
34 | }
35 |
36 | func gofumptVersion() string {
37 | info, ok := debug.ReadBuildInfo()
38 | if !ok {
39 | return fallbackVersion // no build info available
40 | }
41 | // Note that gofumpt may be used as a library via the format package,
42 | // so we cannot assume it is the main module in the build.
43 | mod := findModule(info, ourModulePath)
44 | if mod == nil {
45 | return fallbackVersion // not found?
46 | }
47 | if mod.Replace != nil {
48 | mod = mod.Replace
49 | }
50 |
51 | // If we found a meaningful version, we are done.
52 | // If gofumpt is not the main module, stop as well,
53 | // as VCS info is only for the main module.
54 | if mod.Version != "(devel)" || mod != &info.Main {
55 | return mod.Version
56 | }
57 |
58 | // Fall back to trying to use VCS information.
59 | // Until https://github.com/golang/go/issues/50603 is implemented,
60 | // manually construct something like a pseudo-version.
61 | // TODO: remove when this code is dead, hopefully in Go 1.20.
62 |
63 | // For the tests, as we don't want the VCS information to change over time.
64 | if v := os.Getenv("GARBLE_TEST_BUILDSETTINGS"); v != "" {
65 | var extra []debug.BuildSetting
66 | if err := json.Unmarshal([]byte(v), &extra); err != nil {
67 | panic(err)
68 | }
69 | info.Settings = append(info.Settings, extra...)
70 | }
71 |
72 | var vcsTime time.Time
73 | var vcsRevision string
74 | for _, setting := range info.Settings {
75 | switch setting.Key {
76 | case "vcs.time":
77 | // If the format is invalid, we'll print a zero timestamp.
78 | vcsTime, _ = time.Parse(time.RFC3339Nano, setting.Value)
79 | case "vcs.revision":
80 | vcsRevision = setting.Value
81 | if len(vcsRevision) > 12 {
82 | vcsRevision = vcsRevision[:12]
83 | }
84 | }
85 | }
86 | if vcsRevision != "" {
87 | return module.PseudoVersion("", "", vcsTime, vcsRevision)
88 | }
89 | return fallbackVersion
90 | }
91 |
92 | func goVersion() string {
93 | // For the tests, as we don't want the Go version to change over time.
94 | if testVersion := os.Getenv("GO_VERSION_TEST"); testVersion != "" {
95 | return testVersion
96 | }
97 | return runtime.Version()
98 | }
99 |
100 | func String(injected string) string {
101 | if injected != "" {
102 | return fmt.Sprintf("%s (%s)", injected, goVersion())
103 | }
104 | return fmt.Sprintf("%s (%s)", gofumptVersion(), goVersion())
105 | }
106 |
--------------------------------------------------------------------------------
/main_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019, Daniel Martí
2 | // See LICENSE for licensing information
3 |
4 | package main
5 |
6 | import (
7 | "encoding/json"
8 | "flag"
9 | "os/exec"
10 | "path/filepath"
11 | "testing"
12 |
13 | "github.com/go-quicktest/qt"
14 |
15 | "github.com/rogpeppe/go-internal/gotooltest"
16 | "github.com/rogpeppe/go-internal/testscript"
17 | )
18 |
19 | func TestMain(m *testing.M) {
20 | testscript.Main(m, map[string]func(){
21 | "gofumpt": main,
22 | })
23 | }
24 |
25 | var update = flag.Bool("u", false, "update testscript output files")
26 |
27 | func TestScript(t *testing.T) {
28 | t.Parallel()
29 |
30 | var goEnv struct {
31 | GOCACHE string
32 | GOMODCACHE string
33 | GOMOD string
34 | }
35 | out, err := exec.Command("go", "env", "-json").CombinedOutput()
36 | if err != nil {
37 | t.Fatal(err)
38 | }
39 | if err := json.Unmarshal(out, &goEnv); err != nil {
40 | t.Fatal(err)
41 | }
42 |
43 | p := testscript.Params{
44 | Dir: filepath.Join("testdata", "script"),
45 | UpdateScripts: *update,
46 | RequireExplicitExec: true,
47 | Setup: func(env *testscript.Env) error {
48 | env.Setenv("GOCACHE", goEnv.GOCACHE)
49 | env.Setenv("GOMODCACHE", goEnv.GOMODCACHE)
50 | env.Setenv("GOMOD_DIR", filepath.Dir(goEnv.GOMOD))
51 | return nil
52 | },
53 | }
54 | err = gotooltest.Setup(&p)
55 | qt.Assert(t, qt.IsNil(err))
56 | testscript.Run(t, p)
57 | }
58 |
--------------------------------------------------------------------------------
/testdata/gofumpt-external/go.mod:
--------------------------------------------------------------------------------
1 | module test/gofumpt-external
2 |
3 | go 1.23
4 |
5 | require mvdan.cc/gofumpt v0.6.1-0.20240717113859-88a300bbd6dc
6 |
7 | require (
8 | github.com/google/go-cmp v0.6.0 // indirect
9 | golang.org/x/mod v0.14.0 // indirect
10 | golang.org/x/tools v0.17.0 // indirect
11 | )
12 |
--------------------------------------------------------------------------------
/testdata/gofumpt-external/go.sum:
--------------------------------------------------------------------------------
1 | github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
2 | github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
3 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
4 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
5 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
6 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
7 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
8 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
9 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
10 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
11 | golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
12 | golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
13 | golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
14 | golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
15 | mvdan.cc/gofumpt v0.6.1-0.20240717113859-88a300bbd6dc h1:XDr9kee1c8z5RRdwJnLWbAJ+x8NdkShxcqHSedJ6l+8=
16 | mvdan.cc/gofumpt v0.6.1-0.20240717113859-88a300bbd6dc/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
17 |
--------------------------------------------------------------------------------
/testdata/gofumpt-external/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "io"
5 | "os"
6 |
7 | "mvdan.cc/gofumpt/format"
8 | )
9 |
10 | func main() {
11 | orig, err := io.ReadAll(os.Stdin)
12 | if err != nil {
13 | panic(err)
14 | }
15 | formatted, err := format.Source(orig, format.Options{
16 | LangVersion: "go1.16",
17 | })
18 | if err != nil {
19 | panic(err)
20 | }
21 | os.Stdout.Write(formatted)
22 | }
23 |
--------------------------------------------------------------------------------
/testdata/script/assignment-newlines.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | func f() {
11 | foo :=
12 |
13 |
14 | "bar"
15 |
16 | foo :=
17 | "bar"
18 |
19 | _, _ =
20 | 0,
21 | 1
22 |
23 | _, _ = 0,
24 | 1
25 |
26 | _ =
27 | `
28 | foo
29 | `
30 |
31 | _ = /* inline */
32 | "foo"
33 |
34 | _ = // inline
35 | "foo"
36 | }
37 |
38 | -- foo.go.golden --
39 | package p
40 |
41 | func f() {
42 | foo := "bar"
43 |
44 | foo := "bar"
45 |
46 | _, _ = 0,
47 | 1
48 |
49 | _, _ = 0,
50 | 1
51 |
52 | _ = `
53 | foo
54 | `
55 |
56 | _ = /* inline */ "foo"
57 |
58 | _ = // inline
59 | "foo"
60 | }
61 |
--------------------------------------------------------------------------------
/testdata/script/block-empty.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | func f() {
11 | if true {
12 | // lone comment
13 | }
14 | {
15 |
16 | }
17 |
18 | {
19 |
20 | // lone comment
21 |
22 | }
23 |
24 | type S struct {
25 |
26 |
27 | // lone comment
28 |
29 |
30 | }
31 |
32 | type I interface {
33 |
34 |
35 | // lone comment
36 |
37 |
38 | }
39 |
40 |
41 | }
42 |
43 | type SOut struct {
44 |
45 | // lone comment
46 |
47 | }
48 |
49 | type IOut interface {
50 |
51 |
52 | // lone comment
53 |
54 |
55 | }
56 | -- foo.go.golden --
57 | package p
58 |
59 | func f() {
60 | if true {
61 | // lone comment
62 | }
63 | {
64 | }
65 |
66 | {
67 | // lone comment
68 | }
69 |
70 | type S struct {
71 | // lone comment
72 | }
73 |
74 | type I interface {
75 | // lone comment
76 | }
77 | }
78 |
79 | type SOut struct {
80 | // lone comment
81 | }
82 |
83 | type IOut interface {
84 | // lone comment
85 | }
86 |
--------------------------------------------------------------------------------
/testdata/script/block-single.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | func f() {
11 | if true {
12 |
13 | println()
14 | }
15 |
16 | for true {
17 | println()
18 |
19 | }
20 |
21 | {
22 |
23 |
24 | println(1, 2,
25 | 3, 4, `foo
26 | bar`)
27 |
28 |
29 | }
30 |
31 | {
32 |
33 | // comment directly before
34 | println()
35 |
36 | // comment after
37 |
38 | }
39 |
40 | {
41 |
42 | // comment before
43 |
44 | println()
45 | // comment directly after
46 |
47 | }
48 |
49 | // For readability; the empty line helps separate the multi-line
50 | // condition from the body.
51 | if true &&
52 | true {
53 |
54 | println()
55 | }
56 | for true &&
57 | true {
58 |
59 | println()
60 | }
61 | if true &&
62 | true {
63 |
64 | // documented single statement
65 | println()
66 | }
67 | }
68 | -- foo.go.golden --
69 | package p
70 |
71 | func f() {
72 | if true {
73 | println()
74 | }
75 |
76 | for true {
77 | println()
78 | }
79 |
80 | {
81 | println(1, 2,
82 | 3, 4, `foo
83 | bar`)
84 | }
85 |
86 | {
87 | // comment directly before
88 | println()
89 |
90 | // comment after
91 | }
92 |
93 | {
94 | // comment before
95 |
96 | println()
97 | // comment directly after
98 | }
99 |
100 | // For readability; the empty line helps separate the multi-line
101 | // condition from the body.
102 | if true &&
103 | true {
104 |
105 | println()
106 | }
107 | for true &&
108 | true {
109 |
110 | println()
111 | }
112 | if true &&
113 | true {
114 |
115 | // documented single statement
116 | println()
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/testdata/script/cgo.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | import "C"
11 | import "os"
12 |
13 | import `C`
14 | import "os"
15 |
16 | import "C"
17 | import (
18 | "io"
19 | "utf8"
20 | )
21 |
22 | import `C`
23 | import (
24 | "io"
25 | "utf8"
26 | )
27 |
28 | -- foo.go.golden --
29 | package p
30 |
31 | import "C"
32 | import "os"
33 |
34 | import "C"
35 | import "os"
36 |
37 | import "C"
38 | import (
39 | "io"
40 | "utf8"
41 | )
42 |
43 | import "C"
44 | import (
45 | "io"
46 | "utf8"
47 | )
48 |
--------------------------------------------------------------------------------
/testdata/script/clothe-returns.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | func foo() (err error) {
11 | if true {
12 | return
13 | }
14 | if false {
15 | return func() (err2 error) {
16 | return
17 | }
18 | }
19 | return
20 | }
21 |
22 | func bar() (_ int, err error) {
23 | return
24 | }
25 |
26 | func baz() (a, b, c int) {
27 | return
28 | }
29 |
30 | func qux() (file string, b int, err error) {
31 | if err == nil {
32 | return
33 | }
34 |
35 | // A comment
36 | return
37 | }
38 |
39 | // quux does quuxy things
40 | func quux() {}
41 | -- foo.go.golden --
42 | package p
43 |
44 | func foo() (err error) {
45 | if true {
46 | return err
47 | }
48 | if false {
49 | return func() (err2 error) {
50 | return err2
51 | }
52 | }
53 | return err
54 | }
55 |
56 | func bar() (_ int, err error) {
57 | return
58 | }
59 |
60 | func baz() (a, b, c int) {
61 | return a, b, c
62 | }
63 |
64 | func qux() (file string, b int, err error) {
65 | if err == nil {
66 | return file, b, err
67 | }
68 |
69 | // A comment
70 | return file, b, err
71 | }
72 |
73 | // quux does quuxy things
74 | func quux() {}
75 |
--------------------------------------------------------------------------------
/testdata/script/comment-spaced.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | //go:build tag
9 | // +build tag
10 |
11 | package p
12 |
13 | //go:generate some command
14 |
15 | //go:unknowndirective
16 |
17 | //lint:disablefoo
18 |
19 | //go-sumtype:decl Foo
20 |
21 | //nolint
22 |
23 | //nolint // explanation
24 |
25 | //nolint:somelinter // explanation
26 |
27 | //NOSONAR
28 |
29 | //NOSONAR // explanation
30 |
31 | //noinspection ALL
32 |
33 | //noinspection foo,bar
34 |
35 | //not actually: a directive
36 |
37 | //https://just.one/url
38 |
39 | //TODO: do something
40 |
41 | //export CgoFunc
42 |
43 | //extern open
44 | func c_open(name *byte, mode int, perm int) int
45 |
46 | //line 123
47 |
48 | //sys Unlink(path string) (err error)
49 |
50 | //sysnb Getpid() (pid int)
51 |
52 | //foo is foo.
53 | type foo int
54 |
55 | // comment with a tab.
56 |
57 | // comment with many spaces
58 |
59 | //comment group
60 | //123 numbers too
61 |
62 | // comment group
63 | //123 numbers too
64 |
65 | //{
66 | //this is probably code
67 | //}
68 |
69 | ////////////
70 | // ascii art
71 | //----------
72 |
73 | //
74 | -- foo.go.golden --
75 | //go:build tag
76 | // +build tag
77 |
78 | package p
79 |
80 | //go:generate some command
81 |
82 | //go:unknowndirective
83 |
84 | //lint:disablefoo
85 |
86 | //go-sumtype:decl Foo
87 |
88 | //nolint
89 |
90 | //nolint // explanation
91 |
92 | //nolint:somelinter // explanation
93 |
94 | //NOSONAR
95 |
96 | //NOSONAR // explanation
97 |
98 | //noinspection ALL
99 |
100 | //noinspection foo,bar
101 |
102 | // not actually: a directive
103 |
104 | // https://just.one/url
105 |
106 | // TODO: do something
107 |
108 | //export CgoFunc
109 |
110 | //extern open
111 | func c_open(name *byte, mode int, perm int) int
112 |
113 | //line 123
114 |
115 | //sys Unlink(path string) (err error)
116 |
117 | //sysnb Getpid() (pid int)
118 |
119 | // foo is foo.
120 | type foo int
121 |
122 | // comment with a tab.
123 |
124 | // comment with many spaces
125 |
126 | // comment group
127 | // 123 numbers too
128 |
129 | // comment group
130 | // 123 numbers too
131 |
132 | //{
133 | //this is probably code
134 | //}
135 |
136 | ////////////
137 | // ascii art
138 | //----------
139 |
140 | //
141 |
--------------------------------------------------------------------------------
/testdata/script/composite-literals-leading-lines.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | var _ = []string{
11 |
12 |
13 | "foo",
14 | }
15 |
16 | var _ = []string{
17 |
18 | "foo",
19 | }
20 |
21 | var _ = []string{
22 |
23 | // joint comment
24 | "foo",
25 | }
26 |
27 | var _ = []string{
28 | // separate comment
29 |
30 | "foo",
31 | }
32 |
33 | var _ = map[string]string{
34 |
35 |
36 | "foo": "bar",
37 | }
38 |
39 | var _ = map[string]string{
40 |
41 | "foo": "bar",
42 | }
43 |
44 | var _ = map[string]string{
45 |
46 | // joint comment
47 | "foo": "bar",
48 | }
49 |
50 | var _ = map[string]string{
51 | // separate comment
52 |
53 | "foo": "bar",
54 | }
55 |
56 | var _ = map[string]string{
57 | /*
58 | joint comment
59 | */
60 | "foo": "bar",
61 | }
62 |
63 | -- foo.go.golden --
64 | package p
65 |
66 | var _ = []string{
67 | "foo",
68 | }
69 |
70 | var _ = []string{
71 | "foo",
72 | }
73 |
74 | var _ = []string{
75 | // joint comment
76 | "foo",
77 | }
78 |
79 | var _ = []string{
80 | // separate comment
81 |
82 | "foo",
83 | }
84 |
85 | var _ = map[string]string{
86 | "foo": "bar",
87 | }
88 |
89 | var _ = map[string]string{
90 | "foo": "bar",
91 | }
92 |
93 | var _ = map[string]string{
94 | // joint comment
95 | "foo": "bar",
96 | }
97 |
98 | var _ = map[string]string{
99 | // separate comment
100 |
101 | "foo": "bar",
102 | }
103 |
104 | var _ = map[string]string{
105 | /*
106 | joint comment
107 | */
108 | "foo": "bar",
109 | }
110 |
--------------------------------------------------------------------------------
/testdata/script/composite-multiline.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | var _ = []int{}
11 |
12 | var _ = []int{
13 | }
14 |
15 | var _ = []int{1, 2,
16 | 3, 4}
17 |
18 | var _ = []int{
19 | 1, 2, 3, 4}
20 |
21 | var _ = [][]string{{
22 | "no need for more newlines",
23 | "if wrapping a single expression",
24 | }}
25 |
26 | var _ = []string{`
27 | no need for newlines
28 | `, `
29 | if no elements are surrounded by newlines
30 | `}
31 |
32 | var _ = []struct{ a int }{
33 | { // consistent
34 | a: 1,
35 | },
36 | {
37 | a: 2,
38 | }, { // inconsistent
39 | a: 3,
40 | },
41 | }
42 |
43 | var _ = []struct{ a int }{{
44 | a: 1,
45 | }, {
46 | a: 2,
47 | }, {
48 | a: 3,
49 | }}
50 |
51 | var _ interface{
52 | }
53 |
54 | func _(struct{
55 | })
56 |
57 | var _ = []interface {
58 | }{1, 2, 3}
59 |
60 | func _(
61 | )
62 |
63 | type T struct {
64 | Foo // comment
65 | Bar struct { // comment
66 | }
67 | }
68 | -- foo.go.golden --
69 | package p
70 |
71 | var _ = []int{}
72 |
73 | var _ = []int{}
74 |
75 | var _ = []int{
76 | 1, 2,
77 | 3, 4,
78 | }
79 |
80 | var _ = []int{
81 | 1, 2, 3, 4,
82 | }
83 |
84 | var _ = [][]string{{
85 | "no need for more newlines",
86 | "if wrapping a single expression",
87 | }}
88 |
89 | var _ = []string{`
90 | no need for newlines
91 | `, `
92 | if no elements are surrounded by newlines
93 | `}
94 |
95 | var _ = []struct{ a int }{
96 | { // consistent
97 | a: 1,
98 | },
99 | {
100 | a: 2,
101 | },
102 | { // inconsistent
103 | a: 3,
104 | },
105 | }
106 |
107 | var _ = []struct{ a int }{{
108 | a: 1,
109 | }, {
110 | a: 2,
111 | }, {
112 | a: 3,
113 | }}
114 |
115 | var _ interface{}
116 |
117 | func _(struct{})
118 |
119 | var _ = []interface{}{1, 2, 3}
120 |
121 | func _()
122 |
123 | type T struct {
124 | Foo // comment
125 | Bar struct { // comment
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/testdata/script/decl-group-many.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | var single = "foo"
11 | var another = "bar"
12 |
13 | const one = 'q'
14 | const two = 'w'
15 | const three = 'e'
16 | const four = 'r'
17 |
18 | var not = 'a'
19 |
20 | var v1 = 's'
21 | //go:embed hello.txt
22 | var v2 = 'd'
23 |
24 | var v1 = 's'
25 | // comment line 1
26 | // comment line 2
27 | var v2 = 'd'
28 |
29 | var v1 = "mixed"
30 | const c1 = "mixed"
31 |
32 | //go:embed hello.txt
33 | var v1 = 's'
34 | var v2 = 'd'
35 | var v3 = 'd'
36 |
37 | // comment
38 | var v1 = 's'
39 | var v2 = 'd'
40 | /* comment */
41 | var v3 = 'd'
42 |
43 | const inline1 = "s1" // c1
44 | const inline2 = "s2" // c2
45 | const inline3 = "s3" // c3
46 | -- foo.go.golden --
47 | package p
48 |
49 | var (
50 | single = "foo"
51 | another = "bar"
52 | )
53 |
54 | const (
55 | one = 'q'
56 | two = 'w'
57 | three = 'e'
58 | four = 'r'
59 | )
60 |
61 | var not = 'a'
62 |
63 | var v1 = 's'
64 |
65 | //go:embed hello.txt
66 | var v2 = 'd'
67 |
68 | var (
69 | v1 = 's'
70 | // comment line 1
71 | // comment line 2
72 | v2 = 'd'
73 | )
74 |
75 | var v1 = "mixed"
76 |
77 | const c1 = "mixed"
78 |
79 | //go:embed hello.txt
80 | var v1 = 's'
81 |
82 | var (
83 | v2 = 'd'
84 | v3 = 'd'
85 | )
86 |
87 | // comment
88 | var (
89 | v1 = 's'
90 | v2 = 'd'
91 | /* comment */
92 | v3 = 'd'
93 | )
94 |
95 | const (
96 | inline1 = "s1" // c1
97 | inline2 = "s2" // c2
98 | inline3 = "s3" // c3
99 | )
100 |
--------------------------------------------------------------------------------
/testdata/script/decl-group-single.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w f1.go f2.go
2 | cmp f1.go f1.go.golden
3 | cmp f2.go f2.go.golden
4 |
5 | exec gofumpt -d f1.go.golden f2.go.golden
6 | ! stdout .
7 |
8 | -- f1.go --
9 | package p
10 |
11 | import "non-grouped"
12 |
13 | import (
14 | "grouped"
15 | )
16 |
17 | var single = "foo"
18 |
19 | var (
20 | // verbose is verbose.
21 | verbose = "bar"
22 | )
23 |
24 | // This entire block has a comment.
25 | var (
26 | groupComment = "bar"
27 | )
28 |
29 | var (
30 | multiple1 string
31 | multiple2 string
32 | )
33 |
34 | const (
35 | first = iota
36 | )
37 |
38 | var (
39 | multiline = []string{
40 | "foo",
41 | "bar",
42 | }
43 | )
44 |
45 | var (
46 | foo = "foo"
47 | // bar = "bar"
48 | // baz = "baz"
49 | )
50 | -- f1.go.golden --
51 | package p
52 |
53 | import "non-grouped"
54 |
55 | import (
56 | "grouped"
57 | )
58 |
59 | var single = "foo"
60 |
61 | // verbose is verbose.
62 | var verbose = "bar"
63 |
64 | // This entire block has a comment.
65 | var (
66 | groupComment = "bar"
67 | )
68 |
69 | var (
70 | multiple1 string
71 | multiple2 string
72 | )
73 |
74 | const (
75 | first = iota
76 | )
77 |
78 | var multiline = []string{
79 | "foo",
80 | "bar",
81 | }
82 |
83 | var foo = "foo"
84 |
85 | // bar = "bar"
86 | // baz = "baz"
87 | -- f2.go --
88 | package p
89 |
90 | func _() {
91 | var (
92 | _ int
93 | )
94 | }
95 | -- f2.go.golden --
96 | package p
97 |
98 | func _() {
99 | var _ int
100 | }
101 |
--------------------------------------------------------------------------------
/testdata/script/decls-separated.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | func f1() { println("single line") }
11 | func f2() { println("single line") }
12 |
13 | func f3() {
14 | println("multiline")
15 | }
16 | func f4() {
17 | println("multiline")
18 | }
19 |
20 | // l1 is a var.
21 | var l1 = []int{
22 | 1, 2,
23 | }
24 | // l2 is a var.
25 | var l2 = []int{
26 | 3, 4,
27 | }
28 |
29 | var (
30 | s3 = `
31 | ok if grouped together
32 | `
33 | s4 = `
34 | ok if grouped together
35 | `
36 | )
37 | var _ = "ok if either isn't multiline"
38 | -- foo.go.golden --
39 | package p
40 |
41 | func f1() { println("single line") }
42 | func f2() { println("single line") }
43 |
44 | func f3() {
45 | println("multiline")
46 | }
47 |
48 | func f4() {
49 | println("multiline")
50 | }
51 |
52 | // l1 is a var.
53 | var l1 = []int{
54 | 1, 2,
55 | }
56 |
57 | // l2 is a var.
58 | var l2 = []int{
59 | 3, 4,
60 | }
61 |
62 | var (
63 | s3 = `
64 | ok if grouped together
65 | `
66 | s4 = `
67 | ok if grouped together
68 | `
69 | )
70 | var _ = "ok if either isn't multiline"
71 |
--------------------------------------------------------------------------------
/testdata/script/deprecated-flags.txtar:
--------------------------------------------------------------------------------
1 | cp foo.orig.go foo.go
2 | ! exec gofumpt -w -r foo foo.go
3 | stderr 'the rewrite flag is no longer available; use "gofmt -r" instead\n'
4 | cmp foo.orig.go foo.go
5 |
6 | exec gofumpt -w -s foo.go
7 | stderr 'warning: -s is deprecated as it is always enabled\n'
8 | cmp foo.go foo.go.golden
9 |
10 | exec gofumpt -d foo.go.golden
11 | ! stdout .
12 |
13 | -- foo.orig.go --
14 | package p
15 |
16 | func f() {
17 |
18 | println("foo")
19 |
20 | }
21 | -- foo.go.golden --
22 | package p
23 |
24 | func f() {
25 | println("foo")
26 | }
27 |
--------------------------------------------------------------------------------
/testdata/script/diagnose.txtar:
--------------------------------------------------------------------------------
1 | env GO_VERSION_TEST=go1.18.29
2 |
3 | # First, test a local build of gofumpt resulting from 'git clone'.
4 | # Its version will be inferred from VCS, but since we want a stable test,
5 | # we mock the VCS information. Note that test binaries do not have VCS info.
6 | # Data obtained from a real build while developing.
7 | env GARBLE_TEST_BUILDSETTINGS='[{"Key":"vcs","Value":"git"},{"Key":"vcs.revision","Value":"8dda8068d9f339047fc1777b688afb66a0a0db17"},{"Key":"vcs.time","Value":"2022-07-27T15:58:40Z"},{"Key":"vcs.modified","Value":"true"}]'
8 | exec gofumpt foo.go
9 | cmp stdout foo.go.golden
10 |
11 | exec gofumpt outdated.go
12 | cmp stdout foo.go.golden
13 |
14 | exec gofumpt -extra foo.go
15 | cmp stdout foo.go.golden-extra
16 |
17 | exec gofumpt -lang=go1 foo.go
18 | cmp stdout foo.go.golden-lang
19 |
20 | exec gofumpt -d nochange.go
21 | ! stdout .
22 |
23 | exec gofumpt -d foo.go.golden
24 | ! stdout .
25 |
26 | exec gofumpt -d -extra foo.go.golden-extra
27 | ! stdout .
28 |
29 | # A local build without VCS information will result in a missing version.
30 | env GARBLE_TEST_BUILDSETTINGS='[]'
31 | exec gofumpt foo.go
32 | cmp stdout foo.go.golden-devel
33 |
34 | [short] stop 'the rest of this test builds gofumpt binaries'
35 |
36 | # We want a published version of gofumpt on the public module proxies,
37 | # because that's the only way that its module version will be included.
38 | # Using a directory replace directive will not work.
39 | # This means that any change in how gofumpt reports its own version
40 | # will require two pull requests, the second one updating the test script.
41 | # We could consider using go-internal/goproxytest, but then we would need to
42 | # manually run something like go-internal/cmd/txtar-addmod reguarly.
43 | # Or teach goproxytest to serve a mock version of gofumpt from its local checkout.
44 | # Either way, both are relatively overkill for now.
45 | # Update this pseudo-version to master from time to time as needed.
46 | env GOBIN=${WORK}/bin
47 | env GOFUMPT_PUBLISHED_VERSION=v0.6.1-0.20240717113859-88a300bbd6dc
48 |
49 | # TODO: update these once the library fix hits master
50 |
51 | # gofumpt as the main binary with a real module version.
52 | go install mvdan.cc/gofumpt@${GOFUMPT_PUBLISHED_VERSION}
53 | exec ${GOBIN}/gofumpt foo.go
54 | cmp stdout foo.go.golden-released
55 |
56 | # gofumpt as a library with a real module version.
57 | cd ${GOMOD_DIR}/testdata/gofumpt-external
58 | go install .
59 | cd ${WORK}
60 | stdin foo.go
61 | exec ${GOBIN}/gofumpt-external
62 | cmp stdout foo.go.golden-external
63 |
64 | -- go.mod --
65 | module test
66 |
67 | go 1.16
68 | -- foo.go --
69 | package p
70 |
71 | //gofumpt:diagnose
72 | -- outdated.go --
73 | package p
74 |
75 | //gofumpt:diagnose v0.1.0
76 | -- nochange.go --
77 | package p
78 |
79 | //gofumpt:diagnosefoobar
80 | -- foo.go.golden --
81 | package p
82 |
83 | //gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=go1.16 -modpath=test
84 | -- foo.go.golden-devel --
85 | package p
86 |
87 | //gofumpt:diagnose version: (devel) (go1.18.29) flags: -lang=go1.16 -modpath=test
88 | -- foo.go.golden-extra --
89 | package p
90 |
91 | //gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=go1.16 -modpath=test -extra
92 | -- foo.go.golden-lang --
93 | package p
94 |
95 | //gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=go1 -modpath=test
96 | -- foo.go.golden-released --
97 | package p
98 |
99 | //gofumpt:diagnose version: v0.6.1-0.20240717113859-88a300bbd6dc (go1.18.29) flags: -lang=go1.16 -modpath=test
100 | -- foo.go.golden-external --
101 | package p
102 |
103 | //gofumpt:diagnose version: v0.6.1-0.20240717113859-88a300bbd6dc (go1.18.29) flags: -lang=go1.16 -modpath=
104 |
--------------------------------------------------------------------------------
/testdata/script/diff.txtar:
--------------------------------------------------------------------------------
1 | # gofumpt fails with -d if there is a diff.
2 |
3 | exec gofumpt -d good.go
4 | ! stdout .
5 | ! stderr .
6 |
7 | ! exec gofumpt -d bad.go
8 | cmp stdout bad.go.diff
9 | ! stderr .
10 |
11 | -- good.go --
12 | package p
13 |
14 | func f() {
15 | println("well formatted")
16 | }
17 | -- bad.go --
18 | package p
19 |
20 | func f() {
21 | println("not well formatted")
22 | }
23 | -- bad.go.diff --
24 | diff bad.go.orig bad.go
25 | --- bad.go.orig
26 | +++ bad.go
27 | @@ -1,5 +1,5 @@
28 | package p
29 |
30 | func f() {
31 | -println("not well formatted")
32 | + println("not well formatted")
33 | }
34 |
--------------------------------------------------------------------------------
/testdata/script/func-merge-parameters.txtar:
--------------------------------------------------------------------------------
1 | # By default, this rule isn't enabled.
2 | exec gofumpt foo.go
3 | cmp stdout foo.go
4 |
5 | # It's run with -extra.
6 | exec gofumpt -extra foo.go
7 | cmp stdout foo.go.golden
8 |
9 | exec gofumpt -d foo.go.golden
10 | ! stdout .
11 |
12 | -- foo.go --
13 | package p
14 |
15 | type f func(x int, y int) int
16 |
17 | type i interface {
18 | add(x int, y int)
19 | }
20 |
21 | type s struct {
22 | x int
23 | y int
24 | }
25 |
26 | func mergeAdjacent(x int, y int) {}
27 |
28 | func mergeThreeAdjacent(x int, y int, z int) {}
29 |
30 | func mergeOneWithTwo(x, y int, z int) {}
31 |
32 | func mergeTwoWithOne(x int, y, z int) {}
33 |
34 | func mergeWithComment(
35 | x int, y int, // comment
36 | )
37 |
38 | func mergeAllSyntax(x chan []*foo.Bar, y chan []*foo.Bar) {}
39 |
40 | func dontMergeAnonymousParams(int, int) {}
41 |
42 | func dontMergeMultipleLines(
43 | x int,
44 | y int,
45 | ) {
46 | }
47 |
48 | func dontMergeMultipleLines2(
49 | x,
50 | y int,
51 | z int,
52 | ) {
53 | }
54 |
55 | func dontMergeDifferentKinds(format string, args ...string) {}
56 |
57 | func dontMergeDifferentTypesReturn() (n int, err error) {}
58 | -- foo.go.golden --
59 | package p
60 |
61 | type f func(x, y int) int
62 |
63 | type i interface {
64 | add(x, y int)
65 | }
66 |
67 | type s struct {
68 | x int
69 | y int
70 | }
71 |
72 | func mergeAdjacent(x, y int) {}
73 |
74 | func mergeThreeAdjacent(x, y, z int) {}
75 |
76 | func mergeOneWithTwo(x, y, z int) {}
77 |
78 | func mergeTwoWithOne(x, y, z int) {}
79 |
80 | func mergeWithComment(
81 | x, y int, // comment
82 | )
83 |
84 | func mergeAllSyntax(x, y chan []*foo.Bar) {}
85 |
86 | func dontMergeAnonymousParams(int, int) {}
87 |
88 | func dontMergeMultipleLines(
89 | x int,
90 | y int,
91 | ) {
92 | }
93 |
94 | func dontMergeMultipleLines2(
95 | x,
96 | y int,
97 | z int,
98 | ) {
99 | }
100 |
101 | func dontMergeDifferentKinds(format string, args ...string) {}
102 |
103 | func dontMergeDifferentTypesReturn() (n int, err error) {}
104 |
--------------------------------------------------------------------------------
/testdata/script/func-newlines.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | func f1() {
11 |
12 | println("multiple")
13 |
14 | println("statements")
15 |
16 | }
17 |
18 | func f2() {
19 |
20 | // comment directly before
21 | println()
22 |
23 | // comment after
24 |
25 | }
26 |
27 | func _() {
28 | f3 := func() {
29 |
30 | println()
31 |
32 | }
33 | }
34 |
35 | func multilineParams(p1 string,
36 | p2 string) {
37 |
38 | println("body")
39 |
40 | }
41 |
42 | func multilineParamsUnambiguous(p1 string,
43 | p2 string,
44 | ) {
45 |
46 | println("body")
47 |
48 | }
49 |
50 | func multilineParamsListNoReturn(
51 | p1 string,
52 | p2 string,
53 | ) {
54 |
55 | println("body")
56 |
57 | }
58 |
59 | func multilineParamsListReturningNamedSingleValue(
60 | p1 string,
61 | p2 string,
62 | ) (err error) {
63 |
64 | println("body")
65 | return err
66 |
67 | }
68 |
69 | func multilineParamsListReturningSingleValue(
70 | p1 string,
71 | p2 string,
72 | ) error {
73 |
74 | println("body")
75 | return nil
76 |
77 | }
78 |
79 | func multilineParamsListReturningNamedMultiValues(
80 | p1 string,
81 | p2 string,
82 | ) (s string, err error) {
83 |
84 | println("body")
85 | return s, err
86 |
87 | }
88 |
89 | func multilineParamsListReturningMultiValues(
90 | p1 string,
91 | p2 string,
92 | ) (string, error) {
93 |
94 | println("body")
95 | return "", nil
96 |
97 | }
98 |
99 | func multilineParamsListReturningNamedMultiLineValuesList(
100 | p1 string,
101 | p2 string,
102 | ) (
103 | s string,
104 | err error,
105 | ) {
106 |
107 | println("body")
108 | return s, err
109 |
110 | }
111 |
112 | func multilineParamsListReturningMultiLineValues(
113 | p1 string,
114 | p2 string,
115 | ) (
116 | string,
117 | error,
118 | ) {
119 |
120 | println("body")
121 | return "", nil
122 |
123 | }
124 |
125 | func multilineParamsOneParamNoReturn(
126 | p1 string,
127 | ) {
128 |
129 | println("body")
130 |
131 | }
132 |
133 | func multilineParamsOneParamReturningNamedSingleValue(
134 | p1 string,
135 | ) (err error) {
136 |
137 | println("body")
138 | return err
139 |
140 | }
141 |
142 | func multilineParamsOneParamReturningSingleValue(
143 | p1 string,
144 | ) error {
145 |
146 | println("body")
147 | return nil
148 |
149 | }
150 |
151 | func multilineParamsOneParamReturningNamedMultiValues(
152 | p1 string,
153 | ) (s string, err error) {
154 |
155 | println("body")
156 | return s, err
157 |
158 | }
159 |
160 | func multilineParamsOneParamReturningMultiValues(
161 | p1 string,
162 | ) (string, error) {
163 |
164 | println("body")
165 | return "", nil
166 |
167 | }
168 |
169 | func multilineParamsOneParamReturningNamedMultiLineValuesList(
170 | p1 string,
171 | ) (
172 | s string,
173 | err error,
174 | ) {
175 |
176 | println("body")
177 | return s, err
178 |
179 | }
180 |
181 | func multilineParamsOneParamReturningMultiLineValues(
182 | p1 string,
183 | ) (
184 | string,
185 | error,
186 | ) {
187 |
188 | println("body")
189 | return "", nil
190 |
191 | }
192 |
193 | func multilineResults() (p1 string,
194 | p2 string) {
195 |
196 | println("body")
197 |
198 | }
199 |
200 | func multilineResultsUnambiguous() (p1 string,
201 | p2 string,
202 | ) {
203 |
204 | println("body")
205 |
206 | }
207 |
208 | func multilineNoFields(
209 | ) {
210 |
211 | println("body")
212 |
213 | }
214 |
215 | func f(
216 | foo int,
217 | bar string,
218 | /* baz */) {
219 |
220 | body()
221 | }
222 |
223 | func f2(
224 | foo int,
225 | bar string,
226 | ) (
227 | string,
228 | error,
229 | /* baz */) {
230 |
231 | return "", nil
232 | }
233 |
234 | func multilineResultsMultipleEmptyLines() (p1 string,
235 | p2 string) {
236 |
237 |
238 | println("body")
239 |
240 | }
241 |
242 | func multilineParamsWithoutEmptyLine(p1 string,
243 | p2 string) {
244 | println("body")
245 | }
246 |
247 | func multilineParamsWithoutEmptyLineWithComment(p1 string,
248 | p2 string) {
249 | // comment
250 | println("body")
251 | }
252 |
253 | // Same as the others above, but with a single result parameter without
254 | // parentheses. This used to cause token.File.Offset crashes.
255 | func f(p1 string,
256 | p2 string) int {
257 |
258 | println("body")
259 | return 0
260 | }
261 |
262 | func a() {
263 | f := func(s string,
264 | b bool,
265 | ) {
266 | // foo
267 | }
268 | }
269 |
270 | func f(p1 string,
271 | p2 string) (int, string,
272 | /* baz */) {
273 |
274 | println("body")
275 | return 0, ""
276 | }
277 | -- foo.go.golden --
278 | package p
279 |
280 | func f1() {
281 | println("multiple")
282 |
283 | println("statements")
284 | }
285 |
286 | func f2() {
287 | // comment directly before
288 | println()
289 |
290 | // comment after
291 | }
292 |
293 | func _() {
294 | f3 := func() {
295 | println()
296 | }
297 | }
298 |
299 | func multilineParams(p1 string,
300 | p2 string,
301 | ) {
302 | println("body")
303 | }
304 |
305 | func multilineParamsUnambiguous(p1 string,
306 | p2 string,
307 | ) {
308 | println("body")
309 | }
310 |
311 | func multilineParamsListNoReturn(
312 | p1 string,
313 | p2 string,
314 | ) {
315 | println("body")
316 | }
317 |
318 | func multilineParamsListReturningNamedSingleValue(
319 | p1 string,
320 | p2 string,
321 | ) (err error) {
322 | println("body")
323 | return err
324 | }
325 |
326 | func multilineParamsListReturningSingleValue(
327 | p1 string,
328 | p2 string,
329 | ) error {
330 | println("body")
331 | return nil
332 | }
333 |
334 | func multilineParamsListReturningNamedMultiValues(
335 | p1 string,
336 | p2 string,
337 | ) (s string, err error) {
338 | println("body")
339 | return s, err
340 | }
341 |
342 | func multilineParamsListReturningMultiValues(
343 | p1 string,
344 | p2 string,
345 | ) (string, error) {
346 | println("body")
347 | return "", nil
348 | }
349 |
350 | func multilineParamsListReturningNamedMultiLineValuesList(
351 | p1 string,
352 | p2 string,
353 | ) (
354 | s string,
355 | err error,
356 | ) {
357 | println("body")
358 | return s, err
359 | }
360 |
361 | func multilineParamsListReturningMultiLineValues(
362 | p1 string,
363 | p2 string,
364 | ) (
365 | string,
366 | error,
367 | ) {
368 | println("body")
369 | return "", nil
370 | }
371 |
372 | func multilineParamsOneParamNoReturn(
373 | p1 string,
374 | ) {
375 | println("body")
376 | }
377 |
378 | func multilineParamsOneParamReturningNamedSingleValue(
379 | p1 string,
380 | ) (err error) {
381 | println("body")
382 | return err
383 | }
384 |
385 | func multilineParamsOneParamReturningSingleValue(
386 | p1 string,
387 | ) error {
388 | println("body")
389 | return nil
390 | }
391 |
392 | func multilineParamsOneParamReturningNamedMultiValues(
393 | p1 string,
394 | ) (s string, err error) {
395 | println("body")
396 | return s, err
397 | }
398 |
399 | func multilineParamsOneParamReturningMultiValues(
400 | p1 string,
401 | ) (string, error) {
402 | println("body")
403 | return "", nil
404 | }
405 |
406 | func multilineParamsOneParamReturningNamedMultiLineValuesList(
407 | p1 string,
408 | ) (
409 | s string,
410 | err error,
411 | ) {
412 | println("body")
413 | return s, err
414 | }
415 |
416 | func multilineParamsOneParamReturningMultiLineValues(
417 | p1 string,
418 | ) (
419 | string,
420 | error,
421 | ) {
422 | println("body")
423 | return "", nil
424 | }
425 |
426 | func multilineResults() (p1 string,
427 | p2 string,
428 | ) {
429 | println("body")
430 | }
431 |
432 | func multilineResultsUnambiguous() (p1 string,
433 | p2 string,
434 | ) {
435 | println("body")
436 | }
437 |
438 | func multilineNoFields() {
439 | println("body")
440 | }
441 |
442 | func f(
443 | foo int,
444 | bar string,
445 | /* baz */
446 | ) {
447 | body()
448 | }
449 |
450 | func f2(
451 | foo int,
452 | bar string,
453 | ) (
454 | string,
455 | error,
456 | /* baz */
457 | ) {
458 | return "", nil
459 | }
460 |
461 | func multilineResultsMultipleEmptyLines() (p1 string,
462 | p2 string,
463 | ) {
464 | println("body")
465 | }
466 |
467 | func multilineParamsWithoutEmptyLine(p1 string,
468 | p2 string,
469 | ) {
470 | println("body")
471 | }
472 |
473 | func multilineParamsWithoutEmptyLineWithComment(p1 string,
474 | p2 string,
475 | ) {
476 | // comment
477 | println("body")
478 | }
479 |
480 | // Same as the others above, but with a single result parameter without
481 | // parentheses. This used to cause token.File.Offset crashes.
482 | func f(p1 string,
483 | p2 string,
484 | ) int {
485 | println("body")
486 | return 0
487 | }
488 |
489 | func a() {
490 | f := func(s string,
491 | b bool,
492 | ) {
493 | // foo
494 | }
495 | }
496 |
497 | func f(p1 string,
498 | p2 string) (int, string,
499 |
500 | /* baz */) {
501 | println("body")
502 | return 0, ""
503 | }
504 |
--------------------------------------------------------------------------------
/testdata/script/generated.txtar:
--------------------------------------------------------------------------------
1 | # Explicitly given generated files are formatted with our rules.
2 | exec gofumpt foo.go
3 | cmp stdout foo.go.golden
4 |
5 | # stdin is still considered an explicit file.
6 | stdin foo.go
7 | exec gofumpt
8 | cmp stdout foo.go.golden
9 |
10 | # Implicitly walked generated files get formatted without the added rules.
11 | exec gofumpt -l .
12 | stdout -count=1 '^badgofmt.go$'
13 | ! stdout '^foo.go$'
14 | ! stderr .
15 |
16 | -- badgofmt.go --
17 | // Code generated by foo. DO NOT EDIT.
18 |
19 | package foo
20 |
21 | func f() {
22 | println("body")
23 | }
24 | -- foo.go --
25 | // foo is a package about bar.
26 |
27 | // Code generated by foo. DO NOT EDIT.
28 |
29 | package foo
30 |
31 | func f() {
32 |
33 | println("body")
34 |
35 | }
36 | -- foo.go.golden --
37 | // foo is a package about bar.
38 |
39 | // Code generated by foo. DO NOT EDIT.
40 |
41 | package foo
42 |
43 | func f() {
44 | println("body")
45 | }
46 |
--------------------------------------------------------------------------------
/testdata/script/gomod.txtar:
--------------------------------------------------------------------------------
1 | # Test various edge cases with go.mod files.
2 |
3 | exec gofumpt toolchain-stable/a.go
4 | stdout '//gofumpt:diagnose.* -lang=go1.21'
5 |
6 | exec gofumpt toolchain-unstable/a.go
7 | stdout '//gofumpt:diagnose.* -lang=go1.21'
8 |
9 | exec gofumpt missing-go-directive/a.go
10 | stdout '//gofumpt:diagnose.* -lang=go1.16'
11 |
12 | -- toolchain-stable/go.mod --
13 | module a
14 |
15 | go 1.21.2
16 | -- toolchain-stable/a.go --
17 | package a
18 |
19 | //gofumpt:diagnose
20 |
21 | -- toolchain-unstable/go.mod --
22 | module a
23 |
24 | go 1.21rc3
25 | -- toolchain-unstable/a.go --
26 | package a
27 |
28 | //gofumpt:diagnose
29 |
30 | -- missing-go-directive/go.mod --
31 | module a
32 |
33 | -- missing-go-directive/a.go --
34 | package a
35 |
36 | //gofumpt:diagnose
37 |
--------------------------------------------------------------------------------
/testdata/script/ignore-dirs.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt orig.go.golden
2 | cp stdout formatted.go.golden
3 | mkdir -p vendor/foo testdata/foo
4 | cp orig.go.golden vendor/foo/foo.go
5 | cp orig.go.golden testdata/foo/foo.go
6 |
7 | # format explicit dirs
8 | exec gofumpt -l vendor testdata
9 | stdout -count=1 'vendor[/\\]foo[/\\]foo.go'
10 | stdout -count=1 'testdata[/\\]foo[/\\]foo.go'
11 | ! stderr .
12 |
13 | # format explicit files
14 | exec gofumpt -l vendor/foo/foo.go testdata/foo/foo.go
15 | stdout -count=1 'vendor[/\\]foo[/\\]foo.go'
16 | stdout -count=1 'testdata[/\\]foo[/\\]foo.go'
17 | ! stderr .
18 |
19 | # ignore implicit dirs via fs walking
20 | exec gofumpt -l .
21 | ! stdout .
22 | ! stderr .
23 |
24 | # format explicit pkg while ignoring rest
25 | mkdir vendor/ignore testdata/ignore
26 | cp orig.go.golden vendor/ignore/ignore.go
27 | cp orig.go.golden testdata/ignore/ignore.go
28 | exec gofumpt -l vendor/foo testdata/foo .
29 | stdout -count=1 'vendor[/\\]foo[/\\]foo.go'
30 | stdout -count=1 'testdata[/\\]foo[/\\]foo.go'
31 | ! stderr .
32 |
33 | # format explicit dirs without clean paths
34 | exec gofumpt -l $WORK//vendor ./testdata/./
35 | stdout -count=1 'vendor[/\\]foo[/\\]foo.go'
36 | stdout -count=1 'testdata[/\\]foo[/\\]foo.go'
37 | ! stderr .
38 |
39 | -- orig.go.golden --
40 | package p
41 |
42 | func f() {
43 | if true {
44 | // lone comment
45 | }
46 | {
47 |
48 | }
49 |
50 | {
51 |
52 | // lone comment
53 |
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/testdata/script/interface.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | type i1 interface {
11 |
12 | a(x int) int
13 |
14 |
15 |
16 | b(x int) int
17 |
18 | c(x int) int
19 |
20 | D()
21 |
22 | E()
23 |
24 | f()
25 | }
26 |
27 | type i2 interface {
28 |
29 | // comment for a
30 | a(x int) int
31 |
32 | // comment between a and b
33 |
34 | // comment for b
35 | b(x int) int
36 |
37 | // comment between b and c
38 |
39 | c(x int) int
40 |
41 | d(x int) int
42 |
43 | // comment for e
44 | e(x int) int
45 |
46 | }
47 |
48 | type i3 interface {
49 | a(x int) int
50 |
51 | // standalone comment
52 |
53 | b(x int) int
54 | }
55 |
56 | type leadingLine1 interface {
57 |
58 |
59 | a(x int) int
60 | }
61 |
62 | type leadingLine2 interface {
63 |
64 | a(x int) int
65 | }
66 |
67 | type leadingLine3 interface {
68 |
69 | // comment
70 | a(x int) int
71 | }
72 |
73 | type leadingLine4 interface {
74 | // comment
75 |
76 | a(x int) int
77 | }
78 |
79 | type leadingLine5 interface {
80 | // comment
81 |
82 | // comment for a
83 | a(x int) int
84 | }
85 |
86 | type leadingLine6 interface {
87 |
88 | // comment
89 |
90 | // comment for a
91 | a(x int) int
92 | }
93 |
94 | type leadingLine7 interface {
95 |
96 |
97 | // comment
98 |
99 | // comment for a
100 | a(x int) int
101 | }
102 |
103 | type leadingLine8 interface {
104 | // comment
105 | }
106 |
107 | type ii1 interface {
108 | DoA()
109 | DoB()
110 |
111 | UndoA()
112 | UndoB()
113 | }
114 | -- foo.go.golden --
115 | package p
116 |
117 | type i1 interface {
118 | a(x int) int
119 |
120 | b(x int) int
121 |
122 | c(x int) int
123 |
124 | D()
125 |
126 | E()
127 |
128 | f()
129 | }
130 |
131 | type i2 interface {
132 | // comment for a
133 | a(x int) int
134 |
135 | // comment between a and b
136 |
137 | // comment for b
138 | b(x int) int
139 |
140 | // comment between b and c
141 |
142 | c(x int) int
143 |
144 | d(x int) int
145 |
146 | // comment for e
147 | e(x int) int
148 | }
149 |
150 | type i3 interface {
151 | a(x int) int
152 |
153 | // standalone comment
154 |
155 | b(x int) int
156 | }
157 |
158 | type leadingLine1 interface {
159 | a(x int) int
160 | }
161 |
162 | type leadingLine2 interface {
163 | a(x int) int
164 | }
165 |
166 | type leadingLine3 interface {
167 | // comment
168 | a(x int) int
169 | }
170 |
171 | type leadingLine4 interface {
172 | // comment
173 |
174 | a(x int) int
175 | }
176 |
177 | type leadingLine5 interface {
178 | // comment
179 |
180 | // comment for a
181 | a(x int) int
182 | }
183 |
184 | type leadingLine6 interface {
185 | // comment
186 |
187 | // comment for a
188 | a(x int) int
189 | }
190 |
191 | type leadingLine7 interface {
192 | // comment
193 |
194 | // comment for a
195 | a(x int) int
196 | }
197 |
198 | type leadingLine8 interface {
199 | // comment
200 | }
201 |
202 | type ii1 interface {
203 | DoA()
204 | DoB()
205 |
206 | UndoA()
207 | UndoB()
208 | }
209 |
--------------------------------------------------------------------------------
/testdata/script/linedirectives.txtar:
--------------------------------------------------------------------------------
1 | # Line directives can throw off our use of MergeLines.
2 | # We should ignore them entirely when calculating line numbers.
3 | # The file below is borrowed from Go's test/dwarf/linedirectives.go.
4 |
5 | exec gofumpt -w foo.go
6 | cmp foo.go foo.go.golden
7 |
8 | -- foo.go --
9 | // Copyright 2011 The Go Authors. All rights reserved.
10 | // Use of this source code is governed by a BSD-style
11 | // license that can be found in the LICENSE file.
12 |
13 | //line foo/bar.y:4
14 | package main
15 | //line foo/bar.y:60
16 | func main() {
17 | //line foo/bar.y:297
18 | f, l := 0, 0
19 | //line yacctab:1
20 | f, l = 1, 1
21 | //line yaccpar:1
22 | f, l = 2, 1
23 | //line foo/bar.y:82
24 | f, l = 3, 82
25 | //line foo/bar.y:90
26 | f, l = 3, 90
27 | //line foo/bar.y:92
28 | f, l = 3, 92
29 | //line foo/bar.y:100
30 | f, l = 3, 100
31 | //line foo/bar.y:104
32 | l = 104
33 | //line foo/bar.y:112
34 | l = 112
35 | //line foo/bar.y:117
36 | l = 117
37 | //line foo/bar.y:121
38 | l = 121
39 | //line foo/bar.y:125
40 | l = 125
41 | //line foo/bar.y:133
42 | l = 133
43 | //line foo/bar.y:146
44 | l = 146
45 | //line foo/bar.y:148
46 | //line foo/bar.y:153
47 | //line foo/bar.y:155
48 | l = 155
49 | //line foo/bar.y:160
50 |
51 | //line foo/bar.y:164
52 | //line foo/bar.y:173
53 |
54 | //line foo/bar.y:178
55 | //line foo/bar.y:180
56 | //line foo/bar.y:185
57 | //line foo/bar.y:195
58 | //line foo/bar.y:197
59 | //line foo/bar.y:202
60 | //line foo/bar.y:204
61 | //line foo/bar.y:208
62 | //line foo/bar.y:211
63 | //line foo/bar.y:213
64 | //line foo/bar.y:215
65 | //line foo/bar.y:217
66 | //line foo/bar.y:221
67 | //line foo/bar.y:229
68 | //line foo/bar.y:236
69 | //line foo/bar.y:238
70 | //line foo/bar.y:240
71 | //line foo/bar.y:244
72 | //line foo/bar.y:249
73 | //line foo/bar.y:253
74 | //line foo/bar.y:257
75 | //line foo/bar.y:262
76 | //line foo/bar.y:267
77 | //line foo/bar.y:272
78 | if l == f {
79 | //line foo/bar.y:277
80 | panic("aie!")
81 | //line foo/bar.y:281
82 | }
83 | //line foo/bar.y:285
84 | return
85 | //line foo/bar.y:288
86 | //line foo/bar.y:290
87 | }
88 | //line foo/bar.y:293
89 | //line foo/bar.y:295
90 | -- foo.go.golden --
91 | // Copyright 2011 The Go Authors. All rights reserved.
92 | // Use of this source code is governed by a BSD-style
93 | // license that can be found in the LICENSE file.
94 |
95 | //line foo/bar.y:4
96 | package main
97 |
98 | //line foo/bar.y:60
99 | func main() {
100 | //line foo/bar.y:297
101 | f, l := 0, 0
102 | //line yacctab:1
103 | f, l = 1, 1
104 | //line yaccpar:1
105 | f, l = 2, 1
106 | //line foo/bar.y:82
107 | f, l = 3, 82
108 | //line foo/bar.y:90
109 | f, l = 3, 90
110 | //line foo/bar.y:92
111 | f, l = 3, 92
112 | //line foo/bar.y:100
113 | f, l = 3, 100
114 | //line foo/bar.y:104
115 | l = 104
116 | //line foo/bar.y:112
117 | l = 112
118 | //line foo/bar.y:117
119 | l = 117
120 | //line foo/bar.y:121
121 | l = 121
122 | //line foo/bar.y:125
123 | l = 125
124 | //line foo/bar.y:133
125 | l = 133
126 | //line foo/bar.y:146
127 | l = 146
128 | //line foo/bar.y:148
129 | //line foo/bar.y:153
130 | //line foo/bar.y:155
131 | l = 155
132 | //line foo/bar.y:160
133 |
134 | //line foo/bar.y:164
135 | //line foo/bar.y:173
136 |
137 | //line foo/bar.y:178
138 | //line foo/bar.y:180
139 | //line foo/bar.y:185
140 | //line foo/bar.y:195
141 | //line foo/bar.y:197
142 | //line foo/bar.y:202
143 | //line foo/bar.y:204
144 | //line foo/bar.y:208
145 | //line foo/bar.y:211
146 | //line foo/bar.y:213
147 | //line foo/bar.y:215
148 | //line foo/bar.y:217
149 | //line foo/bar.y:221
150 | //line foo/bar.y:229
151 | //line foo/bar.y:236
152 | //line foo/bar.y:238
153 | //line foo/bar.y:240
154 | //line foo/bar.y:244
155 | //line foo/bar.y:249
156 | //line foo/bar.y:253
157 | //line foo/bar.y:257
158 | //line foo/bar.y:262
159 | //line foo/bar.y:267
160 | //line foo/bar.y:272
161 | if l == f {
162 | //line foo/bar.y:277
163 | panic("aie!")
164 | //line foo/bar.y:281
165 | }
166 | //line foo/bar.y:285
167 | return
168 | //line foo/bar.y:288
169 | //line foo/bar.y:290
170 | }
171 |
172 | //line foo/bar.y:293
173 | //line foo/bar.y:295
174 |
--------------------------------------------------------------------------------
/testdata/script/long-lines.txtar:
--------------------------------------------------------------------------------
1 | cp foo.go foo.go.orig
2 |
3 | exec gofumpt -w foo.go
4 | cmp foo.go foo.go.orig
5 |
6 | env GOFUMPT_SPLIT_LONG_LINES=on
7 | exec gofumpt -w foo.go
8 | cmp foo.go foo.go.golden
9 |
10 | exec gofumpt -d foo.go.golden
11 | ! stdout .
12 |
13 | -- foo.go --
14 | package p
15 |
16 | func _() {
17 | if err := f(argument1, argument2, argument3, argument4, argument5, argument6, argument7, argument8, argument9, argument10); err != nil {
18 | panic(err)
19 | }
20 |
21 | // Tiny arguments to ensure the length calculation is right.
22 | if err := f(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); err != nil {
23 | panic(err)
24 | }
25 |
26 | // These wouldn't take significantly less horizontal space if split.
27 | f(x, "one single very very very very very very very very very very very very very very very very long literal")
28 | if err := f(x, "one single very very very very very very very very very very very very very very very very long literal"); err != nil {
29 | panic(err)
30 | }
31 | {
32 | {
33 | {
34 | {
35 | println("first", "one single very very very very very very very very very very very very very long literal")
36 | }
37 | }
38 | }
39 | }
40 |
41 | // Allow splitting at the start of sub-lists too.
42 | if err := f(argument1, argument2, argument3, argument4, someComplex{argument5, argument6, argument7, argument8, argument9, argument10}); err != nil {
43 | panic(err)
44 | }
45 | if err := f(argument1, argument2, argument3, argument4, &someComplex{argument5, argument6, argument7, argument8, argument9, argument10}); err != nil {
46 | panic(err)
47 | }
48 | if err := f(argument1, argument2, argument3, argument4, []someSlice{argument5, argument6, argument7, argument8, argument9, argument10}); err != nil {
49 | panic(err)
50 | }
51 |
52 | // Allow splitting "lists" of binary expressions.
53 | if boolean1 && boolean2 && boolean3 && boolean4 && boolean5 && boolean6 && boolean7 && boolean8 && boolean9 && boolean10 && boolean11 {
54 | }
55 | // Over 100, and we split in a way that doesn't break "len(" off.
56 | if boolean1 || boolean2 || boolean3 || boolean4 || len(someVeryLongVarName.SomeVeryLongSelector) > 0 {
57 | }
58 | }
59 |
60 | // Note that function declarations have a higher limit of 120.
61 |
62 | // This line goes beyond the limit of 120, but splitting it would leave the
63 | // following line with just 20 non-indentation characters. Not worth it.
64 | func LongButNotWorthSplitting(argument1, argument2, argument3, argument4, argument5, argument6, argument7, argument8, argument9 int) bool {
65 | }
66 |
67 | // This line goes well past the limit and it should be split.
68 | // Note that it has a nested func type in a parameter.
69 | func TooLongWithFuncParam(fn func(int) (int, error), argument1, argument2, argument3, argument4, argument5, argument6, argument7, argument8, argument9, argument10 int) bool {
70 | }
71 |
72 | // This is like LongButNotWorthSplitting, but with a func parameter.
73 | func LongButNotWorthSplitting2(fn func(int) (int, error), argument3, argument4, argument5, argument6, argument7, argument8, argument9 int) bool {
74 | }
75 |
76 | // Never split result parameter lists, as that could easily add confusion with
77 | // extra input parameters.
78 | func NeverSplitResults(argument1, argument2, argument3, argument4, argument5 int) (result1 int, result2, result3, result4, result5, result6, result7, result8 bool) {
79 | }
80 | -- foo.go.golden --
81 | package p
82 |
83 | func _() {
84 | if err := f(argument1, argument2, argument3, argument4, argument5, argument6, argument7,
85 | argument8, argument9, argument10); err != nil {
86 | panic(err)
87 | }
88 |
89 | // Tiny arguments to ensure the length calculation is right.
90 | if err := f(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, 0,
91 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); err != nil {
92 | panic(err)
93 | }
94 |
95 | // These wouldn't take significantly less horizontal space if split.
96 | f(x, "one single very very very very very very very very very very very very very very very very long literal")
97 | if err := f(x, "one single very very very very very very very very very very very very very very very very long literal"); err != nil {
98 | panic(err)
99 | }
100 | {
101 | {
102 | {
103 | {
104 | println("first", "one single very very very very very very very very very very very very very long literal")
105 | }
106 | }
107 | }
108 | }
109 |
110 | // Allow splitting at the start of sub-lists too.
111 | if err := f(argument1, argument2, argument3, argument4, someComplex{
112 | argument5, argument6, argument7, argument8, argument9, argument10,
113 | }); err != nil {
114 | panic(err)
115 | }
116 | if err := f(argument1, argument2, argument3, argument4, &someComplex{
117 | argument5, argument6, argument7, argument8, argument9, argument10,
118 | }); err != nil {
119 | panic(err)
120 | }
121 | if err := f(argument1, argument2, argument3, argument4, []someSlice{
122 | argument5, argument6, argument7, argument8, argument9, argument10,
123 | }); err != nil {
124 | panic(err)
125 | }
126 |
127 | // Allow splitting "lists" of binary expressions.
128 | if boolean1 && boolean2 && boolean3 && boolean4 && boolean5 && boolean6 && boolean7 &&
129 | boolean8 && boolean9 && boolean10 && boolean11 {
130 | }
131 | // Over 100, and we split in a way that doesn't break "len(" off.
132 | if boolean1 || boolean2 || boolean3 || boolean4 ||
133 | len(someVeryLongVarName.SomeVeryLongSelector) > 0 {
134 | }
135 | }
136 |
137 | // Note that function declarations have a higher limit of 120.
138 |
139 | // This line goes beyond the limit of 120, but splitting it would leave the
140 | // following line with just 20 non-indentation characters. Not worth it.
141 | func LongButNotWorthSplitting(argument1, argument2, argument3, argument4, argument5, argument6, argument7, argument8, argument9 int) bool {
142 | }
143 |
144 | // This line goes well past the limit and it should be split.
145 | // Note that it has a nested func type in a parameter.
146 | func TooLongWithFuncParam(fn func(int) (int, error), argument1, argument2, argument3, argument4,
147 | argument5, argument6, argument7, argument8, argument9, argument10 int) bool {
148 | }
149 |
150 | // This is like LongButNotWorthSplitting, but with a func parameter.
151 | func LongButNotWorthSplitting2(fn func(int) (int, error), argument3, argument4, argument5, argument6, argument7, argument8, argument9 int) bool {
152 | }
153 |
154 | // Never split result parameter lists, as that could easily add confusion with
155 | // extra input parameters.
156 | func NeverSplitResults(argument1, argument2, argument3, argument4, argument5 int) (result1 int, result2, result3, result4, result5, result6, result7, result8 bool) {
157 | }
158 |
--------------------------------------------------------------------------------
/testdata/script/missing-import.txtar:
--------------------------------------------------------------------------------
1 | # A missing import shouldn't matter nor be fixed by gofumpt.
2 | exec gofumpt foo.go
3 | cmp stdout foo.go
4 |
5 | -- foo.go --
6 | package p
7 |
8 | var _ bytes.Buffer
9 |
--------------------------------------------------------------------------------
/testdata/script/newline-errcheck.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | var Do1 func() error
11 |
12 | var Do2 func() (int, error)
13 |
14 | func f() {
15 | n1, err := Do2()
16 | if err != nil {
17 | panic(err)
18 | }
19 |
20 | if n2, err := Do2(); err != nil {
21 | panic(err)
22 | }
23 |
24 | n3, err := Do2()
25 |
26 | if err != nil {
27 | panic(err)
28 | }
29 |
30 | select {
31 | default:
32 | err := Do1()
33 |
34 | if err != nil {
35 | panic(err)
36 | }
37 | }
38 |
39 | n4, err := Do2()
40 |
41 | if err != nil && err.Error() == "complex condition" {
42 | panic(err)
43 | }
44 |
45 | err1 := Do1()
46 |
47 | if err != nil {
48 | panic(err)
49 | }
50 |
51 | {
52 | if err != nil {
53 | panic(err)
54 | }
55 | }
56 |
57 | n5, err = Do2()
58 |
59 | if err != nil {
60 | panic(err)
61 | }
62 | }
63 | -- foo.go.golden --
64 | package p
65 |
66 | var Do1 func() error
67 |
68 | var Do2 func() (int, error)
69 |
70 | func f() {
71 | n1, err := Do2()
72 | if err != nil {
73 | panic(err)
74 | }
75 |
76 | if n2, err := Do2(); err != nil {
77 | panic(err)
78 | }
79 |
80 | n3, err := Do2()
81 | if err != nil {
82 | panic(err)
83 | }
84 |
85 | select {
86 | default:
87 | err := Do1()
88 | if err != nil {
89 | panic(err)
90 | }
91 | }
92 |
93 | n4, err := Do2()
94 |
95 | if err != nil && err.Error() == "complex condition" {
96 | panic(err)
97 | }
98 |
99 | err1 := Do1()
100 |
101 | if err != nil {
102 | panic(err)
103 | }
104 |
105 | {
106 | if err != nil {
107 | panic(err)
108 | }
109 | }
110 |
111 | n5, err = Do2()
112 | if err != nil {
113 | panic(err)
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/testdata/script/octal-literals.txtar:
--------------------------------------------------------------------------------
1 | # Initially, the Go language version is too low.
2 | exec gofumpt -l .
3 | ! stdout .
4 |
5 | # We can give an explicitly newer version.
6 | exec gofumpt -lang=go1.13 -l .
7 | stdout -count=1 'foo\.go'
8 | stdout -count=1 'nested[/\\]nested\.go'
9 |
10 | # If we bump the version in go.mod, it should be picked up.
11 | exec go mod edit -go=1.13
12 | exec gofumpt -l .
13 | stdout -count=1 'foo\.go'
14 | ! stdout 'nested'
15 |
16 | # Ensure we produce the output we expect, and that it's stable.
17 | exec gofumpt foo.go
18 | cmp stdout foo.go.golden
19 | exec gofumpt -d foo.go.golden
20 | ! stdout .
21 |
22 | # We can give an explicitly older version, too
23 | exec gofumpt -lang=go1.0 -l .
24 | ! stdout .
25 |
26 | -- go.mod --
27 | module test
28 |
29 | go 1.12
30 | -- foo.go --
31 | package p
32 |
33 | const (
34 | i = 0
35 | j = 022
36 | k = 0o_7_5_5
37 | l = 1022
38 | )
39 | -- foo.go.golden --
40 | package p
41 |
42 | const (
43 | i = 0
44 | j = 0o22
45 | k = 0o_7_5_5
46 | l = 1022
47 | )
48 | -- nested/go.mod --
49 | module nested
50 |
51 | go 1.11
52 | -- nested/nested.go --
53 | package p
54 |
55 | const (
56 | i = 0
57 | j = 022
58 | k = 0o_7_5_5
59 | l = 1022
60 | )
61 |
--------------------------------------------------------------------------------
/testdata/script/short-case.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | func f(r rune) {
11 | switch r {
12 | case 'a',
13 | 'b',
14 | 'c':
15 |
16 | case 'd', 'e', 'f':
17 |
18 | case 'a', 'b',
19 | 'c':
20 |
21 | case 'v', 'e', 'r', 'y', 'l', 'o', 'n', 'g',
22 | 'l', 'i', 's', 't', '.', '.', '.':
23 |
24 | // before
25 | case 'a',
26 | 'b': // inline
27 | // after
28 |
29 | case 'a', // middle
30 | 'b':
31 |
32 | case 'a', 'b', 'c', 'd', 'e', 'f',
33 | 'g': // very very long inline comment at the end
34 |
35 | case 'a', 'b', 'c',
36 | 'd': // short comment
37 | }
38 | {
39 | {
40 | {
41 | {
42 | {
43 | switch r {
44 | case 'i', 'n', 'd', 'e',
45 | 'n', 't', 'e', 'd':
46 | }
47 | }
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
54 | func s(x int) {
55 | switch x {
56 | case
57 | shortConstant1,
58 | shortConstant2:
59 | // A comment.
60 | fmt.Println(x)
61 | case
62 | shortConstant3,
63 | shortConstant4:
64 | // Do nothing.
65 | default:
66 | // Another comment.
67 | fmt.Println(x * 2)
68 | }
69 | }
70 |
71 | func s(x int) {
72 | switch x {
73 | case
74 | longerConstantName1,
75 | longerConstantName2:
76 | // A comment.
77 | fmt.Println(x)
78 | case
79 | longerConstantName3,
80 | longerConstantName4:
81 | // Do nothing.
82 | default:
83 | // Another comment.
84 | fmt.Println(x * 2)
85 | }
86 | }
87 | -- foo.go.golden --
88 | package p
89 |
90 | func f(r rune) {
91 | switch r {
92 | case 'a', 'b', 'c':
93 |
94 | case 'd', 'e', 'f':
95 |
96 | case 'a', 'b', 'c':
97 |
98 | case 'v', 'e', 'r', 'y', 'l', 'o', 'n', 'g',
99 | 'l', 'i', 's', 't', '.', '.', '.':
100 |
101 | // before
102 | case 'a', 'b': // inline
103 | // after
104 |
105 | case 'a', // middle
106 | 'b':
107 |
108 | case 'a', 'b', 'c', 'd', 'e', 'f',
109 | 'g': // very very long inline comment at the end
110 |
111 | case 'a', 'b', 'c', 'd': // short comment
112 | }
113 | {
114 | {
115 | {
116 | {
117 | {
118 | switch r {
119 | case 'i', 'n', 'd', 'e',
120 | 'n', 't', 'e', 'd':
121 | }
122 | }
123 | }
124 | }
125 | }
126 | }
127 | }
128 |
129 | func s(x int) {
130 | switch x {
131 | case shortConstant1, shortConstant2:
132 | // A comment.
133 | fmt.Println(x)
134 | case shortConstant3, shortConstant4:
135 | // Do nothing.
136 | default:
137 | // Another comment.
138 | fmt.Println(x * 2)
139 | }
140 | }
141 |
142 | func s(x int) {
143 | switch x {
144 | case
145 | longerConstantName1,
146 | longerConstantName2:
147 | // A comment.
148 | fmt.Println(x)
149 | case
150 | longerConstantName3,
151 | longerConstantName4:
152 | // Do nothing.
153 | default:
154 | // Another comment.
155 | fmt.Println(x * 2)
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/testdata/script/short-decl.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- foo.go --
8 | package p
9 |
10 | var global = x
11 |
12 | func f() {
13 | var local = x
14 | var local2, local3 = x, y
15 |
16 | var onlyType T
17 |
18 | var typeAndVar T = x
19 |
20 | var _ = unused
21 |
22 | var (
23 | aligned = x
24 | vars = y
25 | here = y
26 | )
27 | }
28 | -- foo.go.golden --
29 | package p
30 |
31 | var global = x
32 |
33 | func f() {
34 | local := x
35 | local2, local3 := x, y
36 |
37 | var onlyType T
38 |
39 | var typeAndVar T = x
40 |
41 | _ = unused
42 |
43 | var (
44 | aligned = x
45 | vars = y
46 | here = y
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/testdata/script/simplify.txtar:
--------------------------------------------------------------------------------
1 | # gofumpt changes -s to default to true.
2 | exec gofumpt foo.go
3 | cmp stdout foo.go.golden
4 |
5 | -- foo.go --
6 | package p
7 |
8 | const ()
9 |
10 | const (
11 | // Comment
12 | )
13 |
14 | type ()
15 |
16 | type (
17 | // Comment
18 | )
19 |
20 | var ()
21 |
22 | var (
23 | // Comment
24 | )
25 |
26 | var _ = [][]int{[]int{1}}
27 | -- foo.go.golden --
28 | package p
29 |
30 | const (
31 | // Comment
32 | )
33 |
34 | type (
35 | // Comment
36 | )
37 |
38 | var (
39 | // Comment
40 | )
41 |
42 | var _ = [][]int{{1}}
43 |
--------------------------------------------------------------------------------
/testdata/script/std-imports.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt -w foo.go
2 | cmp foo.go foo.go.golden
3 |
4 | exec gofumpt -d foo.go.golden
5 | ! stdout .
6 |
7 | -- go.mod --
8 | module nodomainmod/mod1
9 |
10 | go 1.16
11 | -- foo.go --
12 | package p
13 |
14 | import (
15 | "io"
16 | "io/ioutil" // if the user keeps them in the top group, obey that
17 | _ "io/ioutil"
18 |
19 | _ "image/png"
20 |
21 | "bufio" // the above is for a side effect; this one has a comment
22 | )
23 |
24 | import (
25 | "os"
26 |
27 | "foo.local/one"
28 |
29 | bytes_ "bytes"
30 |
31 | "io"
32 | )
33 |
34 | import (
35 | "foo.local/two"
36 |
37 | "fmt"
38 | )
39 |
40 | // If they are in order, but with extra newlines, join them.
41 | import (
42 | "more"
43 |
44 | "std"
45 | )
46 |
47 | // We need to split std vs non-std in this case too.
48 | import (
49 | "foo.local"
50 | "foo.local/three"
51 | math "math"
52 | )
53 |
54 | import (
55 | "x"
56 | // don't mess up this comment
57 | "y"
58 | // or many
59 | // of them
60 | "z"
61 | )
62 |
63 | // This used to crash gofumpt, as there's no space to insert an extra newline.
64 | import (
65 | "std"
66 | "non.std/pkg"
67 | )
68 |
69 | // All of the extra imports below are known to not belong in std.
70 | // For example/ and test/, see https://golang.org/issue/37641.
71 | import (
72 | "io"
73 |
74 | "example/foo"
75 | "internal/bar"
76 | "test/baz"
77 | )
78 |
79 | import (
80 | "io"
81 |
82 | "nodomainmod"
83 | "nodomainmod/mod1/pkg1"
84 | "nodomainmod/mod2"
85 | "nodomainmodextra"
86 | )
87 |
88 | import (
89 | "io"
90 |
91 | "nodomainother/mod.withdot/pkg1"
92 | )
93 |
94 | // TODO: fix issue 225.
95 | import (
96 | "path/filepath"
97 | "time"
98 | "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1"
99 | "k8s.io/apimachinery/pkg/types"
100 | "sigs.k8s.io/yaml"
101 | )
102 | -- foo.go.golden --
103 | package p
104 |
105 | import (
106 | "io"
107 | "io/ioutil" // if the user keeps them in the top group, obey that
108 | _ "io/ioutil"
109 |
110 | _ "image/png"
111 |
112 | "bufio" // the above is for a side effect; this one has a comment
113 | )
114 |
115 | import (
116 | "io"
117 | "os"
118 |
119 | "foo.local/one"
120 |
121 | bytes_ "bytes"
122 | )
123 |
124 | import (
125 | "fmt"
126 |
127 | "foo.local/two"
128 | )
129 |
130 | // If they are in order, but with extra newlines, join them.
131 | import (
132 | "more"
133 | "std"
134 | )
135 |
136 | // We need to split std vs non-std in this case too.
137 | import (
138 | math "math"
139 |
140 | "foo.local"
141 | "foo.local/three"
142 | )
143 |
144 | import (
145 | "x"
146 | // don't mess up this comment
147 | "y"
148 | // or many
149 | // of them
150 | "z"
151 | )
152 |
153 | // This used to crash gofumpt, as there's no space to insert an extra newline.
154 | import (
155 | "std"
156 |
157 | "non.std/pkg"
158 | )
159 |
160 | // All of the extra imports below are known to not belong in std.
161 | // For example/ and test/, see https://golang.org/issue/37641.
162 | import (
163 | "internal/bar"
164 | "io"
165 |
166 | "example/foo"
167 |
168 | "test/baz"
169 | )
170 |
171 | import (
172 | "io"
173 | "nodomainmodextra"
174 |
175 | "nodomainmod"
176 | "nodomainmod/mod1/pkg1"
177 | "nodomainmod/mod2"
178 | )
179 |
180 | import (
181 | "io"
182 | "nodomainother/mod.withdot/pkg1"
183 | )
184 |
185 | // TODO: fix issue 225.
186 | import (
187 | "path/filepath"
188 | "time"
189 |
190 | "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1"
191 | "k8s.io/apimachinery/pkg/types"
192 |
193 | "sigs.k8s.io/yaml"
194 | )
195 |
--------------------------------------------------------------------------------
/testdata/script/typeparams.txtar:
--------------------------------------------------------------------------------
1 | exec gofumpt foo.go
2 | cmp stdout foo.go.golden
3 |
4 | -- go.mod --
5 | module test
6 |
7 | go 1.18
8 | -- foo.go --
9 | package p
10 |
11 | func Foo[A, B any](x A, y B) {}
12 |
13 | type Vector[T any] []T
14 |
15 | var v Vector[int ]
16 |
17 | type PredeclaredSignedInteger interface {
18 | int | int8 | int16 | int32 | int64
19 | }
20 |
21 | type StringableSignedInteger interface {
22 |
23 | ~int | ~int8 | ~int16 | ~int32 | ~int64
24 |
25 | String() string
26 |
27 | }
28 |
29 | type CombineEmbeds interface {
30 | fmt.Stringer
31 | comparable | io.Reader
32 |
33 | Foo()
34 | }
35 |
36 | func Caller() {
37 | Foo[int,int](1,2)
38 | }
39 |
40 | func Issue235[K interface {
41 | comparable
42 | constraints.Ordered
43 | }, V any](m map[K]V) []K {
44 | keys := maps.Keys(m)
45 | slices.Sort(keys)
46 | return keys
47 | }
48 |
49 | func multilineParams[V any](p1 V,
50 | p2 V) {
51 |
52 | println("body")
53 |
54 | }
55 | -- foo.go.golden --
56 | package p
57 |
58 | func Foo[A, B any](x A, y B) {}
59 |
60 | type Vector[T any] []T
61 |
62 | var v Vector[int]
63 |
64 | type PredeclaredSignedInteger interface {
65 | int | int8 | int16 | int32 | int64
66 | }
67 |
68 | type StringableSignedInteger interface {
69 | ~int | ~int8 | ~int16 | ~int32 | ~int64
70 |
71 | String() string
72 | }
73 |
74 | type CombineEmbeds interface {
75 | fmt.Stringer
76 | comparable | io.Reader
77 |
78 | Foo()
79 | }
80 |
81 | func Caller() {
82 | Foo[int, int](1, 2)
83 | }
84 |
85 | func Issue235[K interface {
86 | comparable
87 | constraints.Ordered
88 | }, V any](m map[K]V) []K {
89 | keys := maps.Keys(m)
90 | slices.Sort(keys)
91 | return keys
92 | }
93 |
94 | func multilineParams[V any](p1 V,
95 | p2 V,
96 | ) {
97 | println("body")
98 | }
99 |
--------------------------------------------------------------------------------
/testdata/script/workspaces.txtar:
--------------------------------------------------------------------------------
1 | # Whether we run gofumpt from inside or outside a module,
2 | # we should always use the information from its go.mod.
3 | # We also test that we don't get confused by the presence of go.work.
4 |
5 | exec gofumpt a/go112.go
6 | cmp stdout a/go113.go
7 |
8 | cd a
9 | exec gofumpt go112.go
10 | cmp stdout go113.go
11 |
12 | -- go.work --
13 | go 1.18
14 | use ./a
15 | use ./b
16 | -- a/go.mod --
17 | module a
18 | go 1.18
19 | -- a/a.go --
20 | package a
21 | -- a/go112.go --
22 | package main
23 |
24 | const x = 0777
25 | -- a/go113.go --
26 | package main
27 |
28 | const x = 0o777
29 | -- b/go.mod --
30 | module b
31 | go 1.18
32 | -- b/b.go --
33 | package b
34 |
--------------------------------------------------------------------------------
/ulimit_linux_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019, Daniel Martí
2 | // See LICENSE for licensing information
3 |
4 | package main
5 |
6 | import (
7 | "bytes"
8 | "fmt"
9 | "os"
10 | "os/exec"
11 | "path/filepath"
12 | "strconv"
13 | "testing"
14 |
15 | "github.com/go-quicktest/qt"
16 | "golang.org/x/sys/unix"
17 | )
18 |
19 | func init() {
20 | // Here rather than in TestMain, to reuse the unix build tag.
21 | if limit := os.Getenv("TEST_WITH_FILE_LIMIT"); limit != "" {
22 | n, err := strconv.ParseUint(limit, 10, 64)
23 | if err != nil {
24 | panic(err)
25 | }
26 | rlimit := unix.Rlimit{Cur: n, Max: n}
27 | if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil {
28 | panic(err)
29 | }
30 | main()
31 | }
32 | }
33 |
34 | func TestWithLowOpenFileLimit(t *testing.T) {
35 | if testing.Short() {
36 | t.Skip("this test creates thousands of files")
37 | }
38 | // Safe to run in parallel, as we only change the limit for child processes.
39 | t.Parallel()
40 |
41 | tempDir := t.TempDir()
42 | testBinary, err := os.Executable()
43 | qt.Assert(t, qt.IsNil(err))
44 |
45 | const (
46 | // Enough directories to run into the ulimit.
47 | // Enough number of files in total to run into the ulimit.
48 | numberDirs = 500
49 | numberFilesPerDir = 20
50 | numberFilesTotal = numberDirs * numberFilesPerDir
51 | )
52 | t.Logf("writing %d tiny Go files", numberFilesTotal)
53 | var allGoFiles []string
54 | for i := range numberDirs {
55 | // Prefix "p", so the package name is a valid identifier.
56 | // Add one go.mod file per directory as well,
57 | // which will help catch data races when loading module info.
58 | dirName := fmt.Sprintf("p%03d", i)
59 | dirPath := filepath.Join(tempDir, dirName)
60 | err := os.MkdirAll(dirPath, 0o777)
61 | qt.Assert(t, qt.IsNil(err))
62 |
63 | err = os.WriteFile(filepath.Join(dirPath, "go.mod"),
64 | fmt.Appendf(nil, "module %s\n\ngo 1.16", dirName), 0o666)
65 | qt.Assert(t, qt.IsNil(err))
66 |
67 | for j := range numberFilesPerDir {
68 | filePath := filepath.Join(dirPath, fmt.Sprintf("%03d.go", j))
69 | err := os.WriteFile(filePath,
70 | // Extra newlines so that "-l" prints all paths.
71 | fmt.Appendf(nil, "package %s\n\n\n", dirName), 0o666)
72 | qt.Assert(t, qt.IsNil(err))
73 | allGoFiles = append(allGoFiles, filePath)
74 | }
75 | }
76 | if len(allGoFiles) != numberFilesTotal {
77 | panic("allGoFiles doesn't have the expected number of files?")
78 | }
79 | runGofmt := func(paths ...string) {
80 | t.Logf("running with %d paths", len(paths))
81 | cmd := exec.Command(testBinary, append([]string{"-l"}, paths...)...)
82 | // 256 is a relatively common low limit, e.g. on Mac.
83 | cmd.Env = append(os.Environ(), "TEST_WITH_FILE_LIMIT=256")
84 | out, err := cmd.Output()
85 | var stderr []byte
86 | if err, _ := err.(*exec.ExitError); err != nil {
87 | stderr = err.Stderr
88 | }
89 | qt.Assert(t, qt.IsNil(err), qt.Commentf("stderr:\n%s", stderr))
90 | qt.Assert(t, qt.Equals(bytes.Count(out, []byte("\n")), len(allGoFiles)))
91 | }
92 | runGofmt(tempDir)
93 | runGofmt(allGoFiles...)
94 | }
95 |
--------------------------------------------------------------------------------