Skip to content

Commit f06650f

Browse files
committed
Keep paragraphs apart in ApiGen descriptions
phpdoc-parser puts each paragraph of a PHPDoc into its own text node. ApiGen dropped the empty nodes between them and joined the rest with a space, so every description rendered as one paragraph. Lists, tables and code fences that did not start the description were not recognized, and short descriptions in namespace listings showed the whole text. Amp-Thread-ID: https://ampcode.com/threads/T-01a176c6-528a-7430-b296-e66930d153b4
1 parent 48ddaa1 commit f06650f

3 files changed

Lines changed: 48 additions & 1 deletion

File tree

‎apigen/apigen.neon‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ services:
99
renderer.filter:
1010
factory: PHPStan\ApiGen\RendererFilter
1111

12+
analyze.phpDocResolver:
13+
factory: PHPStan\ApiGen\PhpDocResolver
14+
1215
commonMark:
1316
factory: PHPStan\ApiGen\MarkdownConverter

‎apigen/src/MarkdownConverter.php‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
use League\CommonMark\Extension\CommonMark\Node\Inline\HtmlInline;
88
use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
99
use League\CommonMark\MarkdownConverter as BaseMarkdownConverter;
10+
use League\CommonMark\Output\RenderedContentInterface;
11+
use function str_replace;
1012

11-
/** GitHub-flavored Markdown converter that escapes generic types written in PHPDoc descriptions. */
13+
/** GitHub-flavored Markdown converter for PHPDoc descriptions that escapes generic types and keeps paragraphs apart. */
1214
final class MarkdownConverter extends BaseMarkdownConverter
1315
{
1416

@@ -22,4 +24,11 @@ public function __construct()
2224
parent::__construct($environment);
2325
}
2426

27+
public function convert(string $input): RenderedContentInterface
28+
{
29+
// ApiGen joins description nodes with a space, which would indent the paragraph
30+
// after each separator and break code fences that start it.
31+
return parent::convert(str_replace(' ' . PhpDocResolver::PARAGRAPH_SEPARATOR . ' ', PhpDocResolver::PARAGRAPH_SEPARATOR, $input));
32+
}
33+
2534
}

‎apigen/src/PhpDocResolver.php‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\ApiGen;
4+
5+
use ApiGen\Analyzer\NodeVisitors\PhpDocResolver as BasePhpDocResolver;
6+
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
7+
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
8+
use function array_merge;
9+
10+
/**
11+
* phpdoc-parser puts each paragraph into its own text node with an empty text node
12+
* between them. ApiGen drops the empty nodes, which merges all paragraphs into one,
13+
* so they are kept as blank lines here.
14+
*/
15+
final class PhpDocResolver extends BasePhpDocResolver
16+
{
17+
18+
public const PARAGRAPH_SEPARATOR = "\n\n";
19+
20+
protected function resolvePhpDoc(PhpDocNode $phpDoc): PhpDocNode
21+
{
22+
$children = [];
23+
foreach ($phpDoc->children as $child) {
24+
if ($child instanceof PhpDocTextNode && $child->text === '') {
25+
$children[] = new PhpDocTextNode(self::PARAGRAPH_SEPARATOR);
26+
continue;
27+
}
28+
29+
$children = array_merge($children, parent::resolvePhpDoc(new PhpDocNode([$child]))->children);
30+
}
31+
32+
return new PhpDocNode($children);
33+
}
34+
35+
}

0 commit comments

Comments
 (0)