Skip to content

Commit a34044e

Browse files
committed
formatter: trim single-line block spacers
Single-line block headers should not keep a blank before their first real statement. Extend the blank-line batch pass to remove stale spacers for if, for, range, and case bodies while leaving multiline headers alone. Preserve leading-comment spacing and let the existing sole-return cleanup own return-only blocks. Add a regression that keeps t.Fatalf packed while removing the screenshot-shaped extra blank.
1 parent 95b5b48 commit a34044e

3 files changed

Lines changed: 120 additions & 1 deletion

File tree

dsl/action.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5103,6 +5103,7 @@ func (c *blankLineBatchContext) inspectFile(file *ast.File) {
51035103
caps := Captures{"node": n}
51045104
switch n.(type) {
51055105
case *ast.CaseClause:
5106+
c.maybeRemoveBlankAfterSingleLineBlockHeader(n)
51065107
if c.caseCond.Eval(caps, c.ctx) {
51075108
c.maybeInsertBlankBefore(n)
51085109
}
@@ -5128,19 +5129,91 @@ func (c *blankLineBatchContext) inspectFile(file *ast.File) {
51285129
}
51295130

51305131
case *ast.IfStmt:
5132+
c.maybeRemoveBlankAfterSingleLineBlockHeader(n)
51315133
if c.opts.ExtraIfErrReturn && c.ifErrReturnCond.Eval(
51325134
caps, c.ctx,
51335135
) {
51345136

51355137
c.maybeInsertBlankBefore(n)
51365138
}
5139+
5140+
case *ast.ForStmt:
5141+
c.maybeRemoveBlankAfterSingleLineBlockHeader(n)
5142+
5143+
case *ast.RangeStmt:
5144+
c.maybeRemoveBlankAfterSingleLineBlockHeader(n)
51375145
}
51385146

51395147
return true
51405148
},
51415149
)
51425150
}
51435151

5152+
func (c *blankLineBatchContext) maybeRemoveBlankAfterSingleLineBlockHeader(
5153+
node ast.Node) {
5154+
5155+
var first ast.Stmt
5156+
var headerStart token.Position
5157+
var headerEnd token.Position
5158+
5159+
switch n := node.(type) {
5160+
case *ast.IfStmt:
5161+
if n == nil || n.Body == nil || len(n.Body.List) == 0 {
5162+
return
5163+
}
5164+
first = n.Body.List[0]
5165+
headerStart = c.ctx.Fset.Position(n.Pos())
5166+
headerEnd = c.ctx.Fset.Position(n.Body.Lbrace)
5167+
5168+
case *ast.ForStmt:
5169+
if n == nil || n.Body == nil || len(n.Body.List) == 0 {
5170+
return
5171+
}
5172+
first = n.Body.List[0]
5173+
headerStart = c.ctx.Fset.Position(n.Pos())
5174+
headerEnd = c.ctx.Fset.Position(n.Body.Lbrace)
5175+
5176+
case *ast.RangeStmt:
5177+
if n == nil || n.Body == nil || len(n.Body.List) == 0 {
5178+
return
5179+
}
5180+
first = n.Body.List[0]
5181+
headerStart = c.ctx.Fset.Position(n.Pos())
5182+
headerEnd = c.ctx.Fset.Position(n.Body.Lbrace)
5183+
5184+
case *ast.CaseClause:
5185+
if n == nil || len(n.Body) == 0 {
5186+
return
5187+
}
5188+
first = n.Body[0]
5189+
headerStart = c.ctx.Fset.Position(n.Pos())
5190+
headerEnd = c.ctx.Fset.Position(n.Colon)
5191+
5192+
default:
5193+
return
5194+
}
5195+
5196+
if headerStart.Line != headerEnd.Line {
5197+
return
5198+
}
5199+
5200+
if _, ok := first.(*ast.ReturnStmt); ok &&
5201+
onlyStmtInBlockWithoutLeadingComment(c.ctx, first) {
5202+
return
5203+
}
5204+
5205+
start := c.ctx.Fset.Position(first.Pos()).Offset
5206+
if start <= 0 || start > len(c.ctx.Source) {
5207+
return
5208+
}
5209+
lineStartIdx := lineStart(c.ctx.Source, start)
5210+
if leadingCommentBlockLineStart(c.ctx.Source, lineStartIdx) !=
5211+
lineStartIdx {
5212+
return
5213+
}
5214+
c.maybeRemoveBlankBeforeLineStart(lineStartIdx)
5215+
}
5216+
51445217
func (c *blankLineBatchContext) maybeRemoveBlankBeforeLineStart(
51455218
lineStartIdx int) {
51465219

dsl/string_literal_split_action.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ func buildSplitQuotedForCallArgDSL(text string, startCol int, wsIndent string,
132132
}
133133

134134
for rest != "" {
135-
136135
// If indentation already exceeds the available width budget,
137136
// splitting can't help.
138137
if curStart >= colLimit {

formatter/pipeline_dsl_blanklines_extras_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,51 @@ func f(i int, childIdx uint32) error {
215215
require.NoError(t, err)
216216
}
217217

218+
func TestDSLBlankLinesNative_RemovesBlankAfterSingleLineIfWithWrappedFatalf(
219+
t *testing.T) {
220+
221+
const in = `package p
222+
223+
import "testing"
224+
225+
func f(t *testing.T, got, want []int) {
226+
if len(got) != len(want) {
227+
228+
t.Fatalf("input_indices length mismatch: got %d want %d",
229+
len(got), len(want))
230+
}
231+
}
232+
`
233+
234+
p := NewPipeline(
235+
PipelineConfig{
236+
ColumnLimit: 80,
237+
TabStop: 8,
238+
UseDSLLogCalls: true,
239+
UseDSLBlankLines: true,
240+
UseDSLBlankLinesNative: true,
241+
},
242+
)
243+
244+
first := p.Format([]byte(in))
245+
second := p.Format(first)
246+
require.Equal(t, string(first), string(second))
247+
248+
out := string(first)
249+
require.Contains(
250+
t, out, "if len(got) != len(want) "+
251+
"{\n t.Fatalf(\"input_indices length "+
252+
"mismatch: got %d want %d\",",
253+
)
254+
require.Contains(t, out, "\n\t\t\tlen(got), len(want))")
255+
require.NotContains(
256+
t, out, "if len(got) != len(want) {\n\n t.Fatalf(",
257+
)
258+
require.NotContains(t, out, "t.Fatalf(\n")
259+
260+
fset := token.NewFileSet()
261+
_, err := parser.ParseFile(fset, "out.go", first, parser.AllErrors)
262+
require.NoError(t, err)
263+
}
264+
218265
// Note: legacy/parity profiles were removed; llformat is next-only.

0 commit comments

Comments
 (0)