From 6586403fd1286225a6463a4930204c90952b0827 Mon Sep 17 00:00:00 2001 From: Luke Arms Date: Sun, 24 Nov 2024 22:53:10 +1100 Subject: [PATCH 1/6] Update VS Code return priority snippet [no ci] --- .vscode/php.code-snippets | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/.vscode/php.code-snippets b/.vscode/php.code-snippets index e312e4dc..e0b6e73a 100644 --- a/.vscode/php.code-snippets +++ b/.vscode/php.code-snippets @@ -99,20 +99,17 @@ }, "Return priority": { "scope": "php", - "prefix": "switch", + "prefix": "return", "body": [ - "switch (\\$method) {", - " case self::PROCESS_TOKENS:", - " case self::PROCESS_LIST:", - " case self::PROCESS_BLOCK:", - " return null;", - "", - " case self::BEFORE_RENDER:", - " return null;", - "", - " default:", - " return null;", - "}", + "return [", + " self::PROCESS_TOKENS => null,", + " self::PROCESS_STATEMENTS => null,", + " self::PROCESS_DECLARATIONS => null,", + " self::PROCESS_LIST => null,", + " self::PROCESS_BLOCK => null,", + " self::CALLBACK => null,", + " self::BEFORE_RENDER => null,", + "][\\$method] ?? null;", ] }, "Namespace": { From 29904ca69829758a1f44b13276c08c3ff482553b Mon Sep 17 00:00:00 2001 From: Luke Arms Date: Mon, 25 Nov 2024 15:46:10 +1100 Subject: [PATCH 2/6] Improve token whitespace handling - Reduce whitespace-related `Token` properties from multiple 3-bit masks (8 in total) to one 24-bit mask - This change is expected to reduce memory consumption significantly when formatting large documents - Replace `WhitespaceType` with `WhitespaceFlag` and import it as `Space` for compactness - Simplify `TokenCollection` methods for applying whitespace changes, reducing them to `applyWhitespace()` and `applyInnerWhitespace()` - Refactor as needed --- phpstan-baseline-7.4.neon | 102 ---------------- phpstan-baseline-8.4.neon | 102 ---------------- src/Catalog/WhitespaceFlag.php | 68 +++++++++++ src/Catalog/WhitespaceType.php | 39 ------- src/Concern/RuleTrait.php | 15 +-- src/Formatter.php | 10 +- src/Internal/TokenCollection.php | 118 ++++--------------- src/Renderer.php | 6 +- src/Rule/AlignChains.php | 5 +- src/Rule/ControlStructureSpacing.php | 21 ++-- src/Rule/DeclarationSpacing.php | 26 ++--- src/Rule/EssentialWhitespace.php | 12 +- src/Rule/IndexSpacing.php | 17 ++- src/Rule/ListSpacing.php | 18 +-- src/Rule/OperatorSpacing.php | 30 ++--- src/Rule/PlaceBraces.php | 30 +++-- src/Rule/PlaceComments.php | 33 +++--- src/Rule/PreserveNewlines.php | 22 ++-- src/Rule/Preset/Drupal.php | 24 +--- src/Rule/Preset/Laravel.php | 15 +-- src/Rule/Preset/Symfony.php | 13 +-- src/Rule/Preset/WordPress.php | 37 ++---- src/Rule/ProtectStrings.php | 13 ++- src/Rule/StandardWhitespace.php | 38 +++--- src/Rule/StatementSpacing.php | 10 +- src/Rule/StrictExpressions.php | 13 +-- src/Rule/StrictLists.php | 6 +- src/Rule/SwitchIndentation.php | 7 +- src/Rule/VerticalWhitespace.php | 25 ++-- src/Token.php | 168 ++++++++++++++------------- src/TokenUtil.php | 52 ++++++--- 31 files changed, 392 insertions(+), 703 deletions(-) create mode 100644 src/Catalog/WhitespaceFlag.php delete mode 100644 src/Catalog/WhitespaceType.php diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon index c72e864c..a84c39a5 100644 --- a/phpstan-baseline-7.4.neon +++ b/phpstan-baseline-7.4.neon @@ -24,36 +24,6 @@ parameters: count: 1 path: src/Formatter.php - - - message: '#^Cannot access property \$CriticalWhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Internal/TokenCollection.php - - - - message: '#^Cannot access property \$CriticalWhitespaceMaskPrev on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Internal/TokenCollection.php - - - - message: '#^Cannot access property \$WhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Internal/TokenCollection.php - - - - message: '#^Cannot access property \$WhitespaceMaskPrev on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Internal/TokenCollection.php - - - - message: '#^Cannot call method collect\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: method.nonObject - count: 2 - path: src/Internal/TokenCollection.php - - message: '#^Parameter \#1 \$to of method Lkrms\\PrettyPHP\\Token\:\:collect\(\) expects Lkrms\\PrettyPHP\\Token, Lkrms\\PrettyPHP\\Token\|null given\.$#' identifier: argument.type @@ -150,12 +120,6 @@ parameters: count: 1 path: src/Rule/ControlStructureSpacing.php - - - message: '#^Cannot access property \$WhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 2 - path: src/Rule/ControlStructureSpacing.php - - message: '#^Cannot access property \$LinePadding on Lkrms\\PrettyPHP\\Token\|null\.$#' identifier: property.nonObject @@ -174,12 +138,6 @@ parameters: count: 2 path: src/Rule/HeredocIndentation.php - - - message: '#^Cannot access property \$WhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/PlaceComments.php - - message: '#^Cannot call method collect\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' identifier: method.nonObject @@ -192,72 +150,12 @@ parameters: count: 1 path: src/Rule/PreserveNewlines.php - - - message: '#^Cannot access property \$WhitespaceMaskPrev on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StatementSpacing.php - - message: '#^Cannot access property \$id on Lkrms\\PrettyPHP\\Token\|null\.$#' identifier: property.nonObject count: 3 path: src/Rule/StatementSpacing.php - - - message: '#^Cannot access property \$ClosedBy on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$NextCode on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$PrevCode on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$WhitespaceAfter on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$WhitespaceBefore on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$WhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 2 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$WhitespaceMaskPrev on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 2 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot call method collect\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot call method hasNewlineAfter\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - message: '#^Cannot call method hasNewlineBefore\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' identifier: method.nonObject diff --git a/phpstan-baseline-8.4.neon b/phpstan-baseline-8.4.neon index 1212893b..8792d5a7 100644 --- a/phpstan-baseline-8.4.neon +++ b/phpstan-baseline-8.4.neon @@ -18,36 +18,6 @@ parameters: count: 1 path: src/Filter/RemoveHeredocIndentation.php - - - message: '#^Cannot access property \$CriticalWhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Internal/TokenCollection.php - - - - message: '#^Cannot access property \$CriticalWhitespaceMaskPrev on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Internal/TokenCollection.php - - - - message: '#^Cannot access property \$WhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Internal/TokenCollection.php - - - - message: '#^Cannot access property \$WhitespaceMaskPrev on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Internal/TokenCollection.php - - - - message: '#^Cannot call method collect\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: method.nonObject - count: 2 - path: src/Internal/TokenCollection.php - - message: '#^Parameter \#1 \$to of method Lkrms\\PrettyPHP\\Token\:\:collect\(\) expects Lkrms\\PrettyPHP\\Token, Lkrms\\PrettyPHP\\Token\|null given\.$#' identifier: argument.type @@ -144,12 +114,6 @@ parameters: count: 1 path: src/Rule/ControlStructureSpacing.php - - - message: '#^Cannot access property \$WhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 2 - path: src/Rule/ControlStructureSpacing.php - - message: '#^Cannot access property \$LinePadding on Lkrms\\PrettyPHP\\Token\|null\.$#' identifier: property.nonObject @@ -168,12 +132,6 @@ parameters: count: 2 path: src/Rule/HeredocIndentation.php - - - message: '#^Cannot access property \$WhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/PlaceComments.php - - message: '#^Cannot call method collect\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' identifier: method.nonObject @@ -186,72 +144,12 @@ parameters: count: 1 path: src/Rule/PreserveNewlines.php - - - message: '#^Cannot access property \$WhitespaceMaskPrev on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StatementSpacing.php - - message: '#^Cannot access property \$id on Lkrms\\PrettyPHP\\Token\|null\.$#' identifier: property.nonObject count: 3 path: src/Rule/StatementSpacing.php - - - message: '#^Cannot access property \$ClosedBy on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$NextCode on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$PrevCode on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$WhitespaceAfter on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$WhitespaceBefore on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$WhitespaceMaskNext on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 2 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot access property \$WhitespaceMaskPrev on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: property.nonObject - count: 2 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot call method collect\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - - - message: '#^Cannot call method hasNewlineAfter\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/Rule/StrictExpressions.php - - message: '#^Cannot call method hasNewlineBefore\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' identifier: method.nonObject diff --git a/src/Catalog/WhitespaceFlag.php b/src/Catalog/WhitespaceFlag.php new file mode 100644 index 00000000..45d82d8e --- /dev/null +++ b/src/Catalog/WhitespaceFlag.php @@ -0,0 +1,68 @@ +collect($end) - ->maskInnerWhitespace($mask, true); + ->applyInnerWhitespace(Space::CRITICAL_NO_BLANK | Space::CRITICAL_NO_LINE); return true; } @@ -76,17 +75,13 @@ private function mirrorBracket( $hasNewlineBeforeNextCode = $open->hasNewlineBeforeNextCode(); } if (!$hasNewlineBeforeNextCode) { - $mask = ~WhitespaceType::BLANK & ~WhitespaceType::LINE; - $close->WhitespaceMaskPrev &= $mask; + $close->Whitespace |= Space::NO_BLANK_BEFORE | Space::NO_LINE_BEFORE; return; } - $close->WhitespaceBefore |= WhitespaceType::LINE; + $close->Whitespace |= Space::LINE_BEFORE; if (!$close->hasNewlineBefore()) { - /** @var Token */ - $prev = $close->Prev; - $close->WhitespaceMaskPrev |= WhitespaceType::LINE; - $prev->WhitespaceMaskNext |= WhitespaceType::LINE; + $close->removeWhitespace(Space::NO_LINE_BEFORE); } } } diff --git a/src/Formatter.php b/src/Formatter.php index 52997bf0..d12ee2b2 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -8,7 +8,7 @@ use Lkrms\PrettyPHP\Catalog\ImportSortOrder; use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Contract\BlockRule; use Lkrms\PrettyPHP\Contract\DeclarationRule; use Lkrms\PrettyPHP\Contract\Extension; @@ -925,7 +925,7 @@ public function format( && $last->Statement && $last->Statement->id !== \T_HALT_COMPILER ) { - $last->WhitespaceAfter |= WhitespaceType::LINE; + $last->Whitespace |= Space::LINE_AFTER; } } catch (CompileError $ex) { throw new InvalidSyntaxException(sprintf( @@ -1100,11 +1100,11 @@ public function format( $keep = true; while (true) { if ($token && $token->id !== \T_INLINE_HTML) { - $before = $token->effectiveWhitespaceBefore(); - if ($before & WhitespaceType::BLANK) { + $before = $token->getWhitespaceBefore(); + if ($before & Space::BLANK) { $endOfBlock = true; $endOfLine = true; - } elseif ($before & WhitespaceType::LINE) { + } elseif ($before & Space::LINE) { $endOfLine = true; } } else { diff --git a/src/Internal/TokenCollection.php b/src/Internal/TokenCollection.php index f07914e1..89d94c5d 100644 --- a/src/Internal/TokenCollection.php +++ b/src/Internal/TokenCollection.php @@ -4,6 +4,7 @@ use Lkrms\PrettyPHP\Token; use Salient\Collection\AbstractTypedList; +use InvalidArgumentException; use LogicException; use Stringable; @@ -286,126 +287,53 @@ public function toString(string $delimiter = ''): string /** * @return $this */ - public function addWhitespaceBefore(int $type, bool $critical = false) + public function applyWhitespace(int $whitespace) { - if ($critical) { - /** @var Token $token */ - foreach ($this as $token) { - $token->CriticalWhitespaceBefore |= $type; - } - return $this; - } + // Shift *_BEFORE and *_AFTER to their NO_* counterparts, then clear + // other bits + $remove = $whitespace << 6 & 0b111111000000; /** @var Token $token */ foreach ($this as $token) { - $token->WhitespaceBefore |= $type; - $token->WhitespaceMaskPrev |= $type; - if ($token->Prev) { - $token->Prev->WhitespaceMaskNext |= $type; + $token->Whitespace |= $whitespace; + if ($remove) { + // @phpstan-ignore argument.type + $token->removeWhitespace($remove); } } + return $this; } /** * @return $this */ - public function addWhitespaceAfter(int $type, bool $critical = false) + public function applyInnerWhitespace(int $whitespace) { - if ($critical) { - /** @var Token $token */ - foreach ($this as $token) { - $token->CriticalWhitespaceAfter |= $type; - } - return $this; - } + $this->assertCollected(); - /** @var Token $token */ - foreach ($this as $token) { - $token->WhitespaceAfter |= $type; - $token->WhitespaceMaskNext |= $type; - if ($token->Next) { - $token->Next->WhitespaceMaskPrev |= $type; - } + if (($whitespace & 0b0111000111000111000111) !== $whitespace) { + throw new InvalidArgumentException('Invalid $whitespace (AFTER bits cannot be set)'); } - return $this; - } - /** - * Use T_AND_EQUAL ('&=') to apply a mask to all WhitespaceMaskPrev and - * WhitespaceMaskNext values that cover whitespace before tokens in the - * collection - * - * If `$critical` is set, operate on CriticalWhitespaceMaskPrev and - * CriticalWhitespaceMaskNext instead. - * - * @return $this - */ - public function maskWhitespaceBefore(int $mask, bool $critical = false) - { - if ($critical) { - /** @var Token $token */ - foreach ($this as $token) { - $token->CriticalWhitespaceMaskPrev &= $mask; - if ($token->Prev) { - $token->Prev->CriticalWhitespaceMaskNext &= $mask; - } - } + if ($this->count() < 2) { return $this; } + $remove = $whitespace << 6 & 0b111111000000; + + $i = 0; /** @var Token $token */ foreach ($this as $token) { - $token->WhitespaceMaskPrev &= $mask; - if ($token->Prev) { - $token->Prev->WhitespaceMaskNext &= $mask; - } - } - return $this; - } - - /** - * Use T_AND_EQUAL ('&=') to apply a mask to all inward-facing - * WhitespaceMaskPrev and WhitespaceMaskNext values in the collection - * - * If `$critical` is set, operate on CriticalWhitespaceMaskPrev and - * CriticalWhitespaceMaskNext instead. - * - * @return $this - */ - public function maskInnerWhitespace(int $mask, bool $critical = false) - { - $this->assertCollected(); - - $count = $this->count(); - if ($count < 2) { - return $this; - } - - if ($critical) { - if ($count > 2) { - foreach ($this->nth(2)->collect($this->nth(-2)) as $token) { - $token->CriticalWhitespaceMaskPrev &= $mask; - $token->CriticalWhitespaceMaskNext &= $mask; + if ($i++) { + $token->Whitespace |= $whitespace; + if ($remove) { + // @phpstan-ignore argument.type + $token->removeWhitespace($remove); } } - - $this->first()->CriticalWhitespaceMaskNext &= $mask; - $this->last()->CriticalWhitespaceMaskPrev &= $mask; - - return $this; } - if ($count > 2) { - foreach ($this->nth(2)->collect($this->nth(-2)) as $token) { - $token->WhitespaceMaskPrev &= $mask; - $token->WhitespaceMaskNext &= $mask; - } - } - - $this->first()->WhitespaceMaskNext &= $mask; - $this->last()->WhitespaceMaskPrev &= $mask; - return $this; } diff --git a/src/Renderer.php b/src/Renderer.php index 002e7f73..868649e8 100644 --- a/src/Renderer.php +++ b/src/Renderer.php @@ -133,7 +133,7 @@ public function renderWhitespaceBefore( $before = ''; $padding = $token->Padding; - if ($whitespace = $token->effectiveWhitespaceBefore()) { + if ($whitespace = $token->getWhitespaceBefore()) { $before = TokenUtil::getWhitespace($whitespace); if ($before[0] === "\n") { $indent = $token->indent(); @@ -205,7 +205,7 @@ public function renderWhitespaceAfter(Token $token): string return ''; } - return TokenUtil::getWhitespace($token->effectiveWhitespaceAfter()); + return TokenUtil::getWhitespace($token->getWhitespaceAfter()); } /** @@ -328,7 +328,7 @@ private function getMultiLineComment(Token $token, bool $softTabs): string $indent = "\n" . ltrim($beforeStart, "\n") . str_repeat(' ', mb_strlen($this->render($start, $token->Prev, $softTabs)) - strlen($beforeStart) - + strlen(TokenUtil::getWhitespace($token->effectiveWhitespaceBefore())) + + strlen(TokenUtil::getWhitespace($token->getWhitespaceBefore())) + $token->Padding); } $text = str_replace("\n", $indent, $token->text); diff --git a/src/Rule/AlignChains.php b/src/Rule/AlignChains.php index 31b47702..b65bf2cd 100644 --- a/src/Rule/AlignChains.php +++ b/src/Rule/AlignChains.php @@ -4,7 +4,7 @@ use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Internal\TokenCollection; @@ -81,8 +81,7 @@ public function processTokens(array $tokens): void && $eol->Next === $token && mb_strlen($alignWith->collect($eol)->render()) <= $this->Formatter->TabSize ) { - $token->WhitespaceBefore = WhitespaceType::NONE; - $token->WhitespaceMaskPrev = WhitespaceType::NONE; + $token->Whitespace |= Space::NONE_BEFORE; $alignWith = null; } else { // Safe because $alignWith->text can't have newlines diff --git a/src/Rule/ControlStructureSpacing.php b/src/Rule/ControlStructureSpacing.php index cc95d702..196c1cbe 100644 --- a/src/Rule/ControlStructureSpacing.php +++ b/src/Rule/ControlStructureSpacing.php @@ -3,7 +3,7 @@ namespace Lkrms\PrettyPHP\Rule; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\TokenTypeIndex; @@ -74,16 +74,12 @@ public function processTokens(array $tokens): void if (!$token->PrevCode || $token->PrevCode->id !== \T_CLOSE_BRACE || !$token->continuesControlStructure()) { - $token->WhitespaceBefore |= WhitespaceType::LINE; - $token->WhitespaceMaskPrev |= WhitespaceType::LINE; - $token->Prev->WhitespaceMaskNext |= WhitespaceType::LINE; + $token->applyWhitespace(Space::LINE_BEFORE); } // Add newlines and suppress blank lines before unenclosed bodies - $body->WhitespaceBefore |= WhitespaceType::LINE | WhitespaceType::SPACE; - $body->WhitespaceMaskPrev |= WhitespaceType::LINE; - $body->WhitespaceMaskPrev &= ~WhitespaceType::BLANK; - $body->Prev->WhitespaceMaskNext |= WhitespaceType::LINE; + $body->Whitespace |= Space::NO_BLANK_BEFORE | Space::LINE_BEFORE | Space::SPACE_BEFORE; + $body->removeWhitespace(Space::NO_LINE_BEFORE); // Find the last token in the body $end = null; @@ -114,16 +110,13 @@ public function processTokens(array $tokens): void } // Add a newline after the body - $end->WhitespaceAfter |= WhitespaceType::LINE | WhitespaceType::SPACE; - $end->WhitespaceMaskNext |= WhitespaceType::LINE; - if ($end->Next) { - $end->Next->WhitespaceMaskPrev |= WhitespaceType::LINE; - } + $end->Whitespace |= Space::LINE_AFTER | Space::SPACE_AFTER; + $end->removeWhitespace(Space::NO_LINE_AFTER); // If the control structure continues, suppress blank lines after // the body if ($continues) { - $end->WhitespaceMaskNext &= ~WhitespaceType::BLANK; + $end->Whitespace |= Space::NO_BLANK_AFTER; } if (!$this->Formatter->DetectProblems) { diff --git a/src/Rule/DeclarationSpacing.php b/src/Rule/DeclarationSpacing.php index 0cdb9f6a..cd97e9cd 100644 --- a/src/Rule/DeclarationSpacing.php +++ b/src/Rule/DeclarationSpacing.php @@ -5,7 +5,7 @@ use Lkrms\PrettyPHP\Catalog\DeclarationType as Type; use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\DeclarationRuleTrait; use Lkrms\PrettyPHP\Contract\DeclarationRule; use Lkrms\PrettyPHP\Filter\SortImports; @@ -167,7 +167,7 @@ public function processDeclarations(array $declarations): void unset($declarations[$token->Index]); $prevIsMultiLine = false; - $masked = false; + $applied = false; // Suppress blank lines between `use` statements, one-line // `declare` statements, and property hooks not declared over @@ -177,9 +177,9 @@ public function processDeclarations(array $declarations): void && !$this->isMultiLine($prev) && !$this->isMultiLine($token) )) { - $prevEnd->collect($token)->maskWhitespaceBefore(~WhitespaceType::BLANK); + $prevEnd->collect($token)->applyWhitespace(Space::NO_BLANK_BEFORE); $expand = false; - $masked = true; + $applied = true; } elseif ( // Apply "loose" spacing to multi-line declarations $this->hasDocComment($token) @@ -218,7 +218,7 @@ public function processDeclarations(array $declarations): void $expand = $nextExpand; } - if (!$expand && !$masked && ( + if (!$expand && !$applied && ( // Don't suppress blank lines between declarations with // different modifiers, e.g. preserve the blank line before // `private const` here: @@ -253,17 +253,17 @@ public function processDeclarations(array $declarations): void $group[] = $modifiers; $prevModifiers = $modifiers; - $token->WhitespaceBefore |= WhitespaceType::LINE; + $token->Whitespace |= Space::LINE_BEFORE; // Collapse DocBlocks and suppress blank lines before DocBlocks // above tightly-spaced declarations $this->maybeCollapseComment($token); - if ($masked) { + if ($applied) { continue; } - $token->WhitespaceMaskPrev &= ~WhitespaceType::BLANK; + $token->Whitespace |= Space::NO_BLANK_BEFORE; if ($token->Prev && $token->Prev->id === \T_DOC_COMMENT) { - $token->Prev->WhitespaceMaskPrev &= ~WhitespaceType::BLANK; + $token->Prev->Whitespace |= Space::NO_BLANK_BEFORE; } } @@ -278,7 +278,7 @@ public function processDeclarations(array $declarations): void || !($prevEnd->Next->Flags & TokenFlag::CODE) ) ) { - $prevEnd->WhitespaceAfter |= WhitespaceType::BLANK; + $prevEnd->Whitespace |= Space::BLANK_AFTER; } if ($nextExpand) { @@ -433,7 +433,7 @@ private function maybeCollapseComment(Token $token): void } } - private function maybeApplyBlankLineBefore(Token $token, bool $withMask = false): void + private function maybeApplyBlankLineBefore(Token $token, bool $force = false): void { $this->Declarations[$token->Index][3] = null; $this->Declarations[$token->Index][4] = null; @@ -444,10 +444,10 @@ private function maybeApplyBlankLineBefore(Token $token, bool $withMask = false) && $token->OpenTag && $token->OpenTag->NextCode === $token ) { - $token->WhitespaceBefore |= WhitespaceType::LINE; + $token->Whitespace |= Space::LINE_BEFORE; return; } - $token->applyBlankLineBefore($withMask); + $token->applyBlankLineBefore($force); } } diff --git a/src/Rule/EssentialWhitespace.php b/src/Rule/EssentialWhitespace.php index 96b014f5..f2debefa 100644 --- a/src/Rule/EssentialWhitespace.php +++ b/src/Rule/EssentialWhitespace.php @@ -3,7 +3,7 @@ namespace Lkrms\PrettyPHP\Rule; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\RuleTrait; use Lkrms\PrettyPHP\Contract\Rule; use Salient\Utility\Regex; @@ -41,13 +41,11 @@ public function beforeRender(array $tokens): void /* Add newlines after one-line comments with no subsequent `?>` */ if ($token->Flags & TokenFlag::ONELINE_COMMENT && $next->id !== \T_CLOSE_TAG) { - $token->WhitespaceAfter |= WhitespaceType::LINE; - $token->WhitespaceMaskNext |= WhitespaceType::LINE; - $next->WhitespaceMaskPrev |= WhitespaceType::LINE; + $token->applyWhitespace(Space::LINE_AFTER); continue; } - if ($token->effectiveWhitespaceAfter() + if ($token->getWhitespaceAfter() || $this->Idx->SuppressSpaceAfter[$token->id] || $this->Idx->SuppressSpaceBefore[$next->id]) { continue; @@ -58,9 +56,7 @@ public function beforeRender(array $tokens): void '/^[a-zA-Z0-9\\\\_\x80-\xff]{2}$/', ($token->text[-1] ?? '') . ($next->text[0] ?? '') )) { - $token->WhitespaceAfter |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext |= WhitespaceType::SPACE; - $next->WhitespaceMaskPrev |= WhitespaceType::SPACE; + $token->applyWhitespace(Space::SPACE_AFTER); } } } diff --git a/src/Rule/IndexSpacing.php b/src/Rule/IndexSpacing.php index b37e3d8a..d16339b8 100644 --- a/src/Rule/IndexSpacing.php +++ b/src/Rule/IndexSpacing.php @@ -3,7 +3,7 @@ namespace Lkrms\PrettyPHP\Rule; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\TokenTypeIndex; @@ -59,12 +59,11 @@ public function processTokens(array $tokens): void foreach ($tokens as $token) { if ($idx->AddSpace[$token->id]) { - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $token->WhitespaceAfter |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_BEFORE | Space::SPACE_AFTER; } elseif ($idx->AddSpaceBefore[$token->id]) { - $token->WhitespaceBefore |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_BEFORE; } elseif ($idx->AddSpaceAfter[$token->id]) { - $token->WhitespaceAfter |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_AFTER; } if ($idx->SuppressSpaceAfter[$token->id] || ( @@ -73,9 +72,9 @@ public function processTokens(array $tokens): void || $token->isMatchOpenBrace() ) )) { - $token->WhitespaceMaskNext &= ~WhitespaceType::BLANK & ~WhitespaceType::SPACE; + $token->Whitespace |= Space::NO_BLANK_AFTER | Space::NO_SPACE_AFTER; } elseif ($token->id === \T_COLON && $token->ClosedBy) { - $token->WhitespaceMaskNext &= ~WhitespaceType::BLANK; + $token->Whitespace |= Space::NO_BLANK_AFTER; } if ($idx->SuppressSpaceBefore[$token->id] || ( @@ -84,9 +83,9 @@ public function processTokens(array $tokens): void || ($token->OpenedBy && $token->OpenedBy->isMatchOpenBrace()) ) )) { - $token->WhitespaceMaskPrev &= ~WhitespaceType::BLANK & ~WhitespaceType::SPACE; + $token->Whitespace |= Space::NO_BLANK_BEFORE | Space::NO_SPACE_BEFORE; } elseif ($token->id === \T_END_ALT_SYNTAX) { - $token->WhitespaceMaskPrev &= ~WhitespaceType::BLANK; + $token->Whitespace |= Space::NO_BLANK_BEFORE; } } } diff --git a/src/Rule/ListSpacing.php b/src/Rule/ListSpacing.php index 50a88d62..44bee7c2 100644 --- a/src/Rule/ListSpacing.php +++ b/src/Rule/ListSpacing.php @@ -4,7 +4,7 @@ use Lkrms\PrettyPHP\Catalog\DeclarationType; use Lkrms\PrettyPHP\Catalog\TokenData; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\DeclarationRuleTrait; use Lkrms\PrettyPHP\Concern\ListRuleTrait; use Lkrms\PrettyPHP\Contract\DeclarationRule; @@ -70,11 +70,7 @@ public function processList(Token $parent, TokenCollection $items): void if (!$this->ListRuleEnabled && $items->tokenHasNewlineBefore()) { /** @var Token */ $token = $items->first(); - /** @var Token */ - $prev = $token->Prev; - $token->WhitespaceBefore |= WhitespaceType::LINE; - $token->WhitespaceMaskPrev |= WhitespaceType::LINE; - $prev->WhitespaceMaskNext |= WhitespaceType::LINE; + $token->applyWhitespace(Space::LINE_BEFORE); } return; } @@ -86,7 +82,7 @@ public function processList(Token $parent, TokenCollection $items): void if ($last->id === \T_COMMA) { $items->copy() ->add($parent->ClosedBy) - ->addWhitespaceBefore(WhitespaceType::LINE, true); + ->applyWhitespace(Space::CRITICAL_LINE_BEFORE); } if ($parent->id === \T_OPEN_PARENTHESIS && $parent->isParameterList()) { @@ -147,14 +143,10 @@ private function normaliseDeclarationList(iterable $items): void ?? $item->withNextSiblingsWhile($this->Idx->Attribute, true); $tokens[] = $item->skipNextSiblingsFrom($this->Idx->Attribute); foreach ($tokens as $token) { - /** @var Token */ - $prev = $token->Prev; - $token->WhitespaceBefore |= WhitespaceType::LINE; - $token->WhitespaceMaskPrev |= WhitespaceType::LINE; - $prev->WhitespaceMaskNext |= WhitespaceType::LINE; + $token->applyWhitespace(Space::LINE_BEFORE); if ($this->Idx->Attribute[$token->id]) { $token = $token->ClosedBy ?? $token; - $token->WhitespaceAfter |= WhitespaceType::LINE; + $token->Whitespace |= Space::LINE_AFTER; // Add a blank line before each item with an attribute, and // another before the next item $addBlankBefore = true; diff --git a/src/Rule/OperatorSpacing.php b/src/Rule/OperatorSpacing.php index 8a8a91cc..2db7a3e1 100644 --- a/src/Rule/OperatorSpacing.php +++ b/src/Rule/OperatorSpacing.php @@ -4,7 +4,7 @@ use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Token; @@ -75,8 +75,7 @@ public function processTokens(array $tokens): void ) ) ) { - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext = WhitespaceType::NONE; + $token->Whitespace |= Space::SPACE_BEFORE | Space::NONE_AFTER; continue; } @@ -100,8 +99,7 @@ public function processTokens(array $tokens): void ) ) ) { - $token->WhitespaceMaskNext = WhitespaceType::NONE; - $token->WhitespaceMaskPrev = WhitespaceType::NONE; + $token->Whitespace |= Space::NONE_BEFORE | Space::NONE_AFTER; if ($inTypeContext) { continue; @@ -112,7 +110,7 @@ public function processTokens(array $tokens): void /** @var Token */ $parent = $token->Parent; if (!$parent->PrevCode || $parent->PrevCode->id !== \T_OR) { - $parent->WhitespaceBefore |= WhitespaceType::SPACE; + $parent->Whitespace |= Space::SPACE_BEFORE; } continue; } @@ -124,8 +122,7 @@ public function processTokens(array $tokens): void && $token->Parent->PrevCode && $token->Parent->PrevCode->id === \T_CATCH && !$this->Formatter->Psr12) { - $token->WhitespaceMaskNext = WhitespaceType::NONE; - $token->WhitespaceMaskPrev = WhitespaceType::NONE; + $token->Whitespace |= Space::NONE_BEFORE | Space::NONE_AFTER; continue; } @@ -134,8 +131,7 @@ public function processTokens(array $tokens): void $token->id === \T_QUESTION && !($token->Flags & TokenFlag::TERNARY_OPERATOR) ) { - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext = WhitespaceType::NONE; + $token->Whitespace |= Space::SPACE_BEFORE | Space::NONE_AFTER; continue; } @@ -143,9 +139,9 @@ public function processTokens(array $tokens): void // operate on if ($token->id === \T_INC || $token->id === \T_DEC) { if ($token->Prev && $this->Idx->EndOfVariable[$token->Prev->id]) { - $token->WhitespaceMaskPrev = WhitespaceType::NONE; + $token->Whitespace |= Space::NONE_BEFORE; } else { - $token->WhitespaceMaskNext = WhitespaceType::NONE; + $token->Whitespace |= Space::NONE_AFTER; } } @@ -155,12 +151,12 @@ public function processTokens(array $tokens): void && $token->Next->Flags & TokenFlag::CODE && (!$this->Idx->Operator[$token->Next->id] || $token->Next->isUnaryOperator())) { - $token->WhitespaceMaskNext = WhitespaceType::NONE; + $token->Whitespace |= Space::NONE_AFTER; continue; } - $token->WhitespaceAfter |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_AFTER; if ( $token->id === \T_COLON @@ -176,13 +172,11 @@ public function processTokens(array $tokens): void ? $token : $token->Data[TokenData::OTHER_TERNARY_OPERATOR]) === $token->Prev ) { - $token->WhitespaceBefore = WhitespaceType::NONE; - $token->Prev->WhitespaceAfter = WhitespaceType::NONE; - + $token->Whitespace |= Space::NONE_BEFORE; continue; } - $token->WhitespaceBefore |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_BEFORE; } } diff --git a/src/Rule/PlaceBraces.php b/src/Rule/PlaceBraces.php index c3262b43..0f28ab63 100644 --- a/src/Rule/PlaceBraces.php +++ b/src/Rule/PlaceBraces.php @@ -3,7 +3,7 @@ namespace Lkrms\PrettyPHP\Rule; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Token; @@ -74,8 +74,7 @@ public function processTokens(array $tokens): void $close = $token->ClosedBy; // Suppress blank lines before close braces - $close->WhitespaceBefore |= WhitespaceType::LINE | WhitespaceType::SPACE; - $close->WhitespaceMaskPrev &= ~WhitespaceType::BLANK; + $close->Whitespace |= Space::NO_BLANK_BEFORE | Space::LINE_BEFORE | Space::SPACE_BEFORE; // Don't move subsequent code to the next line if the brace is part // of an expression @@ -83,7 +82,7 @@ public function processTokens(array $tokens): void // Keep structures like `} else {` on the same line $next = $close->NextCode; if ($next && $next->continuesControlStructure()) { - $close->WhitespaceAfter |= WhitespaceType::SPACE; + $close->Whitespace |= Space::SPACE_AFTER; if (!($next->Flags & TokenFlag::HAS_UNENCLOSED_BODY) || ( // `$next` can only be `elseif` or `else`, so if the // close brace is not the body of `if` or `elseif`, the @@ -93,14 +92,14 @@ public function processTokens(array $tokens): void && $close->PrevSibling->PrevSibling && $this->Idx->IfOrElseIf[$close->PrevSibling->PrevSibling->id] )) { - $next->WhitespaceMaskPrev &= ~WhitespaceType::BLANK & ~WhitespaceType::LINE; + $next->Whitespace |= Space::NO_BLANK_BEFORE | Space::NO_LINE_BEFORE; } else { - $close->WhitespaceAfter |= WhitespaceType::LINE; - $next->WhitespaceMaskPrev &= ~WhitespaceType::BLANK; + $close->Whitespace |= Space::LINE_AFTER; + $next->Whitespace |= Space::NO_BLANK_BEFORE; } } else { // Otherwise, add newlines after close braces - $close->WhitespaceAfter |= WhitespaceType::LINE | WhitespaceType::SPACE; + $close->Whitespace |= Space::LINE_AFTER | Space::SPACE_AFTER; } } @@ -117,20 +116,17 @@ public function processTokens(array $tokens): void || $token->inPropertyOrPropertyHook() ) ) { - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $token->WhitespaceMaskPrev = WhitespaceType::SPACE; - $token->WhitespaceMaskNext = WhitespaceType::NONE; + $token->Whitespace |= Space::NONE_BEFORE | Space::NONE_AFTER; + $token->applyWhitespace(Space::SPACE_BEFORE); continue; } // Add newlines and suppress blank lines after open braces - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $token->WhitespaceAfter |= WhitespaceType::LINE | WhitespaceType::SPACE; - $token->WhitespaceMaskNext &= ~WhitespaceType::BLANK; + $token->Whitespace |= Space::SPACE_BEFORE | Space::NO_BLANK_AFTER | Space::LINE_AFTER | Space::SPACE_AFTER; // Suppress horizontal whitespace between empty braces if ($next->id === \T_CLOSE_BRACE) { - $token->WhitespaceMaskNext &= ~WhitespaceType::SPACE; + $token->Whitespace |= Space::NO_SPACE_AFTER; } // Collect consecutive `)` and `{` tokens to collapse before @@ -158,8 +154,8 @@ public function beforeRender(array $tokens): void { foreach ($this->BracketBracePairs as [$bracket, $brace]) { if ($bracket->hasNewlineBefore() && $brace->hasNewlineBefore()) { - $brace->WhitespaceBefore |= WhitespaceType::SPACE; - $brace->WhitespaceMaskPrev = WhitespaceType::SPACE; + $brace->Whitespace |= Space::NONE_BEFORE; + $brace->applyWhitespace(Space::SPACE_BEFORE); } } } diff --git a/src/Rule/PlaceComments.php b/src/Rule/PlaceComments.php index d6acfaf4..3c53fe59 100644 --- a/src/Rule/PlaceComments.php +++ b/src/Rule/PlaceComments.php @@ -4,7 +4,7 @@ use Lkrms\PrettyPHP\Catalog\TokenFlag; use Lkrms\PrettyPHP\Catalog\TokenSubType; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Token; @@ -53,7 +53,7 @@ public function processTokens(array $tokens): void && $token->Next && $token->Next->id !== \T_CLOSE_TAG ) { - $token->CriticalWhitespaceAfter |= WhitespaceType::LINE; + $token->Whitespace |= Space::CRITICAL_LINE_AFTER; } $isDocComment = @@ -82,27 +82,26 @@ public function processTokens(array $tokens): void || $prev->OpenTag === $prev )) { $this->CommentsBesideCode[] = $token; - $token->WhitespaceMaskPrev &= ~WhitespaceType::BLANK & ~WhitespaceType::LINE; + $token->Whitespace |= Space::NO_BLANK_BEFORE | Space::NO_LINE_BEFORE; continue; } - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $token->WhitespaceAfter |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_BEFORE | Space::SPACE_AFTER; continue; } // Aside from DocBlocks and, in strict PSR-12 mode, comments after // top-level close braces, don't move comments to the next line if (!$wasFirstOnLine) { - $token->WhitespaceAfter |= WhitespaceType::LINE | WhitespaceType::SPACE; + $token->Whitespace |= Space::LINE_AFTER | Space::SPACE_AFTER; if ($prev && ( $prev->Flags & TokenFlag::CODE || $prev->OpenTag === $prev )) { $this->CommentsBesideCode[] = $token; - $token->WhitespaceMaskPrev &= ~WhitespaceType::BLANK & ~WhitespaceType::LINE; + $token->Whitespace |= Space::NO_BLANK_BEFORE | Space::NO_LINE_BEFORE; continue; } - $token->WhitespaceBefore |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_BEFORE; continue; } } @@ -114,8 +113,7 @@ public function processTokens(array $tokens): void $this->Comments[] = [$token, $next]; } - $token->WhitespaceAfter |= WhitespaceType::LINE; - $token->WhitespaceBefore |= WhitespaceType::LINE | WhitespaceType::SPACE; + $token->Whitespace |= Space::LINE_BEFORE | Space::SPACE_BEFORE | Space::LINE_AFTER; if (!$isDocComment) { continue; @@ -131,7 +129,7 @@ public function processTokens(array $tokens): void || !$token->NextSibling || $token->PrevSibling->Statement !== $token->NextSibling->Statement) ) { - $token->WhitespaceBefore |= WhitespaceType::BLANK; + $token->Whitespace |= Space::BLANK_BEFORE; } // Add a blank line after file-level DocBlocks and multi-line C-style @@ -146,7 +144,7 @@ public function processTokens(array $tokens): void ) ) ) { - $token->WhitespaceAfter |= WhitespaceType::BLANK; + $token->Whitespace |= Space::BLANK_AFTER; continue; } @@ -156,7 +154,7 @@ public function processTokens(array $tokens): void && $next === $token->Next && $token->id === \T_DOC_COMMENT ) { - $token->WhitespaceMaskNext &= ~WhitespaceType::BLANK; + $token->Whitespace |= Space::NO_BLANK_AFTER; } } } @@ -165,13 +163,12 @@ public function beforeRender(array $tokens): void { foreach ($this->CommentsBesideCode as $token) { if (!$token->hasNewlineBefore()) { - $token->WhitespaceBefore |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_BEFORE; if ($token->hasNewlineAfter()) { - $token->Prev->WhitespaceMaskNext |= WhitespaceType::SPACE; - $token->WhitespaceMaskPrev |= WhitespaceType::SPACE; + $token->removeWhitespace(Space::NO_SPACE_BEFORE); $token->Padding = $this->Formatter->SpacesBesideCode - 1; } else { - $token->WhitespaceAfter |= WhitespaceType::SPACE; + $token->Whitespace |= Space::SPACE_AFTER; } } } @@ -256,7 +253,7 @@ public function beforeRender(array $tokens): void foreach ($this->CollapsibleComments as $token) { if ($token->hasNewline()) { - $token->WhitespaceBefore |= WhitespaceType::BLANK; + $token->Whitespace |= Space::BLANK_BEFORE; } } } diff --git a/src/Rule/PreserveNewlines.php b/src/Rule/PreserveNewlines.php index e3ae6946..9911f609 100644 --- a/src/Rule/PreserveNewlines.php +++ b/src/Rule/PreserveNewlines.php @@ -4,7 +4,7 @@ use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Token; @@ -60,17 +60,17 @@ public function processTokens(array $tokens): void continue; } - $effective = $token->effectiveWhitespaceBefore(); + $before = $token->getWhitespaceBefore(); if ($lines > 1) { - if ($effective & WhitespaceType::BLANK) { + if ($before & Space::BLANK) { continue; } - $line = WhitespaceType::BLANK | WhitespaceType::LINE; + $line = Space::BLANK | Space::LINE; } else { - if ($effective & (WhitespaceType::BLANK | WhitespaceType::LINE)) { + if ($before & (Space::BLANK | Space::LINE)) { continue; } - $line = WhitespaceType::LINE; + $line = Space::LINE; } $min = $prev->line; @@ -122,10 +122,10 @@ private function maybePreserveNewlineBefore( } if (!$this->Idx->AllowBlankBefore[$token->id]) { - $line = WhitespaceType::LINE; + $line = Space::LINE; } - $token->WhitespaceBefore |= $line; + $token->Whitespace |= $line; return true; } @@ -175,7 +175,7 @@ private function maybePreserveNewlineAfter( return false; } - if ($line & WhitespaceType::BLANK + if ($line & Space::BLANK && (!$this->Idx->AllowBlankAfter[$token->id] || ($token->id === \T_COMMA && !$token->isDelimiterBetweenMatchArms()) @@ -193,7 +193,7 @@ private function maybePreserveNewlineAfter( if (!$this->Formatter->PreserveNewlines) { return false; } - $line = WhitespaceType::LINE; + $line = Space::LINE; } if (!$this->Formatter->PreserveNewlines @@ -201,7 +201,7 @@ private function maybePreserveNewlineAfter( return false; } - $token->WhitespaceAfter |= $line; + $token->Whitespace |= $line << 3; return true; } diff --git a/src/Rule/Preset/Drupal.php b/src/Rule/Preset/Drupal.php index 5319c20f..9826d631 100644 --- a/src/Rule/Preset/Drupal.php +++ b/src/Rule/Preset/Drupal.php @@ -3,7 +3,7 @@ namespace Lkrms\PrettyPHP\Rule\Preset; use Lkrms\PrettyPHP\Catalog\HeredocIndent; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\Preset; use Lkrms\PrettyPHP\Contract\TokenRule; @@ -82,15 +82,9 @@ public function processTokens(array $tokens): void } /** @var Token */ $closedBy = $open->ClosedBy; - /** @var Token */ - $prev = $closedBy->Prev; - $open->WhitespaceAfter |= WhitespaceType::BLANK; - $open->WhitespaceMaskNext |= WhitespaceType::BLANK; - $next->WhitespaceMaskPrev |= WhitespaceType::BLANK; - $closedBy->WhitespaceBefore |= WhitespaceType::BLANK; - $closedBy->WhitespaceMaskPrev |= WhitespaceType::BLANK; - $prev->WhitespaceMaskNext |= WhitespaceType::BLANK; + $open->applyWhitespace(Space::BLANK_AFTER); + $closedBy->applyWhitespace(Space::BLANK_BEFORE); continue; } @@ -104,11 +98,7 @@ public function processTokens(array $tokens): void } if ($phpDoc->hasTag('file')) { - $token->WhitespaceAfter |= WhitespaceType::BLANK; - $token->WhitespaceMaskNext |= WhitespaceType::BLANK; - if ($token->Next) { - $token->Next->WhitespaceMaskPrev |= WhitespaceType::BLANK; - } + $token->applyWhitespace(Space::BLANK_AFTER); } continue; @@ -119,11 +109,7 @@ public function processTokens(array $tokens): void /** @var Token */ $prevCode = $token->PrevCode; if ($prevCode->id === \T_CLOSE_BRACE) { - $token->WhitespaceBefore |= WhitespaceType::LINE; - $token->WhitespaceMaskPrev |= WhitespaceType::LINE; - /** @var Token */ - $prev = $token->Prev; - $prev->WhitespaceMaskNext |= WhitespaceType::LINE; + $token->applyWhitespace(Space::LINE_BEFORE); } } } diff --git a/src/Rule/Preset/Laravel.php b/src/Rule/Preset/Laravel.php index e643e5ff..33760cbb 100644 --- a/src/Rule/Preset/Laravel.php +++ b/src/Rule/Preset/Laravel.php @@ -3,7 +3,7 @@ namespace Lkrms\PrettyPHP\Rule\Preset; use Lkrms\PrettyPHP\Catalog\HeredocIndent; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\Preset; use Lkrms\PrettyPHP\Contract\TokenRule; @@ -63,22 +63,15 @@ public function processTokens(array $tokens): void if ($next->id === \T_LOGICAL_NOT) { continue 2; } - $token->WhitespaceAfter |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext |= WhitespaceType::SPACE; - $next->WhitespaceMaskPrev |= WhitespaceType::SPACE; + $token->applyWhitespace(Space::SPACE_AFTER); continue 2; case \T_CONCAT: - $token->WhitespaceMaskPrev &= ~WhitespaceType::SPACE; - $token->WhitespaceMaskNext &= ~WhitespaceType::SPACE; + $token->Whitespace |= Space::NO_SPACE_BEFORE | Space::NO_SPACE_AFTER; continue 2; case \T_FN: - /** @var Token */ - $next = $token->Next; - $token->WhitespaceAfter |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext |= WhitespaceType::SPACE; - $next->WhitespaceMaskPrev |= WhitespaceType::SPACE; + $token->applyWhitespace(Space::SPACE_AFTER); continue 2; } } diff --git a/src/Rule/Preset/Symfony.php b/src/Rule/Preset/Symfony.php index 3ea2ecfc..a5122032 100644 --- a/src/Rule/Preset/Symfony.php +++ b/src/Rule/Preset/Symfony.php @@ -7,7 +7,7 @@ use Lkrms\PrettyPHP\Catalog\ImportSortOrder; use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\ListRule; use Lkrms\PrettyPHP\Contract\Preset; @@ -68,15 +68,10 @@ public function processTokens(array $tokens): void { foreach ($tokens as $token) { if ($token->id === \T_CONCAT) { - $token->WhitespaceMaskPrev &= ~WhitespaceType::SPACE; - $token->WhitespaceMaskNext &= ~WhitespaceType::SPACE; + $token->Whitespace |= Space::NO_SPACE_BEFORE | Space::NO_SPACE_AFTER; continue; } - $token->WhitespaceAfter |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext |= WhitespaceType::SPACE; - /** @var Token */ - $next = $token->Next; - $next->WhitespaceMaskPrev |= WhitespaceType::SPACE; + $token->applyWhitespace(Space::SPACE_AFTER); } } @@ -95,6 +90,6 @@ public function processList(Token $parent, TokenCollection $items): void } } - $parent->outer()->maskInnerWhitespace(~WhitespaceType::BLANK & ~WhitespaceType::LINE); + $parent->outer()->applyInnerWhitespace(Space::NO_BLANK | Space::NO_LINE); } } diff --git a/src/Rule/Preset/WordPress.php b/src/Rule/Preset/WordPress.php index 5ed82f12..54c37ece 100644 --- a/src/Rule/Preset/WordPress.php +++ b/src/Rule/Preset/WordPress.php @@ -3,7 +3,7 @@ namespace Lkrms\PrettyPHP\Rule\Preset; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\Preset; use Lkrms\PrettyPHP\Contract\TokenRule; @@ -78,10 +78,7 @@ public function processTokens(array $tokens): void } if ($token->id === \T_DOC_COMMENT && !$this->DocCommentUnpinned) { - $token->WhitespaceMaskNext |= WhitespaceType::BLANK; - if ($token->Next) { - $token->Next->WhitespaceMaskPrev |= WhitespaceType::BLANK; - } + $token->removeWhitespace(Space::NO_BLANK_AFTER); $this->DocCommentUnpinned = true; } @@ -92,7 +89,7 @@ public function processTokens(array $tokens): void $token->hasBlankLineBefore() && $token->line - $prev->line - substr_count($prev->text, "\n") < 2 ) { - $token->WhitespaceMaskPrev &= ~WhitespaceType::BLANK; + $token->Whitespace |= Space::NO_BLANK_BEFORE; } continue; } @@ -101,11 +98,7 @@ public function processTokens(array $tokens): void if (!$token->isColonAltSyntaxDelimiter()) { continue; } - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $token->WhitespaceMaskPrev |= WhitespaceType::SPACE; - /** @var Token */ - $prev = $token->Prev; - $prev->WhitespaceMaskNext |= WhitespaceType::SPACE; + $token->applyWhitespace(Space::SPACE_BEFORE); continue; } @@ -115,25 +108,17 @@ public function processTokens(array $tokens): void if ($next->id === \T_LOGICAL_NOT) { continue; } - $token->WhitespaceAfter |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext |= WhitespaceType::SPACE; - $next->WhitespaceMaskPrev |= WhitespaceType::SPACE; + $token->applyWhitespace(Space::SPACE_AFTER); continue; } if ($token->id === \T_OPEN_BRACE) { - /** @var Token */ - $next = $token->Next; - $token->WhitespaceMaskNext |= WhitespaceType::BLANK; - $next->WhitespaceMaskPrev |= WhitespaceType::BLANK; + $token->removeWhitespace(Space::NO_BLANK_AFTER); continue; } if ($token->id === \T_CLOSE_BRACE) { - /** @var Token */ - $prev = $token->Prev; - $token->WhitespaceMaskPrev |= WhitespaceType::BLANK; - $prev->WhitespaceMaskNext |= WhitespaceType::BLANK; + $token->removeWhitespace(Space::NO_BLANK_BEFORE); continue; } @@ -153,12 +138,8 @@ public function processTokens(array $tokens): void continue; } - $token->WhitespaceAfter |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext |= WhitespaceType::SPACE; - $token->Next->WhitespaceMaskPrev |= WhitespaceType::SPACE; - $token->ClosedBy->WhitespaceBefore |= WhitespaceType::SPACE; - $token->ClosedBy->WhitespaceMaskPrev |= WhitespaceType::SPACE; - $token->ClosedBy->Prev->WhitespaceMaskNext |= WhitespaceType::SPACE; + $token->ClosedBy->applyWhitespace(Space::SPACE_BEFORE); + $token->applyWhitespace(Space::SPACE_AFTER); } } diff --git a/src/Rule/ProtectStrings.php b/src/Rule/ProtectStrings.php index b117315d..860e1c0f 100644 --- a/src/Rule/ProtectStrings.php +++ b/src/Rule/ProtectStrings.php @@ -2,7 +2,7 @@ namespace Lkrms\PrettyPHP\Rule; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Token; @@ -50,9 +50,10 @@ public static function needsSortedTokens(): bool /** * Apply the rule to the given tokens * - * Whitespace is suppressed via critical masks applied to siblings in - * non-constant strings, and to every token between square brackets in those - * strings. + * Changes to whitespace in non-constant strings are suppressed for: + * + * - nested siblings + * - every descendant of square brackets that are nested siblings */ public function processTokens(array $tokens): void { @@ -65,7 +66,7 @@ public function processTokens(array $tokens): void /** @var Token */ $closedBy = $token->StringClosedBy; foreach ($next->collectSiblings($closedBy) as $current) { - $current->CriticalWhitespaceMaskPrev = WhitespaceType::NONE; + $current->Whitespace |= Space::CRITICAL_NONE_BEFORE; // "$foo[0]" and "$foo[$bar]" fail to parse if there is any // whitespace between the brackets @@ -75,7 +76,7 @@ public function processTokens(array $tokens): void /** @var Token */ $closedBy = $current->ClosedBy; foreach ($next->collect($closedBy) as $inner) { - $inner->CriticalWhitespaceMaskPrev = WhitespaceType::NONE; + $inner->Whitespace |= Space::CRITICAL_NONE_BEFORE; } } } diff --git a/src/Rule/StandardWhitespace.php b/src/Rule/StandardWhitespace.php index 6acfefcc..a1d127e6 100644 --- a/src/Rule/StandardWhitespace.php +++ b/src/Rule/StandardWhitespace.php @@ -4,7 +4,7 @@ use Lkrms\PrettyPHP\Catalog\DeclarationType; use Lkrms\PrettyPHP\Catalog\TokenData; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\DeclarationRuleTrait; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\DeclarationRule; @@ -169,19 +169,19 @@ public function processTokens(array $tokens): void $endIsClose && !strcasecmp((string) $declare->NextSibling->inner(), 'strict_types=1') )) { - $token->WhitespaceAfter |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext = WhitespaceType::SPACE; + $token->Whitespace |= Space::NONE_AFTER; + $token->applyWhitespace(Space::SPACE_AFTER); $endOfLine = $end; if ($endIsClose) { /** @var Token */ $close = $token->CloseTag; - $close->WhitespaceBefore |= WhitespaceType::SPACE; - $close->WhitespaceMaskPrev = WhitespaceType::SPACE; + $close->Whitespace |= Space::NONE_BEFORE; + $close->applyWhitespace(Space::SPACE_BEFORE); } } } if ($endOfLine->id !== \T_CLOSE_TAG) { - $endOfLine->WhitespaceAfter |= WhitespaceType::LINE | WhitespaceType::SPACE; + $endOfLine->Whitespace |= Space::LINE_AFTER | Space::SPACE_AFTER; } // Preserve one-line statements between open and close tags on @@ -242,20 +242,19 @@ static function () use ($idx, $innerIndent, $next, $last) { } if ($token->id === \T_CLOSE_TAG) { - $token->WhitespaceBefore |= WhitespaceType::LINE | WhitespaceType::SPACE; + $token->Whitespace |= Space::LINE_BEFORE | Space::SPACE_BEFORE; continue; } if ($token->id === \T_COMMA) { - $token->WhitespaceMaskPrev = WhitespaceType::NONE; - $token->WhitespaceAfter |= WhitespaceType::SPACE; + $token->Whitespace |= Space::NONE_BEFORE | Space::SPACE_AFTER; continue; } if ($token->id === \T_DECLARE) { /** @var Token */ $nextCode = $token->NextCode; - $nextCode->outer()->maskInnerWhitespace(WhitespaceType::NONE); + $nextCode->outer()->applyInnerWhitespace(Space::NONE); continue; } @@ -272,7 +271,7 @@ static function () use ($idx, $innerIndent, $next, $last) { if ($arm->id === \T_NULL) { break; } - $arm->WhitespaceAfter |= WhitespaceType::LINE; + $arm->Whitespace |= Space::LINE_AFTER; } continue; } @@ -283,18 +282,16 @@ static function () use ($idx, $innerIndent, $next, $last) { ? $token->ClosedBy : $token; if (!$token->inParameterList() && !$token->inPropertyHook()) { - $token->WhitespaceBefore |= WhitespaceType::LINE; - $closedBy->WhitespaceAfter |= WhitespaceType::LINE; + $token->Whitespace |= Space::LINE_BEFORE; + $closedBy->Whitespace |= Space::LINE_AFTER; } - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $closedBy->WhitespaceAfter |= WhitespaceType::SPACE; - $closedBy->WhitespaceMaskNext &= ~WhitespaceType::BLANK; + $token->Whitespace |= Space::SPACE_BEFORE; + $closedBy->Whitespace |= Space::NO_BLANK_AFTER | Space::SPACE_AFTER; continue; } if ($token->id === \T_START_HEREDOC && $this->Formatter->Psr12) { - $token->WhitespaceBefore |= WhitespaceType::SPACE; - $token->WhitespaceMaskPrev &= ~WhitespaceType::BLANK & ~WhitespaceType::LINE; + $token->Whitespace |= Space::NO_BLANK_BEFORE | Space::NO_LINE_BEFORE | Space::SPACE_BEFORE; } } } @@ -344,8 +341,7 @@ public function processDeclarations(array $declarations): void } // Format `set () {}` like `function () {}` if ($hasBody = $next->id === \T_OPEN_BRACE) { - $name->WhitespaceBefore |= WhitespaceType::SPACE; - $name->WhitespaceAfter |= WhitespaceType::SPACE; + $name->Whitespace |= Space::SPACE_BEFORE | Space::SPACE_AFTER; } $hasExpression = $next->id === \T_DOUBLE_ARROW; $collapse = $collapse && !( @@ -372,7 +368,7 @@ public function processDeclarations(array $declarations): void /** @var TokenCollection */ $items = $parent->Data[TokenData::LIST_ITEMS]; foreach ($items as $item) { - $item->WhitespaceBefore |= WhitespaceType::LINE; + $item->Whitespace |= Space::LINE_BEFORE; } } } diff --git a/src/Rule/StatementSpacing.php b/src/Rule/StatementSpacing.php index 911ea4f8..ca035869 100644 --- a/src/Rule/StatementSpacing.php +++ b/src/Rule/StatementSpacing.php @@ -3,7 +3,7 @@ namespace Lkrms\PrettyPHP\Rule; use Lkrms\PrettyPHP\Catalog\TokenSubType; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\TokenTypeIndex; @@ -52,9 +52,7 @@ public function processTokens(array $tokens): void || $token->NextSibling->id === \T_SEMICOLON) { continue 2; } - $token->WhitespaceAfter |= WhitespaceType::SPACE; - $token->WhitespaceMaskNext |= WhitespaceType::SPACE; - $token->Next->WhitespaceMaskPrev |= WhitespaceType::SPACE; + $token->applyWhitespace(Space::SPACE_AFTER); continue 2; } @@ -81,9 +79,7 @@ public function processTokens(array $tokens): void break; } - $token->WhitespaceBefore = WhitespaceType::NONE; - $token->WhitespaceMaskPrev = WhitespaceType::NONE; - $token->WhitespaceAfter |= WhitespaceType::LINE | WhitespaceType::SPACE; + $token->Whitespace |= Space::NONE_BEFORE | Space::LINE_AFTER | Space::SPACE_AFTER; } } } diff --git a/src/Rule/StrictExpressions.php b/src/Rule/StrictExpressions.php index a2d4e49d..935e6c9b 100644 --- a/src/Rule/StrictExpressions.php +++ b/src/Rule/StrictExpressions.php @@ -2,9 +2,10 @@ namespace Lkrms\PrettyPHP\Rule; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; +use Lkrms\PrettyPHP\Token; use Lkrms\PrettyPHP\TokenTypeIndex; /** @@ -46,18 +47,16 @@ public static function needsSortedTokens(): bool public function processTokens(array $tokens): void { foreach ($tokens as $token) { + /** @var Token */ $first = $token->NextCode; if ($first->hasNewlineAfter()) { continue; } + /** @var Token */ $last = $first->ClosedBy; if ($first->collect($last)->hasNewline()) { - $first->WhitespaceAfter |= WhitespaceType::LINE; - $first->WhitespaceMaskNext |= WhitespaceType::LINE; - $first->NextCode->WhitespaceMaskPrev |= WhitespaceType::LINE; - $last->WhitespaceBefore |= WhitespaceType::LINE; - $last->WhitespaceMaskPrev |= WhitespaceType::LINE; - $last->PrevCode->WhitespaceMaskNext |= WhitespaceType::LINE; + $first->applyWhitespace(Space::LINE_AFTER); + $last->applyWhitespace(Space::LINE_BEFORE); } } } diff --git a/src/Rule/StrictLists.php b/src/Rule/StrictLists.php index 71d0b0cf..250d5a83 100644 --- a/src/Rule/StrictLists.php +++ b/src/Rule/StrictLists.php @@ -2,7 +2,7 @@ namespace Lkrms\PrettyPHP\Rule; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\ListRuleTrait; use Lkrms\PrettyPHP\Contract\ListRule; use Lkrms\PrettyPHP\Internal\TokenCollection; @@ -33,14 +33,14 @@ public function processList(Token $parent, TokenCollection $items): void return; } if ($items->nth(2)->hasNewlineBefore()) { - $items->addWhitespaceBefore(WhitespaceType::LINE); + $items->applyWhitespace(Space::LINE_BEFORE); } else { // Leave the first item alone unless strict PSR-12 compliance is // enabled if ($parent->ClosedBy && !$this->Formatter->Psr12) { $items->shift(); } - $items->maskWhitespaceBefore(~WhitespaceType::BLANK & ~WhitespaceType::LINE); + $items->applyWhitespace(Space::NO_BLANK_BEFORE | Space::NO_LINE_BEFORE); } } } diff --git a/src/Rule/SwitchIndentation.php b/src/Rule/SwitchIndentation.php index 14be89ce..839e1a9a 100644 --- a/src/Rule/SwitchIndentation.php +++ b/src/Rule/SwitchIndentation.php @@ -2,7 +2,7 @@ namespace Lkrms\PrettyPHP\Rule; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Token; @@ -47,9 +47,8 @@ public function processTokens(array $tokens): void /** @var Token */ $separator = $token->EndStatement; - $token->WhitespaceBefore |= WhitespaceType::LINE; - $separator->WhitespaceAfter |= WhitespaceType::LINE | WhitespaceType::SPACE; - $separator->WhitespaceMaskNext &= ~WhitespaceType::BLANK; + $token->Whitespace |= Space::LINE_BEFORE; + $separator->Whitespace |= Space::NO_BLANK_AFTER | Space::LINE_AFTER | Space::SPACE_AFTER; $token->collect($separator)->forEach(fn(Token $t) => $t->Deindent++); } } diff --git a/src/Rule/VerticalWhitespace.php b/src/Rule/VerticalWhitespace.php index 491f5602..81dd6135 100644 --- a/src/Rule/VerticalWhitespace.php +++ b/src/Rule/VerticalWhitespace.php @@ -4,7 +4,7 @@ use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\TokenFlag; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Token; @@ -104,7 +104,7 @@ public function boot(): void $t->Prev && $sol->collect($t->Prev)->hasOneNotFrom($this->Idx->CloseBracket) )) { - $t->WhitespaceBefore |= WhitespaceType::LINE; + $t->Whitespace |= Space::LINE_BEFORE; } }; $applyNewlineAfter = function (Token $t): void { @@ -115,7 +115,7 @@ public function boot(): void $t->Next && $t->Next->collect($eol)->hasOneNotFrom($this->Idx->OpenBracketOrNot) )) { - $t->WhitespaceAfter |= WhitespaceType::LINE; + $t->Whitespace |= Space::LINE_AFTER; } }; @@ -219,22 +219,21 @@ public function processTokens(array $tokens): void || $expr2->hasNewlineBetweenTokens() || $expr3->hasNewlineBetweenTokens() ) { - $commas->addWhitespaceAfter(WhitespaceType::LINE); - $semicolons->addWhitespaceAfter(WhitespaceType::BLANK); + $commas->applyWhitespace(Space::LINE_AFTER); + $semicolons->applyWhitespace(Space::BLANK_AFTER); } elseif ($semicolons->tokenHasNewlineAfter()) { // If the second or third expression in a `for` loop is at // the start of a line, add a newline before the other - $semicolons->addWhitespaceAfter(WhitespaceType::LINE); + $semicolons->applyWhitespace(Space::LINE_AFTER); } // Suppress whitespace in empty `for` loop expressions foreach ([[$expr1, 1], [$expr2, 1], [$expr3, 0]] as [$expr, $emptyCount]) { if ($expr->count() === $emptyCount) { if ($emptyCount) { - $expr->maskWhitespaceBefore(WhitespaceType::NONE); + $expr->applyWhitespace(Space::NONE_BEFORE); } else { - $semi2->WhitespaceMaskNext = WhitespaceType::NONE; - $semi2->Next->WhitespaceMaskPrev = WhitespaceType::NONE; + $semi2->Whitespace |= Space::NONE_AFTER; } } } @@ -275,7 +274,7 @@ public function processTokens(array $tokens): void continue; } - $token->WhitespaceBefore |= WhitespaceType::LINE; + $token->Whitespace |= Space::LINE_BEFORE; continue; } @@ -296,9 +295,9 @@ public function processTokens(array $tokens): void $op1Newline = $token->hasNewlineBefore(); $op2Newline = $other->hasNewlineBefore(); if ($op1Newline && !$op2Newline) { - $other->WhitespaceBefore |= WhitespaceType::LINE; + $other->Whitespace |= Space::LINE_BEFORE; } elseif (!$op1Newline && $op2Newline) { - $token->WhitespaceBefore |= WhitespaceType::LINE; + $token->Whitespace |= Space::LINE_BEFORE; } continue; @@ -328,7 +327,7 @@ public function processTokens(array $tokens): void $chain->shift(); } - $chain->addWhitespaceBefore(WhitespaceType::LINE); + $chain->applyWhitespace(Space::LINE_BEFORE); // } } diff --git a/src/Token.php b/src/Token.php index 50bab403..f3c4f489 100644 --- a/src/Token.php +++ b/src/Token.php @@ -7,7 +7,7 @@ use Lkrms\PrettyPHP\Catalog\TokenFlag; use Lkrms\PrettyPHP\Catalog\TokenFlagMask; use Lkrms\PrettyPHP\Catalog\TokenSubType; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Contract\Filter; use Lkrms\PrettyPHP\Contract\HasTokenNames; use Lkrms\PrettyPHP\Internal\TokenCollection; @@ -152,56 +152,10 @@ class Token extends GenericToken implements HasTokenNames, JsonSerializable public ?self $AlignedWith = null; /** - * Bitmask representing whitespace between the token and its predecessor + * Bitmask representing whitespace applied between the token and its + * neighbours */ - public int $WhitespaceBefore = WhitespaceType::NONE; - - /** - * Bitmask representing whitespace between the token and its successor - */ - public int $WhitespaceAfter = WhitespaceType::NONE; - - /** - * Bitmask applied to whitespace between the token and its predecessor - */ - public int $WhitespaceMaskPrev = WhitespaceType::ALL; - - /** - * Bitmask applied to whitespace between the token and its successor - */ - public int $WhitespaceMaskNext = WhitespaceType::ALL; - - /** - * Secondary bitmask representing whitespace between the token and its - * predecessor - * - * Values added to this bitmask MUST NOT BE REMOVED. - */ - public int $CriticalWhitespaceBefore = WhitespaceType::NONE; - - /** - * Secondary bitmask representing whitespace between the token and its - * successor - * - * Values added to this bitmask MUST NOT BE REMOVED. - */ - public int $CriticalWhitespaceAfter = WhitespaceType::NONE; - - /** - * Secondary bitmask applied to whitespace between the token and its - * predecessor - * - * Values removed from this bitmask MUST NOT BE RESTORED. - */ - public int $CriticalWhitespaceMaskPrev = WhitespaceType::ALL; - - /** - * Secondary bitmask applied to whitespace between the token and its - * successor - * - * Values removed from this bitmask MUST NOT BE RESTORED. - */ - public int $CriticalWhitespaceMaskNext = WhitespaceType::ALL; + public int $Whitespace = 0; public int $OutputLine = -1; public int $OutputPos = -1; @@ -1422,7 +1376,7 @@ public function withTerminator(): self return $this; } - public function applyBlankLineBefore(bool $withMask = false): void + public function applyBlankLineBefore(bool $force = false): void { $t = $this; $i = 0; @@ -1444,59 +1398,109 @@ public function applyBlankLineBefore(bool $withMask = false): void $i++; $t = $t->Prev; } - $t->WhitespaceBefore |= WhitespaceType::BLANK; - if ($withMask) { - $t->WhitespaceMaskPrev |= WhitespaceType::BLANK; - if ($t->Prev) { - $t->Prev->WhitespaceMaskNext |= WhitespaceType::BLANK; - } + $t->Whitespace |= Space::BLANK_BEFORE; + if ($force) { + $t->removeWhitespace(Space::NO_BLANK_BEFORE); } } - public function effectiveWhitespaceBefore(): int + /** + * Apply whitespace to the token after removing conflicting whitespace + */ + public function applyWhitespace(int $whitespace): void { - return $this->CriticalWhitespaceBefore - | ($this->Prev->CriticalWhitespaceAfter ?? 0) - | (($this->WhitespaceBefore - | ($this->Prev->WhitespaceAfter ?? 0)) - & ($this->Prev->WhitespaceMaskNext ?? WhitespaceType::ALL) - & ($this->Prev->CriticalWhitespaceMaskNext ?? WhitespaceType::ALL) - & $this->WhitespaceMaskPrev - & $this->CriticalWhitespaceMaskPrev); + // Shift *_BEFORE and *_AFTER to their NO_* counterparts, then clear + // other bits + if ($remove = $whitespace << 6 & 0b111111000000) { + // @phpstan-ignore argument.type + $this->removeWhitespace($remove); + } + + $this->Whitespace |= $whitespace; } - public function effectiveWhitespaceAfter(): int - { - return $this->CriticalWhitespaceAfter - | ($this->Next->CriticalWhitespaceBefore ?? 0) - | (($this->WhitespaceAfter - | ($this->Next->WhitespaceBefore ?? 0)) - & ($this->Next->WhitespaceMaskPrev ?? WhitespaceType::ALL) - & ($this->Next->CriticalWhitespaceMaskPrev ?? WhitespaceType::ALL) - & $this->WhitespaceMaskNext - & $this->CriticalWhitespaceMaskNext); + /** + * Remove whitespace applied to the token or its neighbours + * + * @param int-mask-of $whitespace + */ + public function removeWhitespace(int $whitespace): void + { + $this->Whitespace &= ~$whitespace; + if ($this->Prev && ($before = $whitespace & 0b0111000111)) { + $this->Prev->Whitespace &= ~($before << 3); + } + if ($this->Next && ($after = $whitespace & 0b111000111000)) { + $this->Next->Whitespace &= ~($after >> 3); + } + } + + public function getWhitespaceBefore(): int + { + return $this->Prev + ? ( + $this->Whitespace >> 12 // CRITICAL_*_BEFORE + | $this->Prev->Whitespace >> 15 // CRITICAL_*_AFTER + | (( + $this->Whitespace >> 0 // *_BEFORE + | $this->Prev->Whitespace >> 3 // *_AFTER + ) & ~( + $this->Whitespace >> 6 // NO_*_BEFORE + | $this->Whitespace >> 18 // CRITICAL_NO_*_BEFORE + | $this->Prev->Whitespace >> 9 // NO_*_AFTER + | $this->Prev->Whitespace >> 21 // CRITICAL_NO_*_AFTER + )) + ) & 7 + : ($this->Whitespace >> 12 | ( + $this->Whitespace >> 0 & ~( + $this->Whitespace >> 6 + | $this->Whitespace >> 18 + ) + )) & 7; + } + + public function getWhitespaceAfter(): int + { + return $this->Next + ? ( + $this->Whitespace >> 15 // CRITICAL_*_AFTER + | $this->Next->Whitespace >> 12 // CRITICAL_*_BEFORE + | (( + $this->Whitespace >> 3 // *_AFTER + | $this->Next->Whitespace >> 0 // *_BEFORE + ) & ~( + $this->Whitespace >> 9 // NO_*_AFTER + | $this->Whitespace >> 21 // CRITICAL_NO_*_AFTER + | $this->Next->Whitespace >> 6 // NO_*_BEFORE + | $this->Next->Whitespace >> 18 // CRITICAL_NO_*_BEFORE + )) + ) & 7 + : ($this->Whitespace >> 15 | ( + $this->Whitespace >> 3 & ~( + $this->Whitespace >> 9 + | $this->Whitespace >> 21 + ) + )) & 7; } public function hasNewlineBefore(): bool { - return !!($this->effectiveWhitespaceBefore() - & (WhitespaceType::LINE | WhitespaceType::BLANK)); + return (bool) ($this->getWhitespaceBefore() & (Space::BLANK | Space::LINE)); } public function hasNewlineAfter(): bool { - return !!($this->effectiveWhitespaceAfter() - & (WhitespaceType::LINE | WhitespaceType::BLANK)); + return (bool) ($this->getWhitespaceAfter() & (Space::BLANK | Space::LINE)); } public function hasBlankLineBefore(): bool { - return !!($this->effectiveWhitespaceBefore() & WhitespaceType::BLANK); + return (bool) ($this->getWhitespaceBefore() & Space::BLANK); } public function hasBlankLineAfter(): bool { - return !!($this->effectiveWhitespaceAfter() & WhitespaceType::BLANK); + return (bool) ($this->getWhitespaceAfter() & Space::BLANK); } /** diff --git a/src/TokenUtil.php b/src/TokenUtil.php index 3badec12..48d7710d 100644 --- a/src/TokenUtil.php +++ b/src/TokenUtil.php @@ -5,7 +5,7 @@ use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\TokenFlag; use Lkrms\PrettyPHP\Catalog\TokenSubType; -use Lkrms\PrettyPHP\Catalog\WhitespaceType; +use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Internal\TokenCollection; use Salient\Utility\Arr; use Salient\Utility\Reflect; @@ -98,13 +98,13 @@ public static function isNewlineAllowedAfter(Token $token): bool public static function getWhitespace(int $type): string { - if ($type & WhitespaceType::BLANK) { + if ($type & Space::BLANK) { return "\n\n"; } - if ($type & WhitespaceType::LINE) { + if ($type & Space::LINE) { return "\n"; } - if ($type & WhitespaceType::SPACE) { + if ($type & Space::SPACE) { return ' '; } return ''; @@ -158,9 +158,11 @@ public static function serialize(Token $token): array $t['Heredoc'] = $token->Heredoc; if ($token->Flags) { + static $tokenFlags; + $tokenFlags ??= Reflect::getConstants(TokenFlag::class); $flags = []; /** @var int $value */ - foreach (Reflect::getConstants(TokenFlag::class) as $name => $value) { + foreach ($tokenFlags as $name => $value) { if (($token->Flags & $value) === $value) { $flags[] = $name; } @@ -205,14 +207,38 @@ public static function serialize(Token $token): array $t['Padding'] = $token->Padding; $t['HeredocIndent'] = $token->HeredocIndent; $t['AlignedWith'] = $token->AlignedWith; - $t['WhitespaceBefore'] = self::getWhitespace($token->WhitespaceBefore); - $t['WhitespaceAfter'] = self::getWhitespace($token->WhitespaceAfter); - $t['WhitespaceMaskPrev'] = $token->WhitespaceMaskPrev; - $t['WhitespaceMaskNext'] = $token->WhitespaceMaskNext; - $t['CriticalWhitespaceBefore'] = $token->CriticalWhitespaceBefore; - $t['CriticalWhitespaceAfter'] = $token->CriticalWhitespaceAfter; - $t['CriticalWhitespaceMaskPrev'] = $token->CriticalWhitespaceMaskPrev; - $t['CriticalWhitespaceMaskNext'] = $token->CriticalWhitespaceMaskNext; + + if ($token->Whitespace) { + static $whitespaceFlags; + $whitespaceFlags ??= Arr::unset( + Reflect::getConstants(Space::class), + 'SPACE', + 'LINE', + 'BLANK', + 'NO_SPACE', + 'NO_LINE', + 'NO_BLANK', + 'CRITICAL_SPACE', + 'CRITICAL_LINE', + 'CRITICAL_BLANK', + 'CRITICAL_NO_SPACE', + 'CRITICAL_NO_LINE', + 'CRITICAL_NO_BLANK', + ); + $whitespace = []; + $tokenValue = $token->Whitespace; + /** @var int $value */ + foreach ($whitespaceFlags as $name => $value) { + if (($tokenValue & $value) === $value) { + $whitespace[] = $name; + $tokenValue &= ~$value; + } + } + if ($whitespace) { + $t['Whitespace'] = implode('|', $whitespace); + } + } + $t['OutputLine'] = $token->OutputLine; $t['OutputPos'] = $token->OutputPos; $t['OutputColumn'] = $token->OutputColumn; From 021b5fb0dc6004e0cc45ee8feb56c7b353df6766 Mon Sep 17 00:00:00 2001 From: Luke Arms Date: Mon, 25 Nov 2024 18:17:17 +1100 Subject: [PATCH 3/6] Update documentation --- docs/Rules.md | 75 +++++++++++++++++---------------- src/Rule/DeclarationSpacing.php | 8 ++++ src/Rule/PlaceBraces.php | 7 +-- src/Rule/SimplifyNumbers.php | 4 +- src/Rule/SimplifyStrings.php | 22 +++++----- src/Rule/StandardWhitespace.php | 27 ++++++++++++ 6 files changed, 89 insertions(+), 54 deletions(-) diff --git a/docs/Rules.md b/docs/Rules.md index a3331f1d..40f82b82 100644 --- a/docs/Rules.md +++ b/docs/Rules.md @@ -60,8 +60,10 @@ needed. ### `ProtectStrings` -Whitespace is suppressed via critical masks applied to siblings in non-constant -strings, and to every token between square brackets in those strings. +Changes to whitespace in non-constant strings are suppressed for: + +- nested siblings +- every descendant of square brackets that are nested siblings ### `SimplifyNumbers` @@ -75,7 +77,7 @@ Float literals are normalised by removing redundant zeroes, adding `0` to empty integer or fractional parts, replacing `E` with `e`, removing `+` from exponents, and expressing them with mantissae between 1.0 and 10. -If present in the input, underscores are added to decimal values with no +If present in the input, underscores are applied to decimal values with no exponent every 3 digits, to hexadecimal values with more than 5 digits every 4 digits, and to binary values every 4 digits. @@ -83,18 +85,16 @@ digits, and to binary values every 4 digits. Strings other than nowdocs are normalised as follows: -Single- and double-quoted strings are replaced with the most readable and -economical syntax. Single-quoted strings are preferred unless escaping is -required or the double-quoted equivalent is shorter. - -Backslash escapes are added in contexts where they improve safety, consistency -and readability, otherwise they are removed if possible. - -Aside from leading and continuation bytes in valid UTF-8 strings, control -characters and non-ASCII characters are backslash-escaped using hexadecimal -notation with lowercase digits. Invisible characters that don't belong to a -recognised Unicode sequence are backslash-escaped using Unicode notation with -uppercase digits. +- Single- and double-quoted strings are replaced with the most readable and + economical syntax. Single-quoted strings are preferred unless escaping is + required or the double-quoted equivalent is shorter. +- Backslash escapes are added in contexts where they improve safety, consistency + and readability, otherwise they are removed if possible. +- Aside from leading and continuation bytes in valid UTF-8 strings, control + characters and non-ASCII characters are backslash-escaped using hexadecimal + notation with lowercase digits. Invisible characters that don't belong to a + recognised Unicode sequence are backslash-escaped using Unicode notation with + uppercase digits. ### `NormaliseComments` @@ -181,12 +181,13 @@ Whitespace is applied to structural and `match` expression braces as follows: - Newlines are added after close braces unless they belong to a `match` expression or a control structure that is immediately continued, e.g. `} else {`. In the latter case, trailing newlines are suppressed. -- Empty class, function and property hook bodies are collapsed to ` {}` - immediately after the declaration they belong to. +- Empty class, function and property hook bodies are collapsed to ` {}` on the + same line as the declaration they belong to unless + `CollapseEmptyDeclarationBodies` is disabled. - Horizontal whitespace is suppressed between other empty braces. -Open brace placement is left for a rule that runs after vertical whitespace has -been applied. +> Open brace placement is left for a rule that runs after vertical whitespace +> has been applied. ### `ListSpacing` (call 1: `processDeclarations()`) @@ -284,23 +285,23 @@ lines, they are collapsed to the same line. ## `DeclarationRule` classes, by declaration type -| Declaration | Rules | -| -------------- | ----------------------------------- | -| `*` | `StandardWhitespace` | -| `CASE` | `DeclarationSpacing` | -| `CLASS` | `DeclarationSpacing` | -| `CONST` | `DeclarationSpacing` | -| `DECLARE` | `DeclarationSpacing` | -| `ENUM` | `DeclarationSpacing` | -| `FUNCTION` | `DeclarationSpacing` | -| `INTERFACE` | `DeclarationSpacing` | -| `NAMESPACE` | `DeclarationSpacing` | -| `PARAM` | `ListSpacing` | -| `PROPERTY` | `DeclarationSpacing`, `ListSpacing` | -| `TRAIT` | `DeclarationSpacing` | -| `USE_CONST` | `DeclarationSpacing` | -| `USE_FUNCTION` | `DeclarationSpacing` | -| `USE_TRAIT` | `DeclarationSpacing` | -| `USE` | `DeclarationSpacing` | +| Declaration | Rules | +| -------------- | --------------------------------------------------------- | +| `CASE` | `DeclarationSpacing` | +| `CLASS` | `DeclarationSpacing` | +| `CONST` | `DeclarationSpacing` | +| `DECLARE` | `DeclarationSpacing` | +| `ENUM` | `DeclarationSpacing` | +| `FUNCTION` | `DeclarationSpacing` | +| `HOOK` | `DeclarationSpacing` | +| `INTERFACE` | `DeclarationSpacing` | +| `NAMESPACE` | `DeclarationSpacing` | +| `PARAM` | `ListSpacing`, `StandardWhitespace` | +| `PROPERTY` | `DeclarationSpacing`, `ListSpacing`, `StandardWhitespace` | +| `TRAIT` | `DeclarationSpacing` | +| `USE_CONST` | `DeclarationSpacing` | +| `USE_FUNCTION` | `DeclarationSpacing` | +| `USE_TRAIT` | `DeclarationSpacing` | +| `USE` | `DeclarationSpacing` | [list-rules.php]: ../scripts/list-rules.php diff --git a/src/Rule/DeclarationSpacing.php b/src/Rule/DeclarationSpacing.php index cd97e9cd..b54572a9 100644 --- a/src/Rule/DeclarationSpacing.php +++ b/src/Rule/DeclarationSpacing.php @@ -68,6 +68,14 @@ public static function getDeclarationTypes(array $all): array ] + $all; } + /** + * @inheritDoc + */ + public static function needsSortedDeclarations(): bool + { + return false; + } + /** * @inheritDoc */ diff --git a/src/Rule/PlaceBraces.php b/src/Rule/PlaceBraces.php index 0f28ab63..8876ac8e 100644 --- a/src/Rule/PlaceBraces.php +++ b/src/Rule/PlaceBraces.php @@ -54,11 +54,12 @@ public static function getTokenTypes(TokenTypeIndex $idx): array * expression or a control structure that is immediately continued, e.g. * `} else {`. In the latter case, trailing newlines are suppressed. * - Empty class, function and property hook bodies are collapsed to ` {}` - * immediately after the declaration they belong to. + * on the same line as the declaration they belong to unless + * `CollapseEmptyDeclarationBodies` is disabled. * - Horizontal whitespace is suppressed between other empty braces. * - * Open brace placement is left for a rule that runs after vertical - * whitespace has been applied. + * > Open brace placement is left for a rule that runs after vertical + * > whitespace has been applied. */ public function processTokens(array $tokens): void { diff --git a/src/Rule/SimplifyNumbers.php b/src/Rule/SimplifyNumbers.php index bed1d98c..72b55bfb 100644 --- a/src/Rule/SimplifyNumbers.php +++ b/src/Rule/SimplifyNumbers.php @@ -60,8 +60,8 @@ public static function needsSortedTokens(): bool * empty integer or fractional parts, replacing `E` with `e`, removing `+` * from exponents, and expressing them with mantissae between 1.0 and 10. * - * If present in the input, underscores are added to decimal values with no - * exponent every 3 digits, to hexadecimal values with more than 5 digits + * If present in the input, underscores are applied to decimal values with + * no exponent every 3 digits, to hexadecimal values with more than 5 digits * every 4 digits, and to binary values every 4 digits. */ public function processTokens(array $tokens): void diff --git a/src/Rule/SimplifyStrings.php b/src/Rule/SimplifyStrings.php index 625c225b..c9266f8e 100644 --- a/src/Rule/SimplifyStrings.php +++ b/src/Rule/SimplifyStrings.php @@ -42,18 +42,16 @@ public static function getTokenTypes(TokenTypeIndex $idx): array * * Strings other than nowdocs are normalised as follows: * - * Single- and double-quoted strings are replaced with the most readable and - * economical syntax. Single-quoted strings are preferred unless escaping is - * required or the double-quoted equivalent is shorter. - * - * Backslash escapes are added in contexts where they improve safety, - * consistency and readability, otherwise they are removed if possible. - * - * Aside from leading and continuation bytes in valid UTF-8 strings, control - * characters and non-ASCII characters are backslash-escaped using - * hexadecimal notation with lowercase digits. Invisible characters that - * don't belong to a recognised Unicode sequence are backslash-escaped using - * Unicode notation with uppercase digits. + * - Single- and double-quoted strings are replaced with the most readable + * and economical syntax. Single-quoted strings are preferred unless + * escaping is required or the double-quoted equivalent is shorter. + * - Backslash escapes are added in contexts where they improve safety, + * consistency and readability, otherwise they are removed if possible. + * - Aside from leading and continuation bytes in valid UTF-8 strings, + * control characters and non-ASCII characters are backslash-escaped using + * hexadecimal notation with lowercase digits. Invisible characters that + * don't belong to a recognised Unicode sequence are backslash-escaped + * using Unicode notation with uppercase digits. */ public function processTokens(array $tokens): void { diff --git a/src/Rule/StandardWhitespace.php b/src/Rule/StandardWhitespace.php index a1d127e6..5d4f2b6b 100644 --- a/src/Rule/StandardWhitespace.php +++ b/src/Rule/StandardWhitespace.php @@ -55,6 +55,33 @@ public static function getTokenTypes(TokenTypeIndex $idx): array ); } + /** + * @inheritDoc + */ + public static function needsSortedTokens(): bool + { + return false; + } + + /** + * @inheritDoc + */ + public static function getDeclarationTypes(array $all): array + { + return [ + DeclarationType::PROPERTY => true, + DeclarationType::PARAM => true, + ]; + } + + /** + * @inheritDoc + */ + public static function needsSortedDeclarations(): bool + { + return false; + } + /** * Apply the rule to the given tokens * From 9e5321c0f0b00694830e02ec5dd4b15cd25c1491 Mon Sep 17 00:00:00 2001 From: Luke Arms Date: Mon, 25 Nov 2024 19:35:30 +1100 Subject: [PATCH 4/6] Replace `Token::$StringClosedBy` with a `Token::$Data` value --- src/Catalog/TokenData.php | 15 ++++++++++----- src/Parser.php | 10 +++++++--- src/Rule/ProtectStrings.php | 4 ++-- src/Rule/SimplifyStrings.php | 8 ++++---- src/Token.php | 3 +-- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Catalog/TokenData.php b/src/Catalog/TokenData.php index 9c12f3b6..ce5e50e7 100644 --- a/src/Catalog/TokenData.php +++ b/src/Catalog/TokenData.php @@ -36,30 +36,35 @@ interface TokenData */ public const OTHER_TERNARY_OPERATOR = 4; + /** + * The last token of the string opened by the token + */ + public const STRING_CLOSED_BY = 5; + /** * The first T_OBJECT_OPERATOR or T_NULLSAFE_OBJECT_OPERATOR in a chain * thereof */ - public const CHAIN_OPENED_BY = 5; + public const CHAIN_OPENED_BY = 6; /** * A collection of tokens that form a NAMED_DECLARATION */ - public const NAMED_DECLARATION_PARTS = 6; + public const NAMED_DECLARATION_PARTS = 7; /** * The type of a NAMED_DECLARATION */ - public const NAMED_DECLARATION_TYPE = 7; + public const NAMED_DECLARATION_TYPE = 8; /** * A collection of property hooks for a NAMED_DECLARATION with type PROPERTY * or PROMOTED_PARAM */ - public const PROPERTY_HOOKS = 8; + public const PROPERTY_HOOKS = 9; /** * The type applied to an open bracket by the HangingIndentation rule */ - public const HANGING_INDENT_PARENT_TYPE = 9; + public const HANGING_INDENT_PARENT_TYPE = 10; } diff --git a/src/Parser.php b/src/Parser.php index 669ac637..b668b4e1 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -160,8 +160,8 @@ private function linkTokens(array $tokens): void * - `Parent` * - `Depth` * - `String` - * - `StringClosedBy` * - `Heredoc` + * - `Data[TokenData::STRING_CLOSED_BY]` * * @param non-empty-list $tokens * @param-out non-empty-list $tokens @@ -338,7 +338,11 @@ private function buildHierarchy( $token->String = $prev->String; $token->Heredoc = $prev->Heredoc; if ($idx->StringDelimiter[$prev->id]) { - if ($prev->String && $prev->String->StringClosedBy === $prev) { + if ( + $prev->String + && isset($prev->String->Data[TokenData::STRING_CLOSED_BY]) + && $prev->String->Data[TokenData::STRING_CLOSED_BY] === $prev + ) { $token->String = $prev->String->String; if ($prev->id === \T_END_HEREDOC) { assert($prev->Heredoc !== null); @@ -361,7 +365,7 @@ private function buildHierarchy( || ($token->String->id !== \T_START_HEREDOC && $token->String->id === $token->id) ) ) { - $token->String->StringClosedBy = $token; + $token->String->Data[TokenData::STRING_CLOSED_BY] = $token; } if ($idx->CloseBracketOrAlt[$token->id]) { diff --git a/src/Rule/ProtectStrings.php b/src/Rule/ProtectStrings.php index 860e1c0f..573d725c 100644 --- a/src/Rule/ProtectStrings.php +++ b/src/Rule/ProtectStrings.php @@ -2,6 +2,7 @@ namespace Lkrms\PrettyPHP\Rule; +use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Catalog\WhitespaceFlag as Space; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; @@ -63,8 +64,7 @@ public function processTokens(array $tokens): void continue; } - /** @var Token */ - $closedBy = $token->StringClosedBy; + $closedBy = $token->Data[TokenData::STRING_CLOSED_BY]; foreach ($next->collectSiblings($closedBy) as $current) { $current->Whitespace |= Space::CRITICAL_NONE_BEFORE; diff --git a/src/Rule/SimplifyStrings.php b/src/Rule/SimplifyStrings.php index c9266f8e..2a1bc2e5 100644 --- a/src/Rule/SimplifyStrings.php +++ b/src/Rule/SimplifyStrings.php @@ -2,6 +2,7 @@ namespace Lkrms\PrettyPHP\Rule; +use Lkrms\PrettyPHP\Catalog\TokenData; use Lkrms\PrettyPHP\Concern\TokenRuleTrait; use Lkrms\PrettyPHP\Contract\TokenRule; use Lkrms\PrettyPHP\Exception\RuleException; @@ -115,8 +116,7 @@ public function processTokens(array $tokens): void break; case \T_START_HEREDOC: - /** @var Token */ - $closedBy = $openedBy->StringClosedBy; + $closedBy = $openedBy->Data[TokenData::STRING_CLOSED_BY]; $start = trim($openedBy->text); $text = $token->text; @@ -195,7 +195,7 @@ function ($matches) use (&$utf8Escapes) { $reserved = "[nrtvef\\\\\${$reserved}]|[0-7]|x[0-9a-fA-F]|u\{[0-9a-fA-F]+\}"; if ( $token->id === \T_CONSTANT_ENCAPSED_STRING - || $next !== $openedBy->StringClosedBy + || $next !== $openedBy->Data[TokenData::STRING_CLOSED_BY] || $openedBy->id !== \T_START_HEREDOC ) { $reserved .= '|$'; @@ -210,7 +210,7 @@ function ($matches) use (&$utf8Escapes) { // the brace to remain escaped lest it become a `T_CURLY_OPEN` if ( $token->id !== \T_CONSTANT_ENCAPSED_STRING - && $next !== $openedBy->StringClosedBy + && $next !== $openedBy->Data[TokenData::STRING_CLOSED_BY] ) { $double = Regex::replace( '/(? - * @phpstan-var array{string,TokenCollection,int,self,self,self,TokenCollection,int,TokenCollection,int} + * @phpstan-var array{string,TokenCollection,int,self,self,self,self,TokenCollection,int,TokenCollection,int} */ public array $Data; From 7a58e3fae27a847918e6f9cb06049802a9f9ef07 Mon Sep 17 00:00:00 2001 From: Luke Arms Date: Mon, 25 Nov 2024 20:16:06 +1100 Subject: [PATCH 5/6] Review `StrictLists` - When `strict-lists` are enabled, always remove newlines before the first item in horizontal lists (not just in strict PSR-12 mode) --- docs/Rules.md | 5 +++++ phpstan-baseline-7.4.neon | 6 ------ phpstan-baseline-8.4.neon | 6 ------ src/Rule/StrictLists.php | 26 +++++++++++++++----------- tests/unit/Rule/StrictListsTest.php | 12 +++--------- 5 files changed, 23 insertions(+), 32 deletions(-) diff --git a/docs/Rules.md b/docs/Rules.md index 40f82b82..7b5f0d22 100644 --- a/docs/Rules.md +++ b/docs/Rules.md @@ -207,6 +207,11 @@ annotated parameters to improve readability. If interface lists break over multiple lines and neither `StrictLists` nor `AlignLists` are enabled, a newline is added before the first interface. +### `StrictLists` + +Items in lists are arranged horizontally or vertically by replicating the +arrangement of the first and second items. + ### `StandardWhitespace` (call 3: _`callback`_) The `TagIndent` of tokens between indented tags is adjusted by the difference, diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon index a84c39a5..3ebf8125 100644 --- a/phpstan-baseline-7.4.neon +++ b/phpstan-baseline-7.4.neon @@ -155,9 +155,3 @@ parameters: identifier: property.nonObject count: 3 path: src/Rule/StatementSpacing.php - - - - message: '#^Cannot call method hasNewlineBefore\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/Rule/StrictLists.php diff --git a/phpstan-baseline-8.4.neon b/phpstan-baseline-8.4.neon index 8792d5a7..4deda609 100644 --- a/phpstan-baseline-8.4.neon +++ b/phpstan-baseline-8.4.neon @@ -149,9 +149,3 @@ parameters: identifier: property.nonObject count: 3 path: src/Rule/StatementSpacing.php - - - - message: '#^Cannot call method hasNewlineBefore\(\) on Lkrms\\PrettyPHP\\Token\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/Rule/StrictLists.php diff --git a/src/Rule/StrictLists.php b/src/Rule/StrictLists.php index 250d5a83..e75e6e37 100644 --- a/src/Rule/StrictLists.php +++ b/src/Rule/StrictLists.php @@ -9,17 +9,17 @@ use Lkrms\PrettyPHP\Token; /** - * Arrange items in lists horizontally or vertically by replicating the - * arrangement of the first and second items + * Arrange items in lists horizontally or vertically * - * Newlines are added before the first item in vertical lists. Newlines before - * the first item in horizontal lists are removed when strict PSR-12 compliance - * is enabled. + * @api */ final class StrictLists implements ListRule { use ListRuleTrait; + /** + * @inheritDoc + */ public static function getPriority(string $method): ?int { return [ @@ -27,19 +27,23 @@ public static function getPriority(string $method): ?int ][$method] ?? null; } + /** + * Apply the rule to a token and the list of items associated with it + * + * Items in lists are arranged horizontally or vertically by replicating the + * arrangement of the first and second items. + */ public function processList(Token $parent, TokenCollection $items): void { if ($items->count() < 2) { return; } - if ($items->nth(2)->hasNewlineBefore()) { + + /** @var Token */ + $second = $items->nth(2); + if ($second->hasNewlineBefore()) { $items->applyWhitespace(Space::LINE_BEFORE); } else { - // Leave the first item alone unless strict PSR-12 compliance is - // enabled - if ($parent->ClosedBy && !$this->Formatter->Psr12) { - $items->shift(); - } $items->applyWhitespace(Space::NO_BLANK_BEFORE | Space::NO_LINE_BEFORE); } } diff --git a/tests/unit/Rule/StrictListsTest.php b/tests/unit/Rule/StrictListsTest.php index 6cdbceb8..849f6ee4 100644 --- a/tests/unit/Rule/StrictListsTest.php +++ b/tests/unit/Rule/StrictListsTest.php @@ -40,9 +40,7 @@ public static function outputProvider(): array 'multi-line array with opening newline' => [ <<<'PHP' Date: Mon, 25 Nov 2024 20:21:48 +1100 Subject: [PATCH 6/6] Update formatting --- scripts/update-3rdparty-fixtures.php | 4 +--- src/App/FormatPhpCommand.php | 5 ++++- src/Token.php | 8 +++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/scripts/update-3rdparty-fixtures.php b/scripts/update-3rdparty-fixtures.php index fcbb53a7..d742083b 100755 --- a/scripts/update-3rdparty-fixtures.php +++ b/scripts/update-3rdparty-fixtures.php @@ -102,9 +102,7 @@ function quote(string $string): string } $xml = ''; while ($split) { - $xml .= Regex::replace( - '/&[[:alpha:]_][[:alnum:]_.-]*;/', '', array_shift($split) - ); + $xml .= Regex::replace('/&[[:alpha:]_][[:alnum:]_.-]*;/', '', array_shift($split)); if ($split) { $xml .= array_shift($split); } diff --git a/src/App/FormatPhpCommand.php b/src/App/FormatPhpCommand.php index 1dd16c94..2421ad33 100644 --- a/src/App/FormatPhpCommand.php +++ b/src/App/FormatPhpCommand.php @@ -1453,7 +1453,10 @@ private function validateOptions( ): void { $throw = fn(string $message, string ...$names) => $this->throwInvalidOptionsException( - $exception, $configFile, $message, ...$names + $exception, + $configFile, + $message, + ...$names, ); if ($this->OperatorsFirst && $this->OperatorsLast) { diff --git a/src/Token.php b/src/Token.php index 2640b993..61fac043 100644 --- a/src/Token.php +++ b/src/Token.php @@ -1625,10 +1625,12 @@ public function expandedText(): string return $this->text; } - $tabSize = $this->Formatter->Indentation->TabSize - ?? $this->Formatter->TabSize; return Str::expandLeadingTabs( - $this->text, $tabSize, !$this->wasFirstOnLine(), $this->column + $this->text, + $this->Formatter->Indentation->TabSize + ?? $this->Formatter->TabSize, + !$this->wasFirstOnLine(), + $this->column, ); }