Skip to content

Commit

Permalink
Merge pull request #10 from laravel/better-blade-parsing
Browse files Browse the repository at this point in the history
Better Blade parsing
  • Loading branch information
joetannenbaum authored Jan 16, 2025
2 parents 2b87dd8 + acc138d commit 12ff875
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 33 deletions.
9 changes: 9 additions & 0 deletions app/Parser/Walker.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Contexts\Base;
use App\Support\Debugs;
use Microsoft\PhpParser\Node\SourceFileNode;
use Microsoft\PhpParser\Node\Statement\InlineHtml;
use Microsoft\PhpParser\Parser;
use Microsoft\PhpParser\SkippedToken;

Expand All @@ -31,6 +32,14 @@ public function __construct(protected string $document, $debug = false)

protected function documentSkipsClosingQuote()
{
if (count($this->sourceFile->statementList) === 1 && $this->sourceFile->statementList[0] instanceof InlineHtml) {
// Probably Blade...
$lastChar = substr($this->sourceFile->getFullText(), -1);
$closesWithQuote = in_array($lastChar, ['"', "'"]);

return $closesWithQuote;
}

foreach ($this->sourceFile->getDescendantNodesAndTokens() as $child) {
if ($child instanceof SkippedToken && $child->getText($this->sourceFile->getFullText()) === "'") {
return true;
Expand Down
91 changes: 58 additions & 33 deletions app/Parsers/InlineHtmlParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@
use Microsoft\PhpParser\Parser;
use Microsoft\PhpParser\Range;
use Stillat\BladeParser\Document\Document;
use Stillat\BladeParser\Nodes\BaseNode;
use Stillat\BladeParser\Nodes\DirectiveNode;
use Stillat\BladeParser\Nodes\EchoNode;
use Stillat\BladeParser\Nodes\EchoType;
use Stillat\BladeParser\Nodes\LiteralNode;

class InlineHtmlParser extends AbstractParser
{
protected $echoStrings = [
'{!!' => '!!}',
'{{{' => '}}}',
'{{' => '}}',
];

/**
* @var Blade
*/
Expand All @@ -42,6 +50,11 @@ public function parse(InlineHtml $node)
protected function parseBladeContent($node)
{
foreach ($node->getNodes() as $child) {
// TODO: Add other echo types as well
if ($child instanceof LiteralNode) {
$this->parseLiteralNode($child);
}

if ($child instanceof DirectiveNode) {
$this->parseBladeDirective($child);
}
Expand All @@ -54,22 +67,18 @@ protected function parseBladeContent($node)
}
}

protected function parseBladeDirective(DirectiveNode $node)
protected function doEchoParse(BaseNode $node, $prefix, $content)
{
if ($node->isClosingDirective || !$node->hasArguments()) {
return;
}

$methodUsed = '@' . $node->content;
$safetyPrefix = 'directive';
$snippet = "<?php\n" . str_repeat(' ', $node->getStartIndentationLevel()) . str_replace($methodUsed, $safetyPrefix . $node->content, $node->toString() . ';');
$snippet = "<?php\n" . str_repeat(' ', $node->getStartIndentationLevel()) . str_replace($prefix, '', $content) . ';';

$sourceFile = (new Parser)->parseSourceFile($snippet);

Settings::$calculatePosition = function (Range $range) use ($node, $safetyPrefix) {
$suffix = $this->echoStrings[$prefix];

Settings::$calculatePosition = function (Range $range) use ($node, $prefix, $suffix) {
if ($range->start->line === 1) {
$range->start->character -= strlen($safetyPrefix) - 1;
$range->end->character -= strlen($safetyPrefix) - 1;
$range->start->character += mb_strlen($prefix);
$range->end->character += mb_strlen($suffix);
}

$range->start->line += $node->position->startLine - 2;
Expand All @@ -80,35 +89,42 @@ protected function parseBladeDirective(DirectiveNode $node)

$result = Parse::parse($sourceFile);

$child = $result->children[0];
if (count($result->children) === 0) {
return;
}

$child->methodName = '@' . substr($child->methodName, strlen($safetyPrefix));
$child = $result->children[0];

$this->items[] = $child;
}

protected function parseEchoNode(EchoNode $node)
protected function parseLiteralNode(LiteralNode $node)
{
$snippet = "<?php\n" . str_repeat(' ', $node->getStartIndentationLevel()) . $node->innerContent . ';';
foreach ($this->echoStrings as $prefix => $suffix) {
if (!str_starts_with($node->content, $prefix)) {
continue;
}

$sourceFile = (new Parser)->parseSourceFile($snippet);
$this->doEchoParse($node, $prefix, $node->content);
}
}

Settings::$calculatePosition = function (Range $range) use ($node) {
$prefix = match ($node->type) {
EchoType::RawEcho => '{!!',
EchoType::TripleEcho => '{{{',
default => '{{',
};
protected function parseBladeDirective(DirectiveNode $node)
{
if ($node->isClosingDirective || !$node->hasArguments()) {
return;
}

$suffix = match ($node->type) {
EchoType::RawEcho => '!!}',
EchoType::TripleEcho => '}}}',
default => '}}',
};
$methodUsed = '@' . $node->content;
$safetyPrefix = 'directive';
$snippet = "<?php\n" . str_repeat(' ', $node->getStartIndentationLevel()) . str_replace($methodUsed, $safetyPrefix . $node->content, $node->toString() . ';');

$sourceFile = (new Parser)->parseSourceFile($snippet);

Settings::$calculatePosition = function (Range $range) use ($node, $safetyPrefix) {
if ($range->start->line === 1) {
$range->start->character += strlen($prefix);
$range->end->character += strlen($suffix);
$range->start->character -= mb_strlen($safetyPrefix) - 1;
$range->end->character -= mb_strlen($safetyPrefix) - 1;
}

$range->start->line += $node->position->startLine - 2;
Expand All @@ -119,12 +135,21 @@ protected function parseEchoNode(EchoNode $node)

$result = Parse::parse($sourceFile);

if (count($result->children) === 0) {
return;
}

$child = $result->children[0];

$child->methodName = '@' . substr($child->methodName, mb_strlen($safetyPrefix));

$this->items[] = $child;
}

protected function parseEchoNode(EchoNode $node)
{
$prefix = match ($node->type) {
EchoType::RawEcho => '{!!',
EchoType::TripleEcho => '{{{',
default => '{{',
};

$this->doEchoParse($node, $prefix, $node->innerContent);
}
}

0 comments on commit 12ff875

Please sign in to comment.