Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions blink/internal/fixer/rules/array_list_item_newline.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,18 @@ func (ArrayListItemNewline) Fix(s *tokens.Stream) bool {
if closeIdx < 0 || sigNext(s, open) == closeIdx {
continue // empty []
}
if arrayTopLevelMultiline(s, open, closeIdx) {
continue // items already on their own lines: array_indentation aligns it
}
if !arrayHasTopLevelArrow(s, open, closeIdx) {
continue // plain list array: leave inline
}
if arrayTopLevelMultiline(s, open, closeIdx) {
// already spread over lines, but a top-level comma may still share its
// line with the next item ("], 'attr' => ["); break each such comma and
// let array_indentation align the result
if splitTopLevelCommas(s, open, closeIdx) {
changed = true
}
continue
}
// a multiline array carries a trailing comma; add it before reflowing so
// the last item ends up on its own line with the comma
if last := sigPrev(s, closeIdx); last > open && s.At(last).Value != "," {
Expand All @@ -57,6 +63,41 @@ func (ArrayListItemNewline) Fix(s *tokens.Stream) bool {
return changed
}

// splitTopLevelCommas breaks every top-level comma of a multiline array onto a
// new line when it still shares its line with the next item, mirroring Symplify's
// ArrayItemNewliner. A comma already followed by a newline, by a comment, or by a
// "{" is left alone; array_indentation then aligns the inserted breaks.
func splitTopLevelCommas(s *tokens.Stream, open, closeIdx int) bool {
changed := false
for i := open + 1; i < closeIdx; i++ {
t := s.At(i)
if t.Kind != token.Punct {
continue
}
switch t.Value {
case "(", "[", "{":
if c := s.MatchForward(i); c > 0 && c < closeIdx {
i = c
}
case ",":
if i+1 < closeIdx && s.At(i+1).Kind == token.Whitespace && hasNewline(s.At(i+1).Value) {
continue // already on its own line
}
ns := nextSignificantIndex(s, i)
if ns < 0 || ns >= closeIdx {
continue // trailing comma before "]"
}
if isComment(s.At(ns)) || (s.At(ns).Kind == token.Punct && s.At(ns).Value == "{") {
continue
}
if editSlotAfter(s, i, "\n") {
changed = true
}
}
}
return changed
}

// arrayTopLevelMultiline reports whether the array already has a newline at its
// own nesting level (an item on its own line). Newlines only inside a nested
// element (e.g. a match block) do not count.
Expand Down
6 changes: 6 additions & 0 deletions blink/internal/fixer/rules/array_list_item_newline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ func TestArrayListItemNewline(t *testing.T) {
if _, changed := apply(t, f, "<?php\n$a = foo()['x'];"); changed {
t.Fatal("call-result index access must stay inline")
}

// a multiline array whose top-level comma still shares a line gets split
got, changed = apply(t, f, "<?php\n$a = [\n 'x' => 1,\n 'y' => [\n 'z' => 2,\n ], 'w' => 3,\n];")
if want := "<?php\n$a = [\n 'x' => 1,\n 'y' => [\n 'z' => 2,\n ],\n'w' => 3,\n];"; !changed || got != want {
t.Fatalf("multiline split: changed=%v got=%q", changed, got)
}
}
Loading