Skip to content

Commit 010a11d

Browse files
committed
formatter: preserve fitting formatted blocks
Keep already-multiline multi-argument fmt.Errorf calls unchanged when all existing physical lines fit the configured column limit. This avoids collapsing a deliberately split format string and argument back onto one line just because the call target is printf-like. Treat blank line comments as paragraph boundaries in conservative overflow mode. A wrapped paragraph can still be fixed, but a separate fitting paragraph after a `//` separator is preserved as its own unit. Add synthetic regressions for both cases without relying on downstream repository snippets.
1 parent a34044e commit 010a11d

4 files changed

Lines changed: 149 additions & 0 deletions

File tree

dsl/action.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,9 @@ func (a *LeftFlowCallAction) Execute(caps Captures, ctx *Context) ([]byte,
22632263

22642264
original := ctx.Source[start:end]
22652265
wsIndent := ctx.IndentAt(call)
2266+
if shouldPreserveFittingMultilineErrorf(ctx, call, start, end, original) {
2267+
return nil, false
2268+
}
22662269

22672270
// Find the base length (visual width from line start to call start).
22682271
baseLen := prefixWidthAt(ctx.Source, start, ctx.TabStop)
@@ -2315,6 +2318,23 @@ func (a *LeftFlowCallAction) Execute(caps Captures, ctx *Context) ([]byte,
23152318
return out, true
23162319
}
23172320

2321+
func shouldPreserveFittingMultilineErrorf(ctx *Context, call *ast.CallExpr,
2322+
start int, end int, original []byte) bool {
2323+
2324+
if callExprFuncNameFromExpr(call.Fun) != "fmt.Errorf" {
2325+
return false
2326+
}
2327+
if len(call.Args) < 2 {
2328+
return false
2329+
}
2330+
if !strings.Contains(string(original), "\n") {
2331+
return false
2332+
}
2333+
2334+
return maxVisualLineLenInSpan(ctx.Source, start, end, ctx.TabStop) <=
2335+
ctx.ColumnLimit
2336+
}
2337+
23182338
func callIsCompositeKeyValue(call *ast.CallExpr, ctx *Context) bool {
23192339
if call == nil || ctx == nil {
23202340
return false

formatter/comment_formatter_conservative_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,42 @@ func f() {}
5252
requireNoLineLongerThan(t, out, 48)
5353
}
5454

55+
func TestCommentFormatterOverflowModeSplitsLineBlocksAtBlankComments(
56+
t *testing.T) {
57+
58+
in := []byte(`package p
59+
60+
// The queued value is persisted for crash recovery, but successful responses
61+
// are intentionally handled by the in-memory request path for this generic worker.
62+
// Returns an error if the claim has expired or belongs to another worker.
63+
//
64+
// The context should contain any transaction needed for atomic operations.
65+
// If a transaction is present, the update will be part of that
66+
// transaction.
67+
func f() {}
68+
`)
69+
70+
f := compat.NewCommentFormatter(
71+
compat.CommentConfig{
72+
ColumnLimit: 80,
73+
Mode: compat.CommentModeOverflow,
74+
},
75+
)
76+
out := string(f.FormatFile(in))
77+
78+
require.Contains(
79+
t, out, "// The context should contain any transaction "+
80+
"needed for atomic operations.\n// If a "+
81+
"transaction is present, the update will be part "+
82+
"of that\n// transaction.",
83+
)
84+
require.NotContains(
85+
t, out, "// The context should contain any transaction "+
86+
"needed for atomic operations. If a",
87+
)
88+
requireNoLineLongerThan(t, out, 80)
89+
}
90+
5591
func TestCommentFormatterOverflowModePreservesPreformattedBlocks(t *testing.T) {
5692
in := []byte(`package p
5793

formatter/pipeline_logcalls_next_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,48 @@ func f(t *Tree) (*Path, error) {
555555
require.NotContains(t, out, "fmt.Errorf(\n")
556556
}
557557

558+
func TestPipelineNext_LogCalls_PreservesFittingMultilineErrorf(t *testing.T) {
559+
const in = `package p
560+
561+
import "fmt"
562+
563+
const maxTreeDepth = 32
564+
565+
func nodeMaxDepth(depth int) (int, error) {
566+
if depth > maxTreeDepth {
567+
return 0, fmt.Errorf(
568+
"tree depth exceeds limit %d",
569+
maxTreeDepth,
570+
)
571+
}
572+
573+
return 0, nil
574+
}
575+
`
576+
577+
p := NewPipeline(PipelineConfig{
578+
ColumnLimit: 80,
579+
TabStop: 8,
580+
UseDSLLogCalls: true,
581+
// Keep other DSL stages off to make this test focused.
582+
UseDSLMultiLineCalls: false,
583+
UseDSLExpr: false,
584+
UseDSLComments: false,
585+
UseDSLFuncSigs: false,
586+
UseDSLBlankLines: false,
587+
})
588+
589+
out := string(p.Format([]byte(in)))
590+
591+
require.Contains(
592+
t, out, "\"tree depth exceeds limit "+
593+
"%d\",\n maxTreeDepth,",
594+
)
595+
require.NotContains(
596+
t, out, "\"tree depth exceeds limit %d\", maxTreeDepth,",
597+
)
598+
}
599+
558600
func TestPipelineNext_LogCalls_FormatCompositeArgAsBlock(t *testing.T) {
559601
const in = `package p
560602

internal/compat/comment_formatter.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,12 +958,63 @@ func processLineCommentBlock(lines []string, i int, out []string) ([]string,
958958
j++
959959
}
960960

961+
if commentMode == CommentModeOverflow &&
962+
lineCommentBlockHasBlank(block) {
963+
964+
reflowed := reflowLineCommentBlockByBlankBoundaries(
965+
block, indent,
966+
)
967+
out = append(out, reflowed...)
968+
969+
return out, j, true
970+
}
971+
961972
reflowed := reflowLineCommentBlock(block, indent)
962973
out = append(out, reflowed...)
963974

964975
return out, j, true
965976
}
966977

978+
func lineCommentBlockHasBlank(block []string) bool {
979+
for _, line := range block {
980+
if isBlankLineComment(line) {
981+
return true
982+
}
983+
}
984+
985+
return false
986+
}
987+
988+
func isBlankLineComment(line string) bool {
989+
return strings.TrimSpace(commentLineContent(line)) == ""
990+
}
991+
992+
func reflowLineCommentBlockByBlankBoundaries(block []string,
993+
indent string) []string {
994+
995+
var out []string
996+
var segment []string
997+
flush := func() {
998+
if len(segment) == 0 {
999+
return
1000+
}
1001+
out = append(out, reflowLineCommentBlock(segment, indent)...)
1002+
segment = nil
1003+
}
1004+
1005+
for _, line := range block {
1006+
if isBlankLineComment(line) {
1007+
flush()
1008+
out = append(out, line)
1009+
continue
1010+
}
1011+
segment = append(segment, line)
1012+
}
1013+
flush()
1014+
1015+
return out
1016+
}
1017+
9671018
// processBlockComment handles a standalone block comment starting at index i.
9681019
// Returns the updated output slice, new index, and whether a block was
9691020
// processed.

0 commit comments

Comments
 (0)