@@ -1010,7 +1010,6 @@ func (a *BreakArithmeticChainLayoutAction) Execute(caps Captures,
10101010
10111011 switch binExpr .Op {
10121012 case token .ADD , token .SUB , token .MUL , token .QUO , token .REM :
1013-
10141013 default :
10151014 return nil , false
10161015 }
@@ -5163,6 +5162,21 @@ func (c *blankLineBatchContext) initConditions() {
51635162}
51645163
51655164func (c * blankLineBatchContext ) maybeInsertBlankBefore (n ast.Node ) {
5165+ c .maybeInsertBlankBeforeNode (n , true )
5166+ }
5167+
5168+ func (c * blankLineBatchContext ) maybeInsertBlankBeforeClause (n ast.Node ) {
5169+ insert , attachLeadingComments := clauseSeparatorBehavior (c .ctx , n )
5170+ if ! insert {
5171+ return
5172+ }
5173+
5174+ c .maybeInsertBlankBeforeNode (n , attachLeadingComments )
5175+ }
5176+
5177+ func (c * blankLineBatchContext ) maybeInsertBlankBeforeNode (n ast.Node ,
5178+ attachLeadingComments bool ) {
5179+
51665180 if n == nil {
51675181 return
51685182 }
@@ -5171,7 +5185,9 @@ func (c *blankLineBatchContext) maybeInsertBlankBefore(n ast.Node) {
51715185 return
51725186 }
51735187 ls := lineStart (c .ctx .Source , start )
5174- ls = leadingCommentBlockLineStart (c .ctx .Source , ls )
5188+ if attachLeadingComments {
5189+ ls = leadingCommentBlockLineStart (c .ctx .Source , ls )
5190+ }
51755191 if ls <= 0 || hasBlankLineBeforeLineStart (c .ctx .Source , ls ) {
51765192 return
51775193 }
@@ -5191,7 +5207,13 @@ func (c *blankLineBatchContext) inspectFile(file *ast.File) {
51915207 case * ast.CaseClause :
51925208 c .maybeRemoveBlankAfterSingleLineBlockHeader (n )
51935209 if c .caseCond .Eval (caps , c .ctx ) {
5194- c .maybeInsertBlankBefore (n )
5210+ c .maybeInsertBlankBeforeClause (n )
5211+ }
5212+
5213+ case * ast.CommClause :
5214+ c .maybeRemoveBlankAfterSingleLineBlockHeader (n )
5215+ if c .caseCond .Eval (caps , c .ctx ) {
5216+ c .maybeInsertBlankBeforeClause (n )
51955217 }
51965218
51975219 case * ast.ReturnStmt :
@@ -5268,12 +5290,34 @@ func (c *blankLineBatchContext) maybeRemoveBlankAfterSingleLineBlockHeader(
52685290 headerEnd = c .ctx .Fset .Position (n .Body .Lbrace )
52695291
52705292 case * ast.CaseClause :
5271- if n == nil || len (n .Body ) == 0 {
5293+ if n == nil {
5294+ return
5295+ }
5296+ headerStart = c .ctx .Fset .Position (n .Pos ())
5297+ headerEnd = c .ctx .Fset .Position (n .Colon )
5298+ if len (n .Body ) == 0 {
5299+ c .maybeRemoveBlankAfterHeaderLine (
5300+ headerStart , headerEnd ,
5301+ )
5302+
52725303 return
52735304 }
52745305 first = n .Body [0 ]
5306+
5307+ case * ast.CommClause :
5308+ if n == nil {
5309+ return
5310+ }
52755311 headerStart = c .ctx .Fset .Position (n .Pos ())
52765312 headerEnd = c .ctx .Fset .Position (n .Colon )
5313+ if len (n .Body ) == 0 {
5314+ c .maybeRemoveBlankAfterHeaderLine (
5315+ headerStart , headerEnd ,
5316+ )
5317+
5318+ return
5319+ }
5320+ first = n .Body [0 ]
52775321
52785322 default :
52795323 return
@@ -5300,6 +5344,31 @@ func (c *blankLineBatchContext) maybeRemoveBlankAfterSingleLineBlockHeader(
53005344 c .maybeRemoveBlankBeforeLineStart (lineStartIdx )
53015345}
53025346
5347+ func (c * blankLineBatchContext ) maybeRemoveBlankAfterHeaderLine (
5348+ headerStart , headerEnd token.Position ) {
5349+
5350+ if headerStart .Line != headerEnd .Line {
5351+ return
5352+ }
5353+
5354+ headerLineEnd := lineEnd (c .ctx .Source , headerEnd .Offset )
5355+ if headerLineEnd < 0 || headerLineEnd >= len (c .ctx .Source ) ||
5356+ c .ctx .Source [headerLineEnd ] != '\n' {
5357+ return
5358+ }
5359+
5360+ blankLineStart := headerLineEnd + 1
5361+ blankLineEnd := lineEnd (c .ctx .Source , blankLineStart )
5362+ if blankLineEnd < blankLineStart || blankLineEnd >= len (c .ctx .Source ) {
5363+ return
5364+ }
5365+ if ! isWhitespaceOnlyLine (c .ctx .Source [blankLineStart :blankLineEnd ]) {
5366+ return
5367+ }
5368+
5369+ c .b .Delete (blankLineStart , blankLineEnd + 1 )
5370+ }
5371+
53035372func (c * blankLineBatchContext ) maybeRemoveBlankBeforeLineStart (
53045373 lineStartIdx int ) {
53055374
@@ -5468,6 +5537,130 @@ func (a *InsertBlankBeforeAction) Execute(caps Captures, ctx *Context) ([]byte,
54685537 return out , true
54695538}
54705539
5540+ // InsertBlankBeforeClauseAction inserts a blank line before a switch/select
5541+ // clause label.
5542+ type InsertBlankBeforeClauseAction struct {
5543+ Target string
5544+ }
5545+
5546+ // Execute implements Action for InsertBlankBeforeClauseAction.
5547+ func (a * InsertBlankBeforeClauseAction ) Execute (caps Captures , ctx * Context ) (
5548+ []byte , bool ) {
5549+
5550+ node := resolveTarget (caps , a .Target )
5551+ if node == nil {
5552+ return nil , false
5553+ }
5554+
5555+ pos := ctx .Fset .Position (node .Pos ())
5556+ insert , attachLeadingComments := clauseSeparatorBehavior (ctx , node )
5557+ if ! insert {
5558+ return nil , false
5559+ }
5560+
5561+ ls := lineStart (ctx .Source , pos .Offset )
5562+ if attachLeadingComments {
5563+ ls = leadingCommentBlockLineStart (ctx .Source , ls )
5564+ }
5565+ if hasBlankLineBeforeLineStart (ctx .Source , ls ) {
5566+ return nil , false
5567+ }
5568+
5569+ out , err := ApplySingleEdit (ctx .Source , ls , ls , []byte ("\n " ))
5570+ if err != nil {
5571+ return nil , false
5572+ }
5573+
5574+ return out , true
5575+ }
5576+
5577+ func clauseSeparatorBehavior (ctx * Context ,
5578+ node ast.Node ) (insert bool , attachLeadingComments bool ) {
5579+
5580+ parent , ok := ctx .Parent (node ).(* ast.BlockStmt )
5581+ if ! ok || parent == nil {
5582+ return true , true
5583+ }
5584+
5585+ for i , stmt := range parent .List {
5586+ if stmt != node {
5587+ continue
5588+ }
5589+ if i == 0 {
5590+ return true , true
5591+ }
5592+
5593+ prev := parent .List [i - 1 ]
5594+ if clauseBodyLen (prev ) > 0 {
5595+ return true , true
5596+ }
5597+
5598+ if clauseGapHasComment (ctx , prev , node ) {
5599+ return true , false
5600+ }
5601+
5602+ return false , false
5603+ }
5604+
5605+ return true , true
5606+ }
5607+
5608+ func clauseBodyLen (stmt ast.Stmt ) int {
5609+ switch n := stmt .(type ) {
5610+ case * ast.CaseClause :
5611+ if n == nil {
5612+ return 0
5613+ }
5614+
5615+ return len (n .Body )
5616+
5617+ case * ast.CommClause :
5618+ if n == nil {
5619+ return 0
5620+ }
5621+
5622+ return len (n .Body )
5623+
5624+ default :
5625+ return 0
5626+ }
5627+ }
5628+
5629+ func clauseGapHasComment (ctx * Context , prev ast.Stmt , next ast.Node ) bool {
5630+ start := clauseColonOffset (ctx , prev )
5631+ end := ctx .Fset .Position (next .Pos ()).Offset
5632+ if start < 0 || end < 0 || start >= end || end > len (ctx .Source ) {
5633+ return false
5634+ }
5635+
5636+ gap := ctx .Source [start :end ]
5637+
5638+ return bytes .Contains (gap , []byte ("//" )) ||
5639+ bytes .Contains (gap , []byte ("/*" ))
5640+ }
5641+
5642+ func clauseColonOffset (ctx * Context , stmt ast.Stmt ) int {
5643+ var pos token.Pos
5644+ switch n := stmt .(type ) {
5645+ case * ast.CaseClause :
5646+ if n == nil {
5647+ return - 1
5648+ }
5649+ pos = n .Colon
5650+
5651+ case * ast.CommClause :
5652+ if n == nil {
5653+ return - 1
5654+ }
5655+ pos = n .Colon
5656+
5657+ default :
5658+ return - 1
5659+ }
5660+
5661+ return ctx .Fset .Position (pos ).Offset
5662+ }
5663+
54715664// InsertBlankAfterAction inserts a blank line after a node if not already
54725665// present.
54735666type InsertBlankAfterAction struct {
0 commit comments