Skip to content

Commit e9abfc2

Browse files
authored
Blink parity: split shared-line commas in multiline arrays (#133)
1 parent ffbcd1e commit e9abfc2

2 files changed

Lines changed: 50 additions & 3 deletions

File tree

‎blink/internal/fixer/rules/array_list_item_newline.go‎

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,18 @@ func (ArrayListItemNewline) Fix(s *tokens.Stream) bool {
3737
if closeIdx < 0 || sigNext(s, open) == closeIdx {
3838
continue // empty []
3939
}
40-
if arrayTopLevelMultiline(s, open, closeIdx) {
41-
continue // items already on their own lines: array_indentation aligns it
42-
}
4340
if !arrayHasTopLevelArrow(s, open, closeIdx) {
4441
continue // plain list array: leave inline
4542
}
43+
if arrayTopLevelMultiline(s, open, closeIdx) {
44+
// already spread over lines, but a top-level comma may still share its
45+
// line with the next item ("], 'attr' => ["); break each such comma and
46+
// let array_indentation align the result
47+
if splitTopLevelCommas(s, open, closeIdx) {
48+
changed = true
49+
}
50+
continue
51+
}
4652
// a multiline array carries a trailing comma; add it before reflowing so
4753
// the last item ends up on its own line with the comma
4854
if last := sigPrev(s, closeIdx); last > open && s.At(last).Value != "," {
@@ -57,6 +63,41 @@ func (ArrayListItemNewline) Fix(s *tokens.Stream) bool {
5763
return changed
5864
}
5965

66+
// splitTopLevelCommas breaks every top-level comma of a multiline array onto a
67+
// new line when it still shares its line with the next item, mirroring Symplify's
68+
// ArrayItemNewliner. A comma already followed by a newline, by a comment, or by a
69+
// "{" is left alone; array_indentation then aligns the inserted breaks.
70+
func splitTopLevelCommas(s *tokens.Stream, open, closeIdx int) bool {
71+
changed := false
72+
for i := open + 1; i < closeIdx; i++ {
73+
t := s.At(i)
74+
if t.Kind != token.Punct {
75+
continue
76+
}
77+
switch t.Value {
78+
case "(", "[", "{":
79+
if c := s.MatchForward(i); c > 0 && c < closeIdx {
80+
i = c
81+
}
82+
case ",":
83+
if i+1 < closeIdx && s.At(i+1).Kind == token.Whitespace && hasNewline(s.At(i+1).Value) {
84+
continue // already on its own line
85+
}
86+
ns := nextSignificantIndex(s, i)
87+
if ns < 0 || ns >= closeIdx {
88+
continue // trailing comma before "]"
89+
}
90+
if isComment(s.At(ns)) || (s.At(ns).Kind == token.Punct && s.At(ns).Value == "{") {
91+
continue
92+
}
93+
if editSlotAfter(s, i, "\n") {
94+
changed = true
95+
}
96+
}
97+
}
98+
return changed
99+
}
100+
60101
// arrayTopLevelMultiline reports whether the array already has a newline at its
61102
// own nesting level (an item on its own line). Newlines only inside a nested
62103
// element (e.g. a match block) do not count.

‎blink/internal/fixer/rules/array_list_item_newline_test.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,10 @@ func TestArrayListItemNewline(t *testing.T) {
3737
if _, changed := apply(t, f, "<?php\n$a = foo()['x'];"); changed {
3838
t.Fatal("call-result index access must stay inline")
3939
}
40+
41+
// a multiline array whose top-level comma still shares a line gets split
42+
got, changed = apply(t, f, "<?php\n$a = [\n 'x' => 1,\n 'y' => [\n 'z' => 2,\n ], 'w' => 3,\n];")
43+
if want := "<?php\n$a = [\n 'x' => 1,\n 'y' => [\n 'z' => 2,\n ],\n'w' => 3,\n];"; !changed || got != want {
44+
t.Fatalf("multiline split: changed=%v got=%q", changed, got)
45+
}
4046
}

0 commit comments

Comments
 (0)