From 77ffbfe3079f9a90f4206cee7dc0a65094a898c0 Mon Sep 17 00:00:00 2001 From: karitham Date: Fri, 7 Aug 2026 23:04:55 +0200 Subject: [PATCH] treewide: format --- doc/doc.go | 16 ++++++++++++---- doc/print.go | 6 ++++-- formatter/bench_test.go | 3 +++ formatter/body.go | 5 +++++ formatter/comments.go | 3 +++ formatter/field.go | 2 ++ formatter/format.go | 17 +++++++---------- formatter/value.go | 3 +++ lsp/cache/cow_test.go | 3 ++- lsp/cache/file.go | 9 +++------ lsp/cache/parse.go | 5 ++--- lsp/cache/session_test.go | 2 ++ lsp/cache/view.go | 7 ++++--- lsp/codejump/utils.go | 1 + lsp/completion/token_completion.go | 2 ++ lsp/format_range_fuzz_test.go | 1 + lsp/format_range_test.go | 2 ++ lsp/links/links_test.go | 1 + lsp/semantic/semantic.go | 2 ++ lsp/semantic/semantic_test.go | 2 ++ lsp/symbols/workspace_test.go | 4 ++++ syntax/bench_test.go | 3 +++ syntax/lexer.go | 4 ++++ 23 files changed, 74 insertions(+), 29 deletions(-) diff --git a/doc/doc.go b/doc/doc.go index eedf492..839bf39 100644 --- a/doc/doc.go +++ b/doc/doc.go @@ -105,6 +105,7 @@ func (a *Arena) Reset() { // Text returns a doc for literal output, allocated from the arena. func (a *Arena) Text(s string) Doc { a.texts = append(a.texts, textNode{s: s}) + return &a.texts[len(a.texts)-1] } @@ -113,6 +114,7 @@ func (a *Arena) Text(s string) Doc { // be reused by the caller. func (a *Arena) Concat(parts ...Doc) Doc { a.concats = append(a.concats, concatNode{parts: parts}) + return &a.concats[len(a.concats)-1] } @@ -142,10 +144,7 @@ func (a *Arena) Parts(capacity int) []Doc { // Grow the single region geometrically. The old region stays // reachable from slices handed out earlier, so it must not be // reused; the copy is amortized. - n := len(a.parts) * 2 - if n < a.partsLen+capacity { - n = a.partsLen + capacity - } + n := max(len(a.parts)*2, a.partsLen+capacity) grown := make([]Doc, n) copy(grown, a.parts[:a.partsLen]) @@ -161,24 +160,28 @@ func (a *Arena) Parts(capacity int) []Doc { // Group wraps d in a group allocated from the arena. func (a *Arena) Group(d Doc) Doc { a.groups = append(a.groups, group{doc: d}) + return &a.groups[len(a.groups)-1] } // GroupBreak wraps d in a group that always breaks, from the arena. func (a *Arena) GroupBreak(d Doc) Doc { a.groups = append(a.groups, group{doc: d, brk: true}) + return &a.groups[len(a.groups)-1] } // GroupID wraps d in a group with an ID, from the arena. func (a *Arena) GroupID(id int, d Doc) Doc { a.groups = append(a.groups, group{doc: d, id: id}) + return &a.groups[len(a.groups)-1] } // ConditionalGroup tries each state in order, from the arena. func (a *Arena) ConditionalGroup(id int, states ...Doc) Doc { a.groups = append(a.groups, group{doc: states[0], id: id, expanded: states}) + return &a.groups[len(a.groups)-1] } @@ -186,6 +189,7 @@ func (a *Arena) ConditionalGroup(id int, states ...Doc) Doc { // arena. func (a *Arena) IfBreak(broken, flat Doc) Doc { a.ifs = append(a.ifs, ifBreak{breakDoc: broken, flatDoc: flat}) + return &a.ifs[len(a.ifs)-1] } @@ -193,6 +197,7 @@ func (a *Arena) IfBreak(broken, flat Doc) Doc { // from the arena. func (a *Arena) IfBreakFor(broken, flat Doc, groupID int) Doc { a.ifs = append(a.ifs, ifBreak{breakDoc: broken, flatDoc: flat, groupID: groupID}) + return &a.ifs[len(a.ifs)-1] } @@ -200,12 +205,14 @@ func (a *Arena) IfBreakFor(broken, flat Doc, groupID int) Doc { // the arena. func (a *Arena) Indent(d Doc) Doc { a.indents = append(a.indents, indent{doc: d}) + return &a.indents[len(a.indents)-1] } // Align indents its contents by n columns, from the arena. func (a *Arena) Align(n int, d Doc) Doc { a.aligns = append(a.aligns, align{n: n, doc: d}) + return &a.aligns[len(a.aligns)-1] } @@ -213,6 +220,7 @@ func (a *Arena) Align(n int, d Doc) Doc { // the arena. func (a *Arena) LineSuffix(d Doc) Doc { a.suffixes = append(a.suffixes, lineSuffix{doc: d}) + return &a.suffixes[len(a.suffixes)-1] } diff --git a/doc/print.go b/doc/print.go index 572f15b..f9bfa22 100644 --- a/doc/print.go +++ b/doc/print.go @@ -136,12 +136,14 @@ func (p *printer) reset(o Options) { p.o = o p.position = 0 + p.out = p.out[:0] if p.groupMode == nil { p.groupMode = map[int]mode{} } else { clear(p.groupMode) } + p.lineSuffix = p.lineSuffix[:0] p.shouldRemeasure = false p.lastLineComment = false @@ -174,8 +176,8 @@ func (p *printer) write(s string) { // lineEnded reports whether the output already ends with a newline // (ignoring trailing spaces and tabs, i.e. indentation). func (p *printer) lineEnded() bool { - for i := len(p.out) - 1; i >= 0; i-- { - switch p.out[i] { + for _, v := range slices.Backward(p.out) { + switch v { case ' ', '\t': continue case '\n': diff --git a/formatter/bench_test.go b/formatter/bench_test.go index 4902900..f85b812 100644 --- a/formatter/bench_test.go +++ b/formatter/bench_test.go @@ -12,16 +12,19 @@ var benchSrc = func() []byte { if err != nil { panic(err) } + return b }() func BenchmarkParseFormat(b *testing.B) { b.ReportAllocs() + for i := 0; i < b.N; i++ { doc, errs := syntax.Parse(benchSrc) if len(errs) > 0 { b.Fatal(errs) } + if _, err := Format(doc, Options{}); err != nil { b.Fatal(err) } diff --git a/formatter/body.go b/formatter/body.go index 9dca428..01df76a 100644 --- a/formatter/body.go +++ b/formatter/body.go @@ -79,6 +79,7 @@ func (f *formatter) bracedBody(fields []*syntax.Field, open, close int, closeTra } openComments := f.sameLineComments(open) + openDoc := append([]doc.Doc{f.Text(" {")}, openComments...) if len(openComments) > 0 { openDoc = append(openDoc, doc.BreakParent) @@ -113,6 +114,7 @@ func (f *formatter) bracedEnumBody(values []*syntax.EnumValue, open, close int, } openComments := f.sameLineComments(open) + openDoc := append([]doc.Doc{f.Text(" {")}, openComments...) if len(openComments) > 0 { openDoc = append(openDoc, doc.BreakParent) @@ -158,6 +160,7 @@ func (f *formatter) service(v *syntax.Service) doc.Doc { p := f.Parts(2) p = append(p, f.Concat(parts...)) p = append(p, f.Concat(f.ownLineComments(close)...)) + inner := f.Concat(p...) if len(v.Functions) > 0 { // The first function starts its own line; the closing trivia @@ -174,6 +177,7 @@ func (f *formatter) service(v *syntax.Service) doc.Doc { } openComments := f.sameLineComments(open) + openDoc := append([]doc.Doc{f.Text(" {")}, openComments...) if len(openComments) > 0 { openDoc = append(openDoc, doc.BreakParent) @@ -276,6 +280,7 @@ func (f *formatter) throwsGroup(v *syntax.Function) doc.Doc { p := f.Parts(2) p = append(p, f.Text(" throws ")) p = append(p, f.parenGroup(v.Throws.Fields, v.Throws.TokStart(), v.Throws.TokEnd(), forced, f.opts.Separator.Get(ConstructThrows))) + return f.Concat(p...) } diff --git a/formatter/comments.go b/formatter/comments.go index 0e42236..b3521f4 100644 --- a/formatter/comments.go +++ b/formatter/comments.go @@ -61,6 +61,7 @@ func (f *formatter) ownLineComment(c, prev int, first bool) []doc.Doc { // the line. func (f *formatter) sameLineComment(ct syntax.Token) ([]doc.Doc, bool) { parts := f.Parts(2) + parts = append(parts, f.Text(" "), f.Text(trimComment(ct.Text))) if lineComment(ct.Kind) { return append(parts, doc.CommentLine), true @@ -183,6 +184,7 @@ func (f *formatter) hasOwnLineComments(idx int) bool { // comments after it. func (f *formatter) hasSameLineComments(idx int) bool { found := false + f.sameLineRun(idx, func(int) { found = true }) return found @@ -192,6 +194,7 @@ func (f *formatter) hasSameLineComments(idx int) bool { // a line comment, which owns its line end. func (f *formatter) sameLineEndsLine(idx int) bool { ended := false + f.sameLineRun(idx, func(c int) { if lineComment(f.token(c).Kind) { ended = true diff --git a/formatter/field.go b/formatter/field.go index 1dea6fa..a6c49d5 100644 --- a/formatter/field.go +++ b/formatter/field.go @@ -90,6 +90,7 @@ func (f *formatter) enumValueList(values []*syntax.EnumValue, bodyID int) doc.Do // comments stay on the item's line and do not break the group. func (f *formatter) groupedWith(prev, cur syntax.Node, sepMode SeparatorMode) bool { prevEnd := prev.TokEnd() + sep := syntax.TokenKind(0) if isListSep(f.token(prevEnd).Kind) { // The previous item's separator: comments before it belong to @@ -366,6 +367,7 @@ func (f *formatter) emitWithAnnotations(start, end int, ann *syntax.Annotations, parts := f.Parts(3) parts = append(parts, f.emitTokens(start, ann.TokStart()-1, first)) + parts = append(parts, f.annotationsDoc(ann, ann.TokEnd() == end)) if ann.TokEnd() < end { parts = append(parts, f.emitTokens(f.nextReal(ann.TokEnd()+1), end, emitOpts{leading: true, skipText: o.skipText})) diff --git a/formatter/format.go b/formatter/format.go index 877bef8..b2139ca 100644 --- a/formatter/format.go +++ b/formatter/format.go @@ -11,6 +11,7 @@ package formatter import ( "errors" "fmt" + "slices" "strings" "sync" @@ -335,27 +336,21 @@ type padEntry struct { // containsInt reports whether xs contains v. func containsInt(xs []int, v int) bool { - for _, x := range xs { - if x == v { - return true - } - } - - return false + return slices.Contains(xs, v) } // padAt returns the combined pads for the token index, or "". Multiple // entries at the same index (id pad + requiredness column) concatenate. func padAt(pads []padEntry, idx int) string { - var out string + var out strings.Builder for _, p := range pads { if p.idx == idx { - out += p.text + out.WriteString(p.text) } } - return out + return out.String() } // prevReal returns the index of the previous real (non-comment) token @@ -415,6 +410,7 @@ func (f *formatter) emitTokens(start, end int, o emitOpts) doc.Doc { // caller emits itself (braces, field separators) gets no // canonical gap — the caller's own spacing provides it. comments, lineEnded := f.commentsRun(prev, i) + parts = append(parts, comments...) if !lineEnded && (!skipped || o.text != "") { parts = append(parts, f.tokenGap(prev, i)) @@ -648,6 +644,7 @@ func (f *formatter) constant(v *syntax.Const) doc.Doc { // Own-line comments before the value render at the value boundary, // outside the value's own group. parts = append(parts, f.ownLineComments(value.TokStart())...) + parts = append(parts, f.constValue(value, value.TokEnd() == v.TokEnd())) if value.TokEnd() < v.TokEnd() { // Same-line comments after the value render at the value diff --git a/formatter/value.go b/formatter/value.go index 90dcdba..1d774a1 100644 --- a/formatter/value.go +++ b/formatter/value.go @@ -89,6 +89,7 @@ func (f *formatter) constItems(items []constItem, open, close int, c Construct, for i, item := range items { if i > 0 { prevEnd := items[i-1].end + sepIdx := f.nextReal(prevEnd + 1) if isListSep(f.token(sepIdx).Kind) { middle = append(middle, f.itemSep(sepIdx, sepMode)...) @@ -162,6 +163,7 @@ func sepForcesBreakList(seps []syntax.TokenKind, mode SeparatorMode) bool { // end (HardLine), so the separator lands on the next line by construction. func (f *formatter) itemSep(sep int, mode SeparatorMode) []doc.Doc { text := f.token(sep).Text + switch mode { case SeparatorComma: text = "," @@ -204,6 +206,7 @@ func (f *formatter) trailingItemSep(last int, mode SeparatorMode) doc.Doc { if hasSep { text = f.token(sep).Text } + switch mode { case SeparatorComma: text = "," diff --git a/lsp/cache/cow_test.go b/lsp/cache/cow_test.go index 6b10f28..f67c35e 100644 --- a/lsp/cache/cow_test.go +++ b/lsp/cache/cow_test.go @@ -65,7 +65,7 @@ func TestSnapshotCloneIsolation(t *testing.T) { // files is O(1): the maps are shared copy-on-write. func BenchmarkSnapshotClone(b *testing.B) { files := make([]*FileChange, 0, 100) - for i := 0; i < 100; i++ { + for i := range 100 { files = append(files, &FileChange{ URI: uri.URI(fmt.Sprintf("file:///tmp/bench%d.thrift", i)), Content: []byte("struct Gundam {\n\t1: required string Name\n}"), @@ -76,6 +76,7 @@ func BenchmarkSnapshotClone(b *testing.B) { ss := BuildSnapshotForTest(files) b.ResetTimer() + for i := 0; i < b.N; i++ { _, release := ss.clone() release() diff --git a/lsp/cache/file.go b/lsp/cache/file.go index 99f752d..7e70460 100644 --- a/lsp/cache/file.go +++ b/lsp/cache/file.go @@ -5,6 +5,7 @@ import ( "context" "crypto/sha256" "fmt" + "maps" "sync" "time" @@ -166,14 +167,10 @@ func (m *FilesMap) copyOnWrite() { } files := make(map[uri.URI]FileHandle, len(m.files)+1) - for k, v := range m.files { - files[k] = v - } + maps.Copy(files, m.files) overlays := make(map[uri.URI]*Overlay, len(m.overlays)+1) - for k, v := range m.overlays { - overlays[k] = v - } + maps.Copy(overlays, m.overlays) m.files = files m.overlays = overlays diff --git a/lsp/cache/parse.go b/lsp/cache/parse.go index 1d0143c..1b878f6 100644 --- a/lsp/cache/parse.go +++ b/lsp/cache/parse.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "log/slog" + "maps" "sync" "go.lsp.dev/uri" @@ -72,9 +73,7 @@ func (c *ParseCaches) copyOnWrite() { } caches := make(map[uri.URI]*ParsedFile, len(c.caches)+1) - for k, v := range c.caches { - caches[k] = v - } + maps.Copy(caches, c.caches) c.caches = caches c.shared = false diff --git a/lsp/cache/session_test.go b/lsp/cache/session_test.go index 48ebe4b..bad499b 100644 --- a/lsp/cache/session_test.go +++ b/lsp/cache/session_test.go @@ -62,6 +62,7 @@ func TestSessionViews(t *testing.T) { tt.setup(s) views := s.Views() + var got []uri.URI for _, v := range views { got = append(got, v.Folder()) @@ -88,6 +89,7 @@ func TestSessionRemoveViewForgetsMappings(t *testing.T) { folder := uri.File("/tmp/a") other := uri.File("/tmp/b") + s.AddView(folder) s.AddView(other) diff --git a/lsp/cache/view.go b/lsp/cache/view.go index efa3050..007b920 100644 --- a/lsp/cache/view.go +++ b/lsp/cache/view.go @@ -4,7 +4,7 @@ import ( "context" "log/slog" "math/rand" - "sort" + "slices" "strings" "sync" @@ -117,7 +117,7 @@ func (v *View) KnownFiles() []uri.URI { files = append(files, file) } - sort.Slice(files, func(i, j int) bool { return files[i] < files[j] }) + slices.Sort(files) return files } @@ -154,7 +154,8 @@ func (v *View) FileChange(ctx context.Context, changes []*FileChange, postFns .. for _, change := range changes { uris = append(uris, change.URI) } - sort.Slice(uris, func(i, j int) bool { return uris[i] < uris[j] }) + + slices.Sort(uris) for _, uri := range uris { if _, err := v.snapshot.Parse(ctx, uri); err != nil { diff --git a/lsp/codejump/utils.go b/lsp/codejump/utils.go index f2a07b6..811c5da 100644 --- a/lsp/codejump/utils.go +++ b/lsp/codejump/utils.go @@ -53,6 +53,7 @@ func definitionFiles(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast visit = func(f uri.URI) { doc := ast + if f != file { pf, err := ss.Parse(ctx, f) if err != nil || pf.AST() == nil { diff --git a/lsp/completion/token_completion.go b/lsp/completion/token_completion.go index b6676d9..109c5c7 100644 --- a/lsp/completion/token_completion.go +++ b/lsp/completion/token_completion.go @@ -106,6 +106,7 @@ func (c *TokenCompletion) Completion(ctx context.Context, ss *cache.Snapshot, cm // Shared pipeline: prefix filter, dedupe, sort, cap. filtered := candidates[:0] + seen := make(map[string]struct{}, len(candidates)) for _, cand := range candidates { // Echo suppression: a candidate identical to the typed text adds @@ -123,6 +124,7 @@ func (c *TokenCompletion) Completion(ctx context.Context, ss *cache.Snapshot, cm if _, ok := seen[cand.showText]; ok { continue } + seen[cand.showText] = struct{}{} filtered = append(filtered, cand) diff --git a/lsp/format_range_fuzz_test.go b/lsp/format_range_fuzz_test.go index 085e985..1b85a05 100644 --- a/lsp/format_range_fuzz_test.go +++ b/lsp/format_range_fuzz_test.go @@ -83,6 +83,7 @@ func FuzzFormatRangeText(f *testing.F) { // Applying every edit reproduces the whole-document formatting. var sb strings.Builder + prev = 0 for _, e := range edits { diff --git a/lsp/format_range_test.go b/lsp/format_range_test.go index 1264675..e6a2fc5 100644 --- a/lsp/format_range_test.go +++ b/lsp/format_range_test.go @@ -91,6 +91,7 @@ func TestBlockDiffApplyAll(t *testing.T) { edits := blockDiff([]byte(old), []byte(new)) var sb strings.Builder + prev := 0 for _, e := range edits { require.GreaterOrEqual(t, e.start, prev, "edits must be ordered and non-overlapping") @@ -98,6 +99,7 @@ func TestBlockDiffApplyAll(t *testing.T) { sb.WriteString(e.text) prev = e.end } + sb.WriteString(old[prev:]) assert.Equal(t, new, sb.String()) diff --git a/lsp/links/links_test.go b/lsp/links/links_test.go index a3f343f..42e80c4 100644 --- a/lsp/links/links_test.go +++ b/lsp/links/links_test.go @@ -64,6 +64,7 @@ struct S {}`, } require.Len(t, got, len(tt.want)) + for i, want := range tt.want { assert.Equal(t, want.line, got[i].Range.Start.Line) require.NotNil(t, got[i].Target) diff --git a/lsp/semantic/semantic.go b/lsp/semantic/semantic.go index 5aa42a0..83341db 100644 --- a/lsp/semantic/semantic.go +++ b/lsp/semantic/semantic.go @@ -178,6 +178,7 @@ func typeReferences(doc *syntax.Document) map[int]bool { types := map[int]bool{} var add func(t *syntax.FieldType) + add = func(t *syntax.FieldType) { if t == nil { return @@ -197,6 +198,7 @@ func typeReferences(doc *syntax.Document) map[int]bool { case *syntax.Service: for _, fn := range v.Functions { add(fn.Type) + for _, arg := range fn.Args { add(arg.Type) } diff --git a/lsp/semantic/semantic_test.go b/lsp/semantic/semantic_test.go index 60f0537..0504c18 100644 --- a/lsp/semantic/semantic_test.go +++ b/lsp/semantic/semantic_test.go @@ -21,6 +21,7 @@ type decodedToken struct { // coordinates. func decode(data []uint32) []decodedToken { var out []decodedToken + line, char := uint32(0), uint32(0) for i := 0; i+4 < len(data); i += 5 { @@ -189,6 +190,7 @@ exception BayFull { got := semanticTokens(t, src) var union, exception decodedToken + found := 0 for _, tok := range got { diff --git a/lsp/symbols/workspace_test.go b/lsp/symbols/workspace_test.go index 145c5eb..fbdd405 100644 --- a/lsp/symbols/workspace_test.go +++ b/lsp/symbols/workspace_test.go @@ -170,6 +170,7 @@ struct C { 1: string x }`, dir := writeTree(t, tt.files) session := cache.NewSession(cache.New(nil)) + if tt.nested { // Each top-level directory is a workspace folder. dirs := map[string]bool{} @@ -239,6 +240,7 @@ typedef string PilotName`, } syms := WorkspaceSymbols(t.Context(), session, "", 0) + byName := make(map[string]protocol.SymbolInformation, len(syms)) for _, s := range syms { byName[s.Name] = s @@ -288,6 +290,7 @@ service Federation { } syms := WorkspaceSymbols(t.Context(), session, "", 0) + byName := make(map[string]protocol.SymbolInformation, len(syms)) for _, s := range syms { byName[s.Name] = s @@ -331,6 +334,7 @@ exception BayFull { openTree(t, session, dir, nil) syms := WorkspaceSymbols(t.Context(), session, "", 0) + byName := make(map[string]protocol.SymbolInformation, len(syms)) for _, s := range syms { byName[s.Name] = s diff --git a/syntax/bench_test.go b/syntax/bench_test.go index e87dcc4..c415df5 100644 --- a/syntax/bench_test.go +++ b/syntax/bench_test.go @@ -10,11 +10,13 @@ var benchSrc = func() []byte { if err != nil { panic(err) } + return b }() func BenchmarkParse(b *testing.B) { b.ReportAllocs() + for i := 0; i < b.N; i++ { _, _ = Parse(benchSrc) } @@ -22,6 +24,7 @@ func BenchmarkParse(b *testing.B) { func BenchmarkLex(b *testing.B) { b.ReportAllocs() + for i := 0; i < b.N; i++ { _, _ = Lex(benchSrc) } diff --git a/syntax/lexer.go b/syntax/lexer.go index 014e9f7..9125d7b 100644 --- a/syntax/lexer.go +++ b/syntax/lexer.go @@ -267,16 +267,19 @@ func (l *lexer) scanTrivia() (blankLines int, comments []Token) { t := l.scanLineComment() t.BlankLinesBefore = blankLines blankLines = 0 + comments = append(comments, t) case c == '/' && l.peekByte(1) == '*': t := l.scanBlockComment() t.BlankLinesBefore = blankLines blankLines = 0 + comments = append(comments, t) case c == '#': t := l.scanLineComment() t.BlankLinesBefore = blankLines blankLines = 0 + comments = append(comments, t) case c == '@': // Java-style annotations (@name{...}) are preserved as trivia, @@ -285,6 +288,7 @@ func (l *lexer) scanTrivia() (blankLines int, comments []Token) { t := l.scanLineAnnotation() t.BlankLinesBefore = blankLines blankLines = 0 + comments = append(comments, t) default: return blankLines, comments -- 2.51.2