From 2947261670ef1e3d675e9698d84ef4927d2588da Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 5 Oct 2026 11:37:07 +0200 Subject: [PATCH 1/3] Collapse blank lines between call and parameter arguments php-cs-fixer's MethodArgumentSpace collapses a blank line between the arguments of a call or declaration (including promoted params with standalone attributes), but a blank line between array items is kept. reflowParen preserved blanks for every context; it now collapses them for call/declaration/parameter parens and keeps them only for array literals. --- .../fixer/rules/array_list_item_newline.go | 2 +- .../rules/array_list_item_newline_test.go | 5 +++++ .../fixer/rules/method_argument_space.go | 18 ++++++++++-------- .../fixer/rules/method_argument_space_test.go | 3 ++- .../standalone_line_in_multiline_array.go | 2 +- .../standalone_line_plain_constructor_param.go | 2 +- .../rules/standalone_line_promoted_property.go | 2 +- .../rules/standalone_line_required_param.go | 2 +- 8 files changed, 22 insertions(+), 14 deletions(-) diff --git a/blink/internal/fixer/rules/array_list_item_newline.go b/blink/internal/fixer/rules/array_list_item_newline.go index 9a4592f9ee..5734135569 100644 --- a/blink/internal/fixer/rules/array_list_item_newline.go +++ b/blink/internal/fixer/rules/array_list_item_newline.go @@ -68,7 +68,7 @@ func (ArrayListItemNewline) Fix(s *tokens.Stream) bool { closeIdx++ changed = true } - if reflowParen(s, open, closeIdx) { + if reflowParen(s, open, closeIdx, false) { changed = true } } diff --git a/blink/internal/fixer/rules/array_list_item_newline_test.go b/blink/internal/fixer/rules/array_list_item_newline_test.go index f46bf8e9db..ef1ef2984f 100644 --- a/blink/internal/fixer/rules/array_list_item_newline_test.go +++ b/blink/internal/fixer/rules/array_list_item_newline_test.go @@ -56,4 +56,9 @@ func TestArrayListItemNewline(t *testing.T) { if _, changed := apply(t, f, " 1,\n 'm' => 2,\n]];"); changed { t.Fatal("already-multiline inner must not break the outer opener") } + + // a blank line a user put between array items is preserved (unlike call args) + if _, changed := apply(t, f, " 1,\n\n 'b' => 2,\n];"); changed { + t.Fatal("blank line between array items must be preserved") + } } diff --git a/blink/internal/fixer/rules/method_argument_space.go b/blink/internal/fixer/rules/method_argument_space.go index 08313d7c97..a0c0df0f0d 100644 --- a/blink/internal/fixer/rules/method_argument_space.go +++ b/blink/internal/fixer/rules/method_argument_space.go @@ -279,7 +279,7 @@ func reflowSingleArgOrMultiline(s *tokens.Stream) bool { if ensureSingleLineForParen(s, open, closeIdx) { changed = true } - } else if reflowParen(s, open, closeIdx) { + } else if reflowParen(s, open, closeIdx, true) { changed = true } } @@ -409,7 +409,7 @@ func reflowMultilineArgs(s *tokens.Stream) bool { if sigNext(s, open) == closeIdx { continue // empty () } - if reflowParen(s, open, closeIdx) { + if reflowParen(s, open, closeIdx, true) { changed = true } } @@ -542,7 +542,7 @@ func placeAttributesInParen(s *tokens.Stream, open, closeIdx int, placement stri // reflowParen puts each top-level argument of the paren at open on its own line, // with "(" and ")" on their own lines, indented one level past the call. -func reflowParen(s *tokens.Stream, open, closeIdx int) bool { +func reflowParen(s *tokens.Stream, open, closeIdx int, collapseBlanks bool) bool { changed := false base := lineIndentBefore(s, open) argNL := "\n" + base + " " @@ -573,7 +573,7 @@ func reflowParen(s *tokens.Stream, open, closeIdx int) bool { changed = true } for _, c := range slices.Backward(commas) { - if reflowAfterComma(s, c, base) { + if reflowAfterComma(s, c, base, collapseBlanks) { changed = true } } @@ -638,7 +638,7 @@ func reflowBreakAfterComments(s *tokens.Stream, open, closeIdx int, base string) // comment that sits on the comma's line ("arg, // note" or "arg, /* note */") // stays there and the break goes after the comment, matching php-cs-fixer; // otherwise the break goes right after the comma. -func reflowAfterComma(s *tokens.Stream, comma int, base string) bool { +func reflowAfterComma(s *tokens.Stream, comma int, base string, collapseBlanks bool) bool { n := comma + 1 ws := -1 if n < s.Len() && s.At(n).Kind == token.Whitespace && !hasNewline(s.At(n).Value) { @@ -663,7 +663,7 @@ func reflowAfterComma(s *tokens.Stream, comma int, base string) bool { } return changed } - return editSlotAfter(s, comma, argNLAfterComma(s, comma, base)) + return editSlotAfter(s, comma, argNLAfterComma(s, comma, base, collapseBlanks)) } // argListIsMultiline reports whether the argument list is split at the top level @@ -756,8 +756,10 @@ func lineIndentBefore(s *tokens.Stream, idx int) string { // arguments is preserved (ECS keeps blank lines in a multiline argument list and // only normalizes the indentation), so a comma whose following whitespace holds a // blank line keeps that blank rather than collapsing to a single newline. -func argNLAfterComma(s *tokens.Stream, comma int, base string) string { - if comma+1 < s.Len() { +func argNLAfterComma(s *tokens.Stream, comma int, base string, collapseBlanks bool) string { + // a call/declaration argument list collapses a blank line between arguments + // (php-cs-fixer); an array literal keeps a blank line a user put between items + if !collapseBlanks && comma+1 < s.Len() { ws := s.At(comma + 1) if ws.Kind == token.Whitespace { if newlines := strings.Count(ws.Value, "\n"); newlines >= 2 { diff --git a/blink/internal/fixer/rules/method_argument_space_test.go b/blink/internal/fixer/rules/method_argument_space_test.go index 3e1fd66dfd..50ec3c90c2 100644 --- a/blink/internal/fixer/rules/method_argument_space_test.go +++ b/blink/internal/fixer/rules/method_argument_space_test.go @@ -22,7 +22,8 @@ func TestMethodArgumentSpace(t *testing.T) { {"multiline arg list becomes fully multiline", " Date: Mon, 5 Oct 2026 11:38:10 +0200 Subject: [PATCH 2/3] Ratchet mautic parity gate to 0.4% --- .github/workflows/blink_parity_mautic.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/blink_parity_mautic.yaml b/.github/workflows/blink_parity_mautic.yaml index 3f61d1805c..b9038ac007 100644 --- a/.github/workflows/blink_parity_mautic.yaml +++ b/.github/workflows/blink_parity_mautic.yaml @@ -95,7 +95,7 @@ jobs: - name: Compare the two trees run: | # ratchet gate: fail once the differing share crosses this; lower it as parity improves - MAX_DIFF_PERCENT=0.5 + MAX_DIFF_PERCENT=0.4 total=0 differ=0 differing_files="" From d6401a2afa941f55047353af935530fee71029ee Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 5 Oct 2026 11:47:41 +0200 Subject: [PATCH 3/3] Keep a blank line before a commented argument when collapsing The call/parameter blank-line collapse removed a blank line that precedes a comment annotating the next argument; php-cs-fixer keeps it. Preserve the blank line when the next token after the comma is a non-attribute comment. --- .../fixer/rules/method_argument_space.go | 18 ++++++++++++++---- .../fixer/rules/method_argument_space_test.go | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/blink/internal/fixer/rules/method_argument_space.go b/blink/internal/fixer/rules/method_argument_space.go index a0c0df0f0d..478e7a37bb 100644 --- a/blink/internal/fixer/rules/method_argument_space.go +++ b/blink/internal/fixer/rules/method_argument_space.go @@ -757,13 +757,23 @@ func lineIndentBefore(s *tokens.Stream, idx int) string { // only normalizes the indentation), so a comma whose following whitespace holds a // blank line keeps that blank rather than collapsing to a single newline. func argNLAfterComma(s *tokens.Stream, comma int, base string, collapseBlanks bool) string { - // a call/declaration argument list collapses a blank line between arguments - // (php-cs-fixer); an array literal keeps a blank line a user put between items - if !collapseBlanks && comma+1 < s.Len() { + // an array literal keeps a blank line a user put between items; a call or + // declaration list collapses it, except before a comment that annotates the + // next argument, which php-cs-fixer keeps + if comma+1 < s.Len() { ws := s.At(comma + 1) if ws.Kind == token.Whitespace { if newlines := strings.Count(ws.Value, "\n"); newlines >= 2 { - return strings.Repeat("\n", newlines) + base + " " + keep := !collapseBlanks + if collapseBlanks { + if nx := nextSignificantIndex(s, comma); nx >= 0 && + isComment(s.At(nx)) && !isAttributeComment(s.At(nx)) { + keep = true + } + } + if keep { + return strings.Repeat("\n", newlines) + base + " " + } } } } diff --git a/blink/internal/fixer/rules/method_argument_space_test.go b/blink/internal/fixer/rules/method_argument_space_test.go index 50ec3c90c2..e69d9dc502 100644 --- a/blink/internal/fixer/rules/method_argument_space_test.go +++ b/blink/internal/fixer/rules/method_argument_space_test.go @@ -24,6 +24,7 @@ func TestMethodArgumentSpace(t *testing.T) { {"trailing comma before paren", "