Skip to content

Commit 43fb992

Browse files
authored
Blink parity: semicolon, bracket-comment, float, destructuring (#130)
1 parent b1d842b commit 43fb992

8 files changed

Lines changed: 46 additions & 9 deletions

File tree

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,17 @@ func topBracketIsArray(stack []string) bool {
250250
return len(stack) > 0 && stack[len(stack)-1] == "["
251251
}
252252

253+
// isDestructuringAssignOpen reports whether the "[" at open is a short-list
254+
// destructuring target - its matching "]" is directly followed by a single "=".
255+
func isDestructuringAssignOpen(s *tokens.Stream, open int) bool {
256+
c := s.MatchForward(open)
257+
if c < 0 {
258+
return false
259+
}
260+
n := nextSignificantIndex(s, c)
261+
return n >= 0 && s.At(n).Kind == token.Punct && s.At(n).Value == "="
262+
}
263+
253264
// PHP-CS-Fixer: https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/src/Fixer/ArrayNotation/NoWhitespaceBeforeCommaInArrayFixer.php
254265
//
255266
// NoWhitespaceBeforeCommaInArray removes single-line whitespace before a comma
@@ -340,7 +351,15 @@ func (f WhitespaceAfterCommaInArray) Fix(s *tokens.Stream) bool {
340351
continue
341352
}
342353
switch t.Value {
343-
case "(", "[", "{":
354+
case "[":
355+
// a destructuring target ("[$a,$b] = ...", or nested in one) is not an
356+
// array literal, so php-cs-fixer does not space its commas
357+
if (len(stack) > 0 && stack[len(stack)-1] == "d[") || isDestructuringAssignOpen(s, i) {
358+
stack = append(stack, "d[")
359+
} else {
360+
stack = append(stack, "[")
361+
}
362+
case "(", "{":
344363
stack = append(stack, t.Value)
345364
case ")", "]", "}":
346365
if len(stack) > 0 {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ func (f NoSpacesInsideParenthesis) Fix(s *tokens.Stream) bool {
261261
for i := 0; i < s.Len(); i++ {
262262
t := s.At(i)
263263
if t.Kind == token.Punct && t.Value == "(" &&
264-
i+1 < s.Len() && s.At(i+1).Kind == token.Whitespace && !hasNewline(s.At(i+1).Value) {
264+
i+1 < s.Len() && s.At(i+1).Kind == token.Whitespace && !hasNewline(s.At(i+1).Value) &&
265+
!commentAt(s, i+2) { // keep the space before a trailing comment
265266
s.RemoveAt(i + 1)
266267
changed = true
267268
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ func isComment(t token.Token) bool {
145145
return t.Kind == token.Comment || t.Kind == token.DocComment
146146
}
147147

148+
// commentAt reports whether index i is in range and holds a comment token.
149+
func commentAt(s *tokens.Stream, i int) bool {
150+
return i >= 0 && i < s.Len() && isComment(s.At(i))
151+
}
152+
148153
func isLineComment(t token.Token) bool {
149154
if t.Kind != token.Comment {
150155
return false

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ func (NoSinglelineWhitespaceBeforeSemicolons) Fix(s *tokens.Stream) bool {
3030
}
3131
prev := s.At(i - 1)
3232
if prev.Kind == token.Whitespace && !strings.ContainsAny(prev.Value, "\n\r") {
33+
// keep the space before a ";" that follows another ";" - the empty
34+
// condition of a for() header ("for ($i = 0; ; ++$i)")
35+
if p, ok := prevSignificant(s, i-1); ok && p.Kind == token.Punct && p.Value == ";" {
36+
continue
37+
}
3338
s.RemoveAt(i - 1)
3439
changed = true
3540
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ func TestSpaceAfterSemicolon(t *testing.T) {
3333
if want := "<?php $a=1; $b=2;"; !changed || got != want {
3434
t.Fatalf("changed=%v got=%q want=%q", changed, got, want)
3535
}
36-
// empty for-loop head must not gain spaces
37-
if _, changed := apply(t, SpaceAfterSemicolon{}, "<?php for(;;){}"); changed {
38-
t.Fatal("';' before ')' should be left alone")
36+
// a ";" followed by ")" is left alone, but one followed by ";" (empty for
37+
// condition) still gains a space, matching php-cs-fixer
38+
if got, changed := apply(t, SpaceAfterSemicolon{}, "<?php for(;;){}"); !changed || got != "<?php for(; ;){}" {
39+
t.Fatalf("empty for head: changed=%v got=%q", changed, got)
3940
}
4041
}
4142

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ func (f SpaceAfterSemicolon) Fix(s *tokens.Stream) bool {
5656
if next.Kind == token.Whitespace || next.Kind == token.CloseTag {
5757
continue
5858
}
59-
if next.Kind == token.Punct && (next.Value == ")" || next.Value == ";") {
59+
// a space is still inserted before a following ";" (empty for condition:
60+
// "for (;; )" -> "for (; ; )"); only ")" suppresses it
61+
if next.Kind == token.Punct && next.Value == ")" {
6062
continue
6163
}
6264
s.InsertAt(i+1, token.Token{Kind: token.Whitespace, Value: " "})

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,14 @@ func (TrimArraySpaces) Fix(s *tokens.Stream) bool {
118118
if closeIdx < 0 {
119119
continue
120120
}
121-
if closeIdx-1 > i && s.At(closeIdx-1).Kind == token.Whitespace && !hasNewline(s.At(closeIdx-1).Value) {
121+
// a space kept next to a comment is left alone ("[ // note" / "/* note */ ]")
122+
if closeIdx-1 > i && s.At(closeIdx-1).Kind == token.Whitespace && !hasNewline(s.At(closeIdx-1).Value) &&
123+
!commentAt(s, closeIdx-2) {
122124
s.RemoveAt(closeIdx - 1)
123125
changed = true
124126
}
125-
if i+1 < s.Len() && s.At(i+1).Kind == token.Whitespace && !hasNewline(s.At(i+1).Value) {
127+
if i+1 < s.Len() && s.At(i+1).Kind == token.Whitespace && !hasNewline(s.At(i+1).Value) &&
128+
!commentAt(s, i+2) {
126129
s.RemoveAt(i + 1)
127130
changed = true
128131
}

‎blink/internal/lexer/lexer.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ func (l *lexer) lexPHP() {
137137
l.lexString(start, '\'')
138138
case c == '"':
139139
l.lexString(start, '"')
140-
case isDigit(c):
140+
case isDigit(c) || (c == '.' && l.pos+1 < len(l.src) && isDigit(l.src[l.pos+1])):
141+
// a leading-dot float (".1") is one DNUMBER in PHP's context-free scanner
141142
for l.pos < len(l.src) && isNumber(l.src[l.pos]) {
142143
l.pos++
143144
}

0 commit comments

Comments
 (0)