diff --git a/lib/PhpParser/Internal/TokenStream.php b/lib/PhpParser/Internal/TokenStream.php index cf9e00ab..fb4e91e5 100644 --- a/lib/PhpParser/Internal/TokenStream.php +++ b/lib/PhpParser/Internal/TokenStream.php @@ -33,7 +33,7 @@ class TokenStream * @return bool */ public function haveParens(int $startPos, int $endPos) : bool { - return $this->haveTokenImmediativelyBefore($startPos, '(') + return $this->haveTokenImmediatelyBefore($startPos, '(') && $this->haveTokenImmediatelyAfter($endPos, ')'); } @@ -46,7 +46,7 @@ class TokenStream * @return bool */ public function haveBraces(int $startPos, int $endPos) : bool { - return $this->haveTokenImmediativelyBefore($startPos, '{') + return $this->haveTokenImmediatelyBefore($startPos, '{') && $this->haveTokenImmediatelyAfter($endPos, '}'); } @@ -60,7 +60,7 @@ class TokenStream * * @return bool Whether the expected token was found */ - public function haveTokenImmediativelyBefore(int $pos, $expectedTokenType) : bool { + public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType) : bool { $tokens = $this->tokens; $pos--; for (; $pos >= 0; $pos--) { diff --git a/lib/PhpParser/PrettyPrinterAbstract.php b/lib/PhpParser/PrettyPrinterAbstract.php index 1a6ba391..6840fcb4 100644 --- a/lib/PhpParser/PrettyPrinterAbstract.php +++ b/lib/PhpParser/PrettyPrinterAbstract.php @@ -706,6 +706,7 @@ abstract class PrettyPrinterAbstract $insertStr = $this->listInsertionMap[$mapKey] ?? null; $beforeFirstKeepOrReplace = true; + $skipRemovedNode = false; $delayedAdd = []; $lastElemIndentLevel = $this->indentLevel; @@ -797,7 +798,7 @@ abstract class PrettyPrinterAbstract $commentStartPos, $itemStartPos, $indentAdjustment); $delayedAdd = []; - } else { + } else if (!$skipRemovedNode) { $result .= $this->origTokens->getTokenCode( $pos, $itemStartPos, $indentAdjustment); } @@ -806,6 +807,9 @@ abstract class PrettyPrinterAbstract // Add new comments $result .= $this->pComments($comments) . $this->nl; } + + // If we had to remove anything, we have done so now. + $skipRemovedNode = false; } elseif ($diffType === DiffElem::TYPE_ADD) { if (null === $insertStr) { // We don't have insertion information for this list type @@ -839,18 +843,42 @@ abstract class PrettyPrinterAbstract $result .= $insertStr; } } elseif ($diffType === DiffElem::TYPE_REMOVE) { - if ($i === 0) { - // TODO Handle removal at the start - return null; - } - if (!$origArrItem instanceof Node) { // We only support removal for nodes return null; } + $itemStartPos = $origArrItem->getStartTokenPos(); $itemEndPos = $origArrItem->getEndTokenPos(); - \assert($itemEndPos >= 0); + \assert($itemStartPos >= 0 && $itemEndPos >= 0); + + // Consider comments part of the node. + $origComments = $origArrItem->getComments(); + if ($origComments) { + $itemStartPos = $origComments[0]->getStartTokenPos(); + } + + if ($i === 0) { + // If we're removing from the start, keep the tokens before the node and drop those after it, + // instead of the other way around. + $result .= $this->origTokens->getTokenCode( + $pos, $itemStartPos, $indentAdjustment); + $skipRemovedNode = true; + + if ($this->origTokens->haveTokenImmediatelyAfter($itemEndPos, '{') + || $this->origTokens->haveTokenImmediatelyAfter($itemEndPos, '}')) { + // We'd remove the brace of a code block. + // TODO: Preserve formatting. + return null; + } + } else { + if ($this->origTokens->haveTokenImmediatelyBefore($itemStartPos, '{') + || $this->origTokens->haveTokenImmediatelyBefore($itemStartPos, '}')) { + // We'd remove the brace of a code block. + // TODO: Preserve formatting. + return null; + } + } $pos = $itemEndPos + 1; continue; @@ -869,6 +897,11 @@ abstract class PrettyPrinterAbstract $pos = $itemEndPos + 1; } + if ($skipRemovedNode) { + // TODO: Support removing single node. + return null; + } + if (!empty($delayedAdd)) { if (!isset($this->emptyListInsertionMap[$mapKey])) { return null; diff --git a/test/code/formatPreservation/basic.test b/test/code/formatPreservation/basic.test index 48db7803..98c80109 100644 --- a/test/code/formatPreservation/basic.test +++ b/test/code/formatPreservation/basic.test @@ -106,7 +106,6 @@ $stmts[2] = $tmp; array_splice($stmts, 0, 1, []); ----- <?php - function test() { call2( $foo diff --git a/test/code/formatPreservation/listRemoval.test b/test/code/formatPreservation/listRemoval.test index c1240a06..9f6396bf 100644 --- a/test/code/formatPreservation/listRemoval.test +++ b/test/code/formatPreservation/listRemoval.test @@ -49,4 +49,66 @@ array_pop($stmts[0]->returnType->types); ----- <?php function test(): A -|B {} \ No newline at end of file +|B {} +----- +<?php $a; $b; $c; +----- +array_splice($stmts, 0, 1, []); +----- +<?php $b; $c; +----- +<?php $a; $b; $c; +----- +array_splice($stmts, 0, 2, []); +----- +<?php $c; +----- +<?php +{ $x; } +$y; +----- +array_splice($stmts, 0, 1, []); +----- +<?php + +$y; +----- +<?php +$x; +{ $y; } +----- +array_splice($stmts, 0, 1, []); +----- +<?php + +$y; +----- +<?php +$x; +{ $y; } +----- +array_pop($stmts); +----- +<?php + +$x; +----- +<?php +{ $x; } +$y; +----- +array_pop($stmts); +----- +<?php + +$x; +----- +<?php +// Foo +$x; +$y; +----- +array_splice($stmts, 0, 1, []); +----- +<?php +$y; \ No newline at end of file