Skip to content

Commit ec9d565

Browse files
committed
Fix for twigphp/issues/3685 (markdown leading linebreaks and indentation)
1 parent ab36653 commit ec9d565

File tree

2 files changed

+82
-37
lines changed

2 files changed

+82
-37
lines changed

extra/markdown-extra/MarkdownRuntime.php

+30-4
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,37 @@ public function __construct(MarkdownInterface $converter)
2222

2323
public function convert(string $body): string
2424
{
25-
// remove indentation
26-
if ($white = substr($body, 0, strspn($body, " \t\r\n\0\x0B"))) {
27-
$body = preg_replace("{^$white}m", '', $body);
28-
}
25+
$body = $this->commonWhitespace($body);
26+
$body = $this->removeIndentation($body);
2927

3028
return $this->converter->convert($body);
3129
}
30+
31+
protected function commonWhitespace(string $body): string
32+
{
33+
return str_replace(["\t", "\0", "\x0B"], [' ', '', ''], $body);
34+
}
35+
36+
protected function removeIndentation(string $body): string
37+
{
38+
$indent = $this->minIndentations($body);
39+
if ($indent > 0) {
40+
$body = preg_replace("{^ {{$indent}}}m", '', $body);
41+
}
42+
43+
return $body;
44+
}
45+
46+
protected function minIndentations(string $body): int
47+
{
48+
$non_empty_lines = preg_split('%(\r|\n)%', $body, -1, PREG_SPLIT_NO_EMPTY);
49+
50+
$list = [];
51+
foreach ($non_empty_lines as $line)
52+
{
53+
$list[] = strspn($line, " ");
54+
}
55+
56+
return min($list);
57+
}
3258
}

extra/markdown-extra/Tests/FunctionalTest.php

+52-33
Original file line numberDiff line numberDiff line change
@@ -27,60 +27,79 @@ class FunctionalTest extends TestCase
2727
/**
2828
* @dataProvider getMarkdownTests
2929
*/
30-
public function testMarkdown(string $template, string $expected): void
30+
public function testMarkdown(string $markdown, string $expected): void
3131
{
3232
foreach ([LeagueMarkdown::class, ErusevMarkdown::class, /*MichelfMarkdown::class,*/ DefaultMarkdown::class] as $class) {
33-
$twig = new Environment(new ArrayLoader([
34-
'index' => $template,
35-
'html' => <<<EOF
36-
Hello
37-
=====
33+
$twig = $this->getTwig($class, [
34+
'apply' => "{% apply markdown_to_html %}\n{$markdown}\n{% endapply %}",
35+
'include' => "{{ include('md')|markdown_to_html }}",
36+
'indent' => "{{ include('indent_md')|markdown_to_html }}",
37+
'md' => $markdown,
38+
'indent_md' => ltrim(str_replace("\n", "\n\t", "\n$markdown"), "\n"),
39+
]);
3840

39-
Great!
40-
EOF
41-
]));
42-
$twig->addExtension(new MarkdownExtension());
43-
$twig->addRuntimeLoader(new class($class) implements RuntimeLoaderInterface {
44-
private $class;
41+
$twig_md = trim($twig->render('apply'));
42+
$this->assertMatchesRegularExpression('{'.$expected.'}m', $twig_md);
4543

46-
public function __construct(string $class)
47-
{
48-
$this->class = $class;
49-
}
44+
$twig_md = trim($twig->render('include'));
45+
$this->assertMatchesRegularExpression('{'.$expected.'}m', $twig_md);
5046

51-
public function load($c)
52-
{
53-
if (MarkdownRuntime::class === $c) {
54-
return new $c(new $this->class());
55-
}
56-
}
57-
});
58-
$this->assertMatchesRegularExpression('{'.$expected.'}m', trim($twig->render('index')));
47+
$twig_md = trim($twig->render('indent'));
48+
$this->assertMatchesRegularExpression('{'.$expected.'}m', $twig_md);
49+
50+
$lib_md = trim((new $class)->convert($markdown));
51+
$this->assertEquals($lib_md, $twig_md, "Twig output versus {$class} output.");
5952
}
6053
}
6154

6255
public function getMarkdownTests()
6356
{
6457
return [
6558
[<<<EOF
66-
{% apply markdown_to_html %}
6759
Hello
6860
=====
6961
7062
Great!
71-
{% endapply %}
7263
EOF
7364
, "<h1>Hello</h1>\n+<p>Great!</p>"],
65+
7466
[<<<EOF
75-
{% apply markdown_to_html %}
76-
Hello
77-
=====
7867
79-
Great!
80-
{% endapply %}
68+
Leading
69+
70+
Linebreak
8171
EOF
82-
, "<h1>Hello</h1>\n+<p>Great!</p>"],
83-
["{{ include('html')|markdown_to_html }}", "<h1>Hello</h1>\n+<p>Great!</p>"],
72+
, "<p>Leading</p>\n+<p>Linebreak</p>"],
73+
74+
[<<<EOF
75+
Code
76+
77+
Paragraph
78+
EOF
79+
, "<pre><code>Code\n?</code></pre>\n+<p>Paragraph</p>"],
8480
];
8581
}
82+
83+
private function getTwig(string $class, array $templates): Environment
84+
{
85+
$twig = new Environment(new ArrayLoader($templates));
86+
$twig->addExtension(new MarkdownExtension());
87+
$twig->addRuntimeLoader(new class($class) implements RuntimeLoaderInterface {
88+
private $class;
89+
90+
public function __construct(string $class)
91+
{
92+
$this->class = $class;
93+
}
94+
95+
public function load($c)
96+
{
97+
if (MarkdownRuntime::class === $c)
98+
{
99+
return new $c(new $this->class());
100+
}
101+
}
102+
});
103+
return $twig;
104+
}
86105
}

0 commit comments

Comments
 (0)