Skip to content

Commit 5d9f4ab

Browse files
committed
wip
1 parent e26572b commit 5d9f4ab

File tree

8 files changed

+122
-6
lines changed

8 files changed

+122
-6
lines changed

packages/debug/src/Debug.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Tempest\EventBus\EventBus;
1313
use Tempest\Highlight\Themes\TerminalStyle;
1414
use Tempest\Support\Filesystem;
15+
use Throwable;
1516

1617
final readonly class Debug
1718
{
@@ -27,7 +28,7 @@ public static function resolve(): self
2728
config: GenericContainer::instance()->get(DebugConfig::class),
2829
eventBus: GenericContainer::instance()->get(EventBus::class),
2930
);
30-
} catch (Exception) {
31+
} catch (Throwable) {
3132
return new self();
3233
}
3334
}

packages/view/src/Elements/ElementFactory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ private function makeElement(Token $token, ?Element $parent): ?Element
6969
return new TextElement(text: $text);
7070
}
7171

72+
if ($token->type === TokenType::WHITESPACE) {
73+
return new WhitespaceElement($token->content);
74+
}
75+
7276
if (! $token->tag || $token->type === TokenType::COMMENT || $token->type === TokenType::PHP) {
7377
return new RawElement(token: $token, tag: null, content: $token->compile());
7478
}

packages/view/src/Elements/IsElement.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ public function setPrevious(?Element $previous): self
110110

111111
public function getPrevious(): ?Element
112112
{
113+
if ($this->previous instanceof WhitespaceElement) {
114+
return $this->previous->getPrevious();
115+
}
116+
113117
return $this->previous;
114118
}
115119

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\View\Elements;
6+
7+
use Tempest\View\Element;
8+
9+
final class WhitespaceElement implements Element
10+
{
11+
use IsElement;
12+
13+
public function __construct(
14+
public string $content,
15+
) {}
16+
17+
public function compile(): string
18+
{
19+
return $this->content;
20+
}
21+
}

packages/view/src/Parser/TempestViewLexer.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ final class TempestViewLexer
1414

1515
public function __construct(
1616
private readonly string $html,
17-
) {
17+
)
18+
{
1819
$this->current = $this->html[$this->position] ?? null;
1920
}
2021

@@ -35,6 +36,8 @@ public function lex(): TokenCollection
3536
$tokens = [...$tokens, ...$this->lexCharacterData()];
3637
} elseif ($this->comesNext('<')) {
3738
$tokens = [...$tokens, ...$this->lexTag()];
39+
} elseif ($this->comesNext(' ') || $this->comesNext(PHP_EOL)) {
40+
$tokens[] = $this->lexWhitespace();
3841
} else {
3942
$tokens[] = $this->lexContent();
4043
}
@@ -214,6 +217,23 @@ private function lexDoctype(): Token
214217
return new Token($buffer, TokenType::DOCTYPE);
215218
}
216219

220+
private function lexWhitespace(): Token
221+
{
222+
$buffer = '';
223+
224+
while ($this->current !== null) {
225+
$seek = $this->seek();
226+
227+
if ($seek !== ' ' && $seek !== PHP_EOL) {
228+
break;
229+
}
230+
231+
$buffer .= $this->consume();
232+
}
233+
234+
return new Token($buffer, TokenType::WHITESPACE);
235+
}
236+
217237
private function lexCharacterData(): array
218238
{
219239
$tokens = [

packages/view/src/Parser/TokenType.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ enum TokenType
1818
case DOCTYPE;
1919
case CHARACTER_DATA_OPEN;
2020
case CHARACTER_DATA_CLOSE;
21+
case WHITESPACE;
2122
}

packages/view/tests/TempestViewLexerTest.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,40 @@ class=', TokenType::ATTRIBUTE_NAME),
173173
new Token("\n>", TokenType::OPEN_TAG_END),
174174
new Token('
175175
176-
', TokenType::CONTENT),
176+
', TokenType::WHITESPACE),
177177
new Token('</div>', TokenType::CLOSING_TAG),
178178
],
179179
actual: $tokens,
180180
);
181181
}
182182

183+
public function test_whitespace(): void
184+
{
185+
$html = <<<'HTML'
186+
<p><strong>Test</strong> <em>Test</em></p>
187+
HTML;
188+
189+
$tokens = new TempestViewLexer($html)->lex();
190+
191+
$this->assertTokens(
192+
expected: [
193+
new Token('<p', TokenType::OPEN_TAG_START),
194+
new Token('>', TokenType::OPEN_TAG_END),
195+
new Token('<strong', TokenType::OPEN_TAG_START),
196+
new Token('>', TokenType::OPEN_TAG_END),
197+
new Token('Test', TokenType::CONTENT),
198+
new Token('</strong>', TokenType::CLOSING_TAG),
199+
new Token(' ', TokenType::WHITESPACE),
200+
new Token('<em', TokenType::OPEN_TAG_START),
201+
new Token('>', TokenType::OPEN_TAG_END),
202+
new Token('Test', TokenType::CONTENT),
203+
new Token('</em>', TokenType::CLOSING_TAG),
204+
new Token('</p>', TokenType::CLOSING_TAG)
205+
],
206+
actual: $tokens,
207+
);
208+
}
209+
183210
public function test_lexer_with_falsy_values(): void
184211
{
185212
$html = <<<'HTML'
@@ -317,7 +344,8 @@ public function test_cdata(): void
317344
{
318345
$tokens = new TempestViewLexer(<<<'RSS'
319346
<title><![CDATA[ {{ $post['title'] }} ]]></title>
320-
RSS)->lex();
347+
RSS,
348+
)->lex();
321349

322350
$this->assertTokens(
323351
expected: [
@@ -336,7 +364,8 @@ public function test_xml(): void
336364
{
337365
$tokens = new TempestViewLexer(<<<'XML'
338366
<?xml version="1.0" encoding="UTF-8" ?>
339-
XML)->lex();
367+
XML,
368+
)->lex();
340369

341370
$this->assertTokens(
342371
expected: [

tests/Integration/View/TempestViewRendererTest.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ public function test_default_slot(): void
292292

293293
public function test_implicit_default_slot(): void
294294
{
295-
$this->assertStringEqualsStringIgnoringLineEndings(
295+
$this->assertSnippetsMatch(
296296
<<<'HTML'
297297
<div class="base">
298298
@@ -891,4 +891,40 @@ public function test_discovery_locations_are_passed_to_compiler(): void
891891

892892
$this->assertSnippetsMatch('<div>Hi</div>', $html);
893893
}
894+
895+
public function test_whitespace_between_inline_elements_is_preserved(): void
896+
{
897+
/** @var TempestViewRenderer $renderer */
898+
$renderer = $this->get(TempestViewRenderer::class);
899+
900+
$this->assertSame(
901+
'<p><strong>Test</strong> <em>Test</em></p>',
902+
$renderer->render('<p><strong>Test</strong> <em>Test</em></p>'),
903+
);
904+
}
905+
906+
public function test_whitespace_introduced_by_line_breaks_is_preserved(): void
907+
{
908+
/** @var TempestViewRenderer $renderer */
909+
$renderer = $this->get(TempestViewRenderer::class);
910+
911+
$this->assertSame(
912+
'<p><strong>Test</strong> <em>Test</em></p>',
913+
$renderer->render('<p><strong>Test</strong>
914+
<em>Test</em></p>'),
915+
);
916+
}
917+
918+
public function test_whitespace_with_blank_lines_between_inline_elements_is_preserved(): void
919+
{
920+
/** @var TempestViewRenderer $renderer */
921+
$renderer = $this->get(TempestViewRenderer::class);
922+
923+
$this->assertSame(
924+
'<p><strong>Test</strong> <em>Test</em></p>',
925+
$renderer->render('<p><strong>Test</strong>
926+
927+
<em>Test</em></p>'),
928+
);
929+
}
894930
}

0 commit comments

Comments
 (0)