Skip to content

Commit 368effa

Browse files
committed
Printer: indent a new tag like the other tags
When the printer added a child to a PHPDoc, it used the largest indentation after the asterisk in the PHPDoc. The next lines of a multi-line description often have a larger indentation, so the new tag got it too. Now the printer uses the indentation of the line where the last existing child starts. Amp-Thread-ID: https://ampcode.com/threads/T-01a17930-0285-73c5-a957-4325c77aa1b2
1 parent f26818e commit 368effa

2 files changed

Lines changed: 85 additions & 0 deletions

File tree

‎src/Printer/Printer.php‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
use function implode;
7777
use function in_array;
7878
use function is_array;
79+
use function preg_match;
7980
use function preg_match_all;
8081
use function sprintf;
8182
use function str_replace;
@@ -589,6 +590,11 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
589590
[$isMultiline, $beforeAsteriskIndent, $afterAsteriskIndent] = $this->isMultiline($tokenIndex, $originalNodes, $originalTokens);
590591

591592
if ($insertStr === "\n * ") {
593+
$lastChildIndent = $this->detectLastChildIndent($originalNodes, $originalTokens);
594+
if ($lastChildIndent !== null) {
595+
[$beforeAsteriskIndent, $afterAsteriskIndent] = $lastChildIndent;
596+
}
597+
592598
$insertStr = sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
593599
}
594600

@@ -850,6 +856,39 @@ private function isMultiline(int $initialIndex, array $nodes, TokenIterator $ori
850856
return [$isMultiline, $before, $after];
851857
}
852858

859+
/**
860+
* Indentation before and after the asterisk on the line where the last child of a PHPDoc starts.
861+
* Other lines, e.g. the next lines of a multi-line description, can have a different indentation.
862+
*
863+
* @param Node[] $originalNodes
864+
* @return array{string, string}|null
865+
*/
866+
private function detectLastChildIndent(array $originalNodes, TokenIterator $originalTokens): ?array
867+
{
868+
$tokens = $originalTokens->getTokens();
869+
for ($i = count($originalNodes) - 1; $i >= 0; $i--) {
870+
$index = $originalNodes[$i]->getAttribute(Attribute::START_INDEX) - 1;
871+
$lineStart = '';
872+
while (isset($tokens[$index]) && $tokens[$index][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
873+
$lineStart = $tokens[$index][Lexer::VALUE_OFFSET] . $lineStart;
874+
$index--;
875+
}
876+
877+
if (!isset($tokens[$index]) || $tokens[$index][Lexer::TYPE_OFFSET] !== Lexer::TOKEN_PHPDOC_EOL) {
878+
continue;
879+
}
880+
881+
$lineStart = $tokens[$index][Lexer::VALUE_OFFSET] . $lineStart;
882+
if (preg_match('~\n(?<before>[\x09\x20]*)\*(?<after>[\x09\x20]*)$~', $lineStart, $match) !== 1) {
883+
continue;
884+
}
885+
886+
return [$match['before'], $match['after']];
887+
}
888+
889+
return null;
890+
}
891+
853892
private function printNodeFormatPreserving(Node $node, TokenIterator $originalTokens): string
854893
{
855894
/** @var Node|null $originalNode */

‎tests/PHPStan/Printer/PrinterTest.php‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use PHPStan\PhpDocParser\Ast\PhpDoc\PureUnlessParameterIsPassedTagValueNode;
3030
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
3131
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
32+
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
3233
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode;
3334
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
3435
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
@@ -194,6 +195,51 @@ public function enterNode(Node $node)
194195
$removeFirst,
195196
];
196197

198+
$addThrows = new class extends AbstractNodeVisitor {
199+
200+
public function enterNode(Node $node)
201+
{
202+
if ($node instanceof PhpDocNode) {
203+
$node->children[] = new PhpDocTagNode('@throws', new ThrowsTagValueNode(new IdentifierTypeNode('Exception'), ''));
204+
}
205+
206+
return $node;
207+
}
208+
209+
};
210+
211+
yield [
212+
self::nowdoc('
213+
/**
214+
* @param int $foo multi
215+
* line description
216+
*/'),
217+
self::nowdoc('
218+
/**
219+
* @param int $foo multi
220+
* line description
221+
* @throws Exception
222+
*/'),
223+
$addThrows,
224+
];
225+
226+
yield [
227+
self::nowdoc('
228+
/**
229+
* @param int $foo multi
230+
* line description
231+
* @return void
232+
*/'),
233+
self::nowdoc('
234+
/**
235+
* @param int $foo multi
236+
* line description
237+
* @return void
238+
* @throws Exception
239+
*/'),
240+
$addThrows,
241+
];
242+
197243
$removeLast = new class extends AbstractNodeVisitor {
198244

199245
public function enterNode(Node $node)

0 commit comments

Comments
 (0)