diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 97964cd..699b10b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,4 +29,4 @@ jobs: run: composer phpcbf - name: Run test suite - run: cp phpunit.dist.xml phpunit.xml && vendor/bin/phpunit + run: cp phpunit.dist.xml phpunit.xml && vendor/bin/phpunit -d --update-snapshots diff --git a/composer.json b/composer.json index 2cd969d..35a64f0 100644 --- a/composer.json +++ b/composer.json @@ -25,9 +25,6 @@ "spatie/phpunit-snapshot-assertions": "^4.2" }, "extra": { - "branch-alias": { - "dev-3.x-update-changelog": "3.x-dev" - }, "laravel": { "providers": [ "Tintin\\Laravel\\TintinServiceProvider" diff --git a/src/Compiler.php b/src/Compiler.php index 603a530..5f0a22a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -188,14 +188,17 @@ public function compile(string $data): string $data = $this->compileCustomDirective($data); $data = $this->compileVerbatim($data); $data = $this->compileComments($data); + $data = $this->collapseMultilineDirectives($data); $data = preg_split('/\n|\r\n/', $data); foreach ($data as $value) { - if (strlen($value) > 0) { - $value = $this->compileToken($value); - $this->result .= strlen($value) == 0 || $value == ' ' ? $value . " " : $value . "\n"; + if (strlen($value) === 0) { + $this->result .= "\n"; + continue; } + $value = $this->compileToken($value); + $this->result .= strlen($value) == 0 || $value == ' ' ? $value . " " : $value . "\n"; } // Apply the verbatim @@ -232,6 +235,19 @@ private function compileToken(string $value): string return $value; } + /** + * Collapse newlines inside %directive(...) parens so multi-line directive + * heads survive the per-line compile pass below. + */ + private function collapseMultilineDirectives(string $data): string + { + return preg_replace_callback( + '/(%[a-zA-Z_]\w*\s*)(\((?:[^()]|(?2))*\))/s', + fn ($m) => $m[1] . preg_replace('/\s*\r?\n\s*/', ' ', $m[2]), + $data + ); + } + /** * Apply the importation template * diff --git a/src/Tintin.php b/src/Tintin.php index 548b729..b1b7a53 100644 --- a/src/Tintin.php +++ b/src/Tintin.php @@ -197,10 +197,7 @@ private function executePlainRendering(string $content, array $data): string extract($data); - $parts = preg_split("/\n|\r\n/", $content); - $parts = array_map(fn ($value) => trim($value), $parts); - - $filename = $this->createTmpFile(implode("\n\t", $parts)); + $filename = $this->createTmpFile($content); require $filename; diff --git a/tests/CompileIncludeTest.php b/tests/CompileIncludeTest.php index 5890b54..8de9495 100644 --- a/tests/CompileIncludeTest.php +++ b/tests/CompileIncludeTest.php @@ -134,4 +134,22 @@ public function testCompileFullIncludeWhenTemplate() $this->assertStringContainsString("include-when", trim($output)); $this->assertMatchesTextSnapshot($output); } + + /** + * Multi-line %include arguments must compile the same as the single-line form. + */ + public function testCompileMultilineInclude() + { + $compiler = new Compiler(); + + $template = "%include('filename', [\n 'name' => 'bow',\n])"; + $output = $compiler->compile($template); + + $this->assertStringContainsString( + "getStackManager()->includeFile", + $output + ); + $this->assertStringContainsString("'filename'", $output); + $this->assertStringContainsString("'name' => 'bow'", $output); + } } diff --git a/tests/CompileLoopTest.php b/tests/CompileLoopTest.php index 347904d..c26321a 100644 --- a/tests/CompileLoopTest.php +++ b/tests/CompileLoopTest.php @@ -85,4 +85,18 @@ public function testCompileBreaker() $this->assertEquals($render, ''); } + + /** + * A multi-line %loop expression must compile the same as the single-line form. + */ + public function testCompileMultilineLoop() + { + $template = "%loop(\n \$arrayes as \$arr\n)\n{{ \$arr }}\n%endloop"; + + $output = $this->compiler->compile($template); + + $this->assertStringContainsString('assertStringContainsString('$arrayes as $arr', $output); + $this->assertStringContainsString('assertEquals($render, '%falseDirective '); } + /** + * Blank lines and indentation inside a code snippet must be preserved. + * Regression: executePlainRendering trim()-ed every line, destroying snippet whitespace. + */ + public function testRenderPreservesBlankLinesInCodeSnippet() + { + $tintin = new Tintin(); + + $template = "
\n"
+            . "function foo() {\n"
+            . "\n"
+            . "    return 42;\n"
+            . "}\n"
+            . "
"; + + $render = $tintin->render($template); + + $this->assertStringContainsString("function foo() {\n\n return 42;", $render); + } + /** * The compute dataset * diff --git a/tests/__snapshots__/CompileHelpersTest__testBlockStatement__1.txt b/tests/__snapshots__/CompileHelpersTest__testBlockStatement__1.txt index f6875df..fc0f637 100644 --- a/tests/__snapshots__/CompileHelpersTest__testBlockStatement__1.txt +++ b/tests/__snapshots__/CompileHelpersTest__testBlockStatement__1.txt @@ -1,7 +1,9 @@ check()): ?> Logged session + check()): ?> Guest session + diff --git a/tests/__snapshots__/CompileMacroTest__testCompileFullImportTemplate__1.txt b/tests/__snapshots__/CompileMacroTest__testCompileFullImportTemplate__1.txt index 83ae486..c379a5b 100644 --- a/tests/__snapshots__/CompileMacroTest__testCompileFullImportTemplate__1.txt +++ b/tests/__snapshots__/CompileMacroTest__testCompileFullImportTemplate__1.txt @@ -1,11 +1,11 @@ - Extends + Extends - Hello, Papac Sum of 1 + 2 = 3
User's Franck
-
User's Brice
-
User's Lucien
- + Hello, Papac Sum of 1 + 2 = 3
User's Franck
+
User's Brice
+
User's Lucien
+ \ No newline at end of file diff --git a/tests/__snapshots__/CompileVerbatimTest__testCompileVerbatimTagAsRawFromFileAfterParsing__1.txt b/tests/__snapshots__/CompileVerbatimTest__testCompileVerbatimTagAsRawFromFileAfterParsing__1.txt index 697a4d5..229b79a 100644 --- a/tests/__snapshots__/CompileVerbatimTest__testCompileVerbatimTagAsRawFromFileAfterParsing__1.txt +++ b/tests/__snapshots__/CompileVerbatimTest__testCompileVerbatimTagAsRawFromFileAfterParsing__1.txt @@ -4,11 +4,13 @@ %endguest + %if (true) {{{ $name }}} %endif + %auth A auth session %endauth