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
12 changes: 9 additions & 3 deletions blink/internal/fixer/rules/binary_operator_spaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ var binaryOperators = map[string]bool{
"&&": true, "||": true, "??": true, "=>": true,
}

// compoundAssignmentOperators are always binary (never unary), so they follow the
// default spacing like "=" without needing an explicit operators override.
var compoundAssignmentOperators = map[string]bool{
"+=": true, "-=": true, "*=": true, "/=": true, ".=": true, "%=": true,
"**=": true, "??=": true, "&=": true, "|=": true, "^=": true, "<<=": true, ">>=": true,
}

// BinaryOperatorSpaces normalizes spacing around binary operators (assignment
// "=", arrow "=>", comparison and logical operators). The zero value applies a
// single space to the default operator set. Whitespace spanning a newline is left
Expand Down Expand Up @@ -203,16 +210,15 @@ func binaryOperatorSpacesDefaultTarget(s *tokens.Stream, i int) bool {
// keeps the variable); only declare(...) headers are left alone
return !insideDeclareArgs(s, i)
}
return binaryOperators[t.Value]
return binaryOperators[t.Value] || compoundAssignmentOperators[t.Value]
}

// binaryOperatorSpacesExtraTarget covers operators outside the default set that
// may only be spaced when explicitly configured through the operators option.
func binaryOperatorSpacesExtraTarget(s *tokens.Stream, i int) bool {
t := s.At(i)
switch t.Value {
case "+=", "-=", "*=", "/=", ".=", "%=", "**=", "??=", "&=", "|=", "^=", "<<=", ">>=",
"**", "/", "%", "^", "<<", ">>":
case "**", "/", "%", "^", "<<", ">>":
return true
case "+", "-", "*":
prev, ok := prevSignificant(s, i)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ func TestBinaryOperatorSpacesConfigCases(t *testing.T) {
{"align unsupported is a no-op", BinaryOperatorSpaces{}.WithConfig(map[string]any{"default": "align"}).(fixerRule), "<?php $a=1;", "<?php $a=1;"},
{"nil default leaves rest alone", BinaryOperatorSpaces{}.WithConfig(map[string]any{"default": nil, "operators": map[string]any{"=": "single_space"}}).(fixerRule), "<?php $a=[1=>2];", "<?php $a = [1=>2];"},
{"extra operator via map", BinaryOperatorSpaces{}.WithConfig(map[string]any{"operators": map[string]any{"+=": "single_space"}}).(fixerRule), "<?php $a+=1;", "<?php $a += 1;"},
{"compound concat assignment spaced by default", BinaryOperatorSpaces{}, "<?php $a .='c';", "<?php $a .= 'c';"},
{"compound add assignment spaced by default", BinaryOperatorSpaces{}, "<?php $a +=1;", "<?php $a += 1;"},
{"compound assignment honors no_space default", BinaryOperatorSpaces{}.WithConfig(map[string]any{"default": "no_space"}).(fixerRule), "<?php $a .= 'c';", "<?php $a.='c';"},
{"collapse aligned arrow inside attribute", BinaryOperatorSpaces{}, "<?php #[X([\n 'a' => 1,\n 'bb' => 2,\n])]\nclass C {}", "<?php #[X([\n 'a' => 1,\n 'bb' => 2,\n])]\nclass C {}"},
{"arrow inside attribute string left alone", BinaryOperatorSpaces{}, "<?php #[X('a => b')]\nclass C {}", "<?php #[X('a => b')]\nclass C {}"},
{"reference assignment is spaced around =", BinaryOperatorSpaces{}, "<?php $a =&$b;", "<?php $a = &$b;"},
Expand Down
Loading