Fix docblock tag descriptions

Newlines and whitespaces were not handled as before. This caused
issues for some users because our indent-recuction was broken.
The cause seems to be an upstream issue in phpstan parser which
is not resolved yet. But this work around post processing the tokens
helps us to make it work as before.
This commit is contained in:
Jaapio
2024-05-21 07:50:53 +02:00
parent 88a07d2628
commit 8c422ab43b
4 changed files with 127 additions and 6 deletions
@@ -23,7 +23,9 @@ use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use RuntimeException;
use function ltrim;
use function property_exists;
use function rtrim;
/**
* Factory class creating tags using phpstan's parser
@@ -42,18 +44,28 @@ class AbstractPHPStanFactory implements Factory
public function __construct(PHPStanFactory ...$factories)
{
$this->lexer = new Lexer();
$constParser = new ConstExprParser();
$this->parser = new PhpDocParser(new TypeParser($constParser), $constParser);
$this->lexer = new Lexer(true);
$constParser = new ConstExprParser(true, true, ['lines' => true, 'indexes' => true]);
$this->parser = new PhpDocParser(
new TypeParser($constParser, true, ['lines' => true, 'indexes' => true]),
$constParser,
true,
true,
['lines' => true, 'indexes' => true],
true
);
$this->factories = $factories;
}
public function create(string $tagLine, ?TypeContext $context = null): Tag
{
$tokens = new TokenIterator($this->lexer->tokenize($tagLine));
$tokens = $this->tokenizeLine($tagLine);
$ast = $this->parser->parseTag($tokens);
if (property_exists($ast->value, 'description') === true) {
$ast->value->setAttribute('description', $ast->value->description . $tokens->joinUntil(Lexer::TOKEN_END));
$ast->value->setAttribute(
'description',
$ast->value->description . $tokens->joinUntil(Lexer::TOKEN_END)
);
}
if ($context === null) {
@@ -75,4 +87,36 @@ class AbstractPHPStanFactory implements Factory
$ast->name
);
}
/**
* Solve the issue with the lexer not tokenizing the line correctly
*
* This method is a workaround for the lexer that includes newline tokens with spaces. For
* phpstan this isn't an issue, as it doesn't do a lot of things with the indentation of descriptions.
* But for us is important to keep the indentation of the descriptions, so we need to fix the lexer output.
*/
private function tokenizeLine(string $tagLine): TokenIterator
{
$tokens = $this->lexer->tokenize($tagLine);
$fixed = [];
foreach ($tokens as $token) {
if (($token[1] === Lexer::TOKEN_PHPDOC_EOL) && rtrim($token[0], " \t") !== $token[0]) {
$fixed[] = [
rtrim($token[Lexer::VALUE_OFFSET], " \t"),
Lexer::TOKEN_PHPDOC_EOL,
$token[2] ?? null,
];
$fixed[] = [
ltrim($token[Lexer::VALUE_OFFSET], "\n\r"),
Lexer::TOKEN_HORIZONTAL_WS,
($token[2] ?? null) + 1,
];
continue;
}
$fixed[] = $token;
}
return new TokenIterator($fixed);
}
}