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
21 changes: 20 additions & 1 deletion blink/internal/fixer/rules/array_notation.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,17 @@ func topBracketIsArray(stack []string) bool {
return len(stack) > 0 && stack[len(stack)-1] == "["
}

// isDestructuringAssignOpen reports whether the "[" at open is a short-list
// destructuring target - its matching "]" is directly followed by a single "=".
func isDestructuringAssignOpen(s *tokens.Stream, open int) bool {
c := s.MatchForward(open)
if c < 0 {
return false
}
n := nextSignificantIndex(s, c)
return n >= 0 && s.At(n).Kind == token.Punct && s.At(n).Value == "="
}

// PHP-CS-Fixer: https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/src/Fixer/ArrayNotation/NoWhitespaceBeforeCommaInArrayFixer.php
//
// NoWhitespaceBeforeCommaInArray removes single-line whitespace before a comma
Expand Down Expand Up @@ -340,7 +351,15 @@ func (f WhitespaceAfterCommaInArray) Fix(s *tokens.Stream) bool {
continue
}
switch t.Value {
case "(", "[", "{":
case "[":
// a destructuring target ("[$a,$b] = ...", or nested in one) is not an
// array literal, so php-cs-fixer does not space its commas
if (len(stack) > 0 && stack[len(stack)-1] == "d[") || isDestructuringAssignOpen(s, i) {
stack = append(stack, "d[")
} else {
stack = append(stack, "[")
}
case "(", "{":
stack = append(stack, t.Value)
case ")", "]", "}":
if len(stack) > 0 {
Expand Down
3 changes: 2 additions & 1 deletion blink/internal/fixer/rules/constructs.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ func (f NoSpacesInsideParenthesis) Fix(s *tokens.Stream) bool {
for i := 0; i < s.Len(); i++ {
t := s.At(i)
if t.Kind == token.Punct && t.Value == "(" &&
i+1 < s.Len() && s.At(i+1).Kind == token.Whitespace && !hasNewline(s.At(i+1).Value) {
i+1 < s.Len() && s.At(i+1).Kind == token.Whitespace && !hasNewline(s.At(i+1).Value) &&
!commentAt(s, i+2) { // keep the space before a trailing comment
s.RemoveAt(i + 1)
changed = true
}
Expand Down
5 changes: 5 additions & 0 deletions blink/internal/fixer/rules/gen_arrays.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ func isComment(t token.Token) bool {
return t.Kind == token.Comment || t.Kind == token.DocComment
}

// commentAt reports whether index i is in range and holds a comment token.
func commentAt(s *tokens.Stream, i int) bool {
return i >= 0 && i < s.Len() && isComment(s.At(i))
}

func isLineComment(t token.Token) bool {
if t.Kind != token.Comment {
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ func (NoSinglelineWhitespaceBeforeSemicolons) Fix(s *tokens.Stream) bool {
}
prev := s.At(i - 1)
if prev.Kind == token.Whitespace && !strings.ContainsAny(prev.Value, "\n\r") {
// keep the space before a ";" that follows another ";" - the empty
// condition of a for() header ("for ($i = 0; ; ++$i)")
if p, ok := prevSignificant(s, i-1); ok && p.Kind == token.Punct && p.Value == ";" {
continue
}
s.RemoveAt(i - 1)
changed = true
}
Expand Down
7 changes: 4 additions & 3 deletions blink/internal/fixer/rules/rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ func TestSpaceAfterSemicolon(t *testing.T) {
if want := "<?php $a=1; $b=2;"; !changed || got != want {
t.Fatalf("changed=%v got=%q want=%q", changed, got, want)
}
// empty for-loop head must not gain spaces
if _, changed := apply(t, SpaceAfterSemicolon{}, "<?php for(;;){}"); changed {
t.Fatal("';' before ')' should be left alone")
// a ";" followed by ")" is left alone, but one followed by ";" (empty for
// condition) still gains a space, matching php-cs-fixer
if got, changed := apply(t, SpaceAfterSemicolon{}, "<?php for(;;){}"); !changed || got != "<?php for(; ;){}" {
t.Fatalf("empty for head: changed=%v got=%q", changed, got)
}
}

Expand Down
4 changes: 3 additions & 1 deletion blink/internal/fixer/rules/space_after_semicolon.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ func (f SpaceAfterSemicolon) Fix(s *tokens.Stream) bool {
if next.Kind == token.Whitespace || next.Kind == token.CloseTag {
continue
}
if next.Kind == token.Punct && (next.Value == ")" || next.Value == ";") {
// a space is still inserted before a following ";" (empty for condition:
// "for (;; )" -> "for (; ; )"); only ")" suppresses it
if next.Kind == token.Punct && next.Value == ")" {
continue
}
s.InsertAt(i+1, token.Token{Kind: token.Whitespace, Value: " "})
Expand Down
7 changes: 5 additions & 2 deletions blink/internal/fixer/rules/token_batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,14 @@ func (TrimArraySpaces) Fix(s *tokens.Stream) bool {
if closeIdx < 0 {
continue
}
if closeIdx-1 > i && s.At(closeIdx-1).Kind == token.Whitespace && !hasNewline(s.At(closeIdx-1).Value) {
// a space kept next to a comment is left alone ("[ // note" / "/* note */ ]")
if closeIdx-1 > i && s.At(closeIdx-1).Kind == token.Whitespace && !hasNewline(s.At(closeIdx-1).Value) &&
!commentAt(s, closeIdx-2) {
s.RemoveAt(closeIdx - 1)
changed = true
}
if i+1 < s.Len() && s.At(i+1).Kind == token.Whitespace && !hasNewline(s.At(i+1).Value) {
if i+1 < s.Len() && s.At(i+1).Kind == token.Whitespace && !hasNewline(s.At(i+1).Value) &&
!commentAt(s, i+2) {
s.RemoveAt(i + 1)
changed = true
}
Expand Down
3 changes: 2 additions & 1 deletion blink/internal/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ func (l *lexer) lexPHP() {
l.lexString(start, '\'')
case c == '"':
l.lexString(start, '"')
case isDigit(c):
case isDigit(c) || (c == '.' && l.pos+1 < len(l.src) && isDigit(l.src[l.pos+1])):
// a leading-dot float (".1") is one DNUMBER in PHP's context-free scanner
for l.pos < len(l.src) && isNumber(l.src[l.pos]) {
l.pos++
}
Expand Down
Loading