Skip to content

Commit 12ff875

Browse files
Merge pull request #10 from laravel/better-blade-parsing
Better Blade parsing
2 parents 2b87dd8 + acc138d commit 12ff875

File tree

2 files changed

+67
-33
lines changed

2 files changed

+67
-33
lines changed

app/Parser/Walker.php

+9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App\Contexts\Base;
66
use App\Support\Debugs;
77
use Microsoft\PhpParser\Node\SourceFileNode;
8+
use Microsoft\PhpParser\Node\Statement\InlineHtml;
89
use Microsoft\PhpParser\Parser;
910
use Microsoft\PhpParser\SkippedToken;
1011

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

3233
protected function documentSkipsClosingQuote()
3334
{
35+
if (count($this->sourceFile->statementList) === 1 && $this->sourceFile->statementList[0] instanceof InlineHtml) {
36+
// Probably Blade...
37+
$lastChar = substr($this->sourceFile->getFullText(), -1);
38+
$closesWithQuote = in_array($lastChar, ['"', "'"]);
39+
40+
return $closesWithQuote;
41+
}
42+
3443
foreach ($this->sourceFile->getDescendantNodesAndTokens() as $child) {
3544
if ($child instanceof SkippedToken && $child->getText($this->sourceFile->getFullText()) === "'") {
3645
return true;

app/Parsers/InlineHtmlParser.php

+58-33
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@
1010
use Microsoft\PhpParser\Parser;
1111
use Microsoft\PhpParser\Range;
1212
use Stillat\BladeParser\Document\Document;
13+
use Stillat\BladeParser\Nodes\BaseNode;
1314
use Stillat\BladeParser\Nodes\DirectiveNode;
1415
use Stillat\BladeParser\Nodes\EchoNode;
1516
use Stillat\BladeParser\Nodes\EchoType;
17+
use Stillat\BladeParser\Nodes\LiteralNode;
1618

1719
class InlineHtmlParser extends AbstractParser
1820
{
21+
protected $echoStrings = [
22+
'{!!' => '!!}',
23+
'{{{' => '}}}',
24+
'{{' => '}}',
25+
];
26+
1927
/**
2028
* @var Blade
2129
*/
@@ -42,6 +50,11 @@ public function parse(InlineHtml $node)
4250
protected function parseBladeContent($node)
4351
{
4452
foreach ($node->getNodes() as $child) {
53+
// TODO: Add other echo types as well
54+
if ($child instanceof LiteralNode) {
55+
$this->parseLiteralNode($child);
56+
}
57+
4558
if ($child instanceof DirectiveNode) {
4659
$this->parseBladeDirective($child);
4760
}
@@ -54,22 +67,18 @@ protected function parseBladeContent($node)
5467
}
5568
}
5669

57-
protected function parseBladeDirective(DirectiveNode $node)
70+
protected function doEchoParse(BaseNode $node, $prefix, $content)
5871
{
59-
if ($node->isClosingDirective || !$node->hasArguments()) {
60-
return;
61-
}
62-
63-
$methodUsed = '@' . $node->content;
64-
$safetyPrefix = 'directive';
65-
$snippet = "<?php\n" . str_repeat(' ', $node->getStartIndentationLevel()) . str_replace($methodUsed, $safetyPrefix . $node->content, $node->toString() . ';');
72+
$snippet = "<?php\n" . str_repeat(' ', $node->getStartIndentationLevel()) . str_replace($prefix, '', $content) . ';';
6673

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

69-
Settings::$calculatePosition = function (Range $range) use ($node, $safetyPrefix) {
76+
$suffix = $this->echoStrings[$prefix];
77+
78+
Settings::$calculatePosition = function (Range $range) use ($node, $prefix, $suffix) {
7079
if ($range->start->line === 1) {
71-
$range->start->character -= strlen($safetyPrefix) - 1;
72-
$range->end->character -= strlen($safetyPrefix) - 1;
80+
$range->start->character += mb_strlen($prefix);
81+
$range->end->character += mb_strlen($suffix);
7382
}
7483

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

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

83-
$child = $result->children[0];
92+
if (count($result->children) === 0) {
93+
return;
94+
}
8495

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

8798
$this->items[] = $child;
8899
}
89100

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

94-
$sourceFile = (new Parser)->parseSourceFile($snippet);
108+
$this->doEchoParse($node, $prefix, $node->content);
109+
}
110+
}
95111

96-
Settings::$calculatePosition = function (Range $range) use ($node) {
97-
$prefix = match ($node->type) {
98-
EchoType::RawEcho => '{!!',
99-
EchoType::TripleEcho => '{{{',
100-
default => '{{',
101-
};
112+
protected function parseBladeDirective(DirectiveNode $node)
113+
{
114+
if ($node->isClosingDirective || !$node->hasArguments()) {
115+
return;
116+
}
102117

103-
$suffix = match ($node->type) {
104-
EchoType::RawEcho => '!!}',
105-
EchoType::TripleEcho => '}}}',
106-
default => '}}',
107-
};
118+
$methodUsed = '@' . $node->content;
119+
$safetyPrefix = 'directive';
120+
$snippet = "<?php\n" . str_repeat(' ', $node->getStartIndentationLevel()) . str_replace($methodUsed, $safetyPrefix . $node->content, $node->toString() . ';');
108121

122+
$sourceFile = (new Parser)->parseSourceFile($snippet);
123+
124+
Settings::$calculatePosition = function (Range $range) use ($node, $safetyPrefix) {
109125
if ($range->start->line === 1) {
110-
$range->start->character += strlen($prefix);
111-
$range->end->character += strlen($suffix);
126+
$range->start->character -= mb_strlen($safetyPrefix) - 1;
127+
$range->end->character -= mb_strlen($safetyPrefix) - 1;
112128
}
113129

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

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

122-
if (count($result->children) === 0) {
123-
return;
124-
}
125-
126138
$child = $result->children[0];
127139

140+
$child->methodName = '@' . substr($child->methodName, mb_strlen($safetyPrefix));
141+
128142
$this->items[] = $child;
129143
}
144+
145+
protected function parseEchoNode(EchoNode $node)
146+
{
147+
$prefix = match ($node->type) {
148+
EchoType::RawEcho => '{!!',
149+
EchoType::TripleEcho => '{{{',
150+
default => '{{',
151+
};
152+
153+
$this->doEchoParse($node, $prefix, $node->innerContent);
154+
}
130155
}

0 commit comments

Comments
 (0)