Skip to content

Commit 387cbd8

Browse files
authored
Merge pull request #844 from phpDocumentor/bugfix/mdsections
[TASK] handle section creation in NodeTransformer
2 parents 04a39bf + 060bb46 commit 387cbd8

File tree

21 files changed

+309
-89
lines changed

21 files changed

+309
-89
lines changed

packages/guides/src/Compiler/DocumentNodeTraverser.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ private function traverseForTransformer(
5555

5656
if ($supports) {
5757
$transformed = $transformer->enterNode($node, $compilerContext);
58-
$shadowNode->getParent()?->replaceChild($node, $transformed);
58+
if ($transformed !== $node) {
59+
$shadowNode->getParent()?->replaceChild($node, $transformed);
60+
}
5961
}
6062

6163
foreach ($shadowNode->getChildren() as $shadowChild) {
@@ -68,7 +70,9 @@ private function traverseForTransformer(
6870

6971
$transformed = $transformer->leaveNode($node, $compilerContext);
7072
if ($transformed !== null) {
71-
$shadowNode->getParent()?->replaceChild($node, $transformed);
73+
if ($transformed !== $node) {
74+
$shadowNode->getParent()?->replaceChild($node, $transformed);
75+
}
7276

7377
return;
7478
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\Compiler\NodeTransformers;
15+
16+
use phpDocumentor\Guides\Compiler\CompilerContext;
17+
use phpDocumentor\Guides\Compiler\NodeTransformer;
18+
use phpDocumentor\Guides\Nodes\DocumentNode;
19+
use phpDocumentor\Guides\Nodes\Node;
20+
use phpDocumentor\Guides\Nodes\SectionNode;
21+
use phpDocumentor\Guides\Nodes\TitleNode;
22+
23+
use function array_pop;
24+
use function count;
25+
use function end;
26+
27+
use const PHP_INT_MAX;
28+
29+
/** @implements NodeTransformer<Node> */
30+
final class SectionCreationTransformer implements NodeTransformer
31+
{
32+
/** @var SectionNode[] $sectionStack */
33+
private array $sectionStack = [];
34+
35+
public function enterNode(Node $node, CompilerContext $compilerContext): Node
36+
{
37+
if (!$compilerContext->getShadowTree()->getParent()?->getNode() instanceof DocumentNode) {
38+
return $node;
39+
}
40+
41+
if (!$node instanceof TitleNode) {
42+
$lastSection = end($this->sectionStack);
43+
if ($lastSection instanceof SectionNode) {
44+
$lastSection->addChildNode($node);
45+
}
46+
}
47+
48+
return $node;
49+
}
50+
51+
public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null
52+
{
53+
if (!$compilerContext->getShadowTree()->getParent()?->getNode() instanceof DocumentNode) {
54+
return $node;
55+
}
56+
57+
if ($node instanceof SectionNode) {
58+
return $node;
59+
}
60+
61+
if (count($this->sectionStack) === 0 && !$node instanceof TitleNode) {
62+
return $node;
63+
}
64+
65+
if (count($this->sectionStack) > 0 && $compilerContext->getShadowTree()->isLastChildOfParent()) {
66+
$lastSection = end($this->sectionStack);
67+
while ($lastSection?->getTitle()->getLevel() > 1) {
68+
$lastSection = array_pop($this->sectionStack);
69+
}
70+
71+
return $lastSection;
72+
}
73+
74+
if (!$node instanceof TitleNode) {
75+
// Remove all nodes that will be attached to a section
76+
return null;
77+
}
78+
79+
$lastSection = end($this->sectionStack);
80+
if ($lastSection instanceof SectionNode && $node !== $lastSection->getTitle() && $node->getLevel() <= $lastSection->getTitle()->getLevel()) {
81+
while (end($this->sectionStack) instanceof SectionNode && $node !== end($this->sectionStack)->getTitle() && $node->getLevel() <= end($this->sectionStack)->getTitle()->getLevel()) {
82+
$lastSection = array_pop($this->sectionStack);
83+
}
84+
85+
$newSection = new SectionNode($node);
86+
// Attach the new section to the last one still on the stack if there still is one
87+
if (end($this->sectionStack) instanceof SectionNode) {
88+
end($this->sectionStack)->addChildNode($newSection);
89+
}
90+
91+
$this->sectionStack[] = $newSection;
92+
93+
return $lastSection?->getTitle()->getLevel() === 1 ? $lastSection : null;
94+
}
95+
96+
$newSection = new SectionNode($node);
97+
if ($lastSection instanceof SectionNode) {
98+
$lastSection->addChildNode($newSection);
99+
}
100+
101+
$this->sectionStack[] = $newSection;
102+
103+
return null;
104+
}
105+
106+
public function supports(Node $node): bool
107+
{
108+
return true;
109+
}
110+
111+
public function getPriority(): int
112+
{
113+
// Should run as first transformer
114+
return PHP_INT_MAX;
115+
}
116+
}

packages/guides/src/Compiler/ShadowTree/TreeNode.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
use function array_unshift;
2222
use function array_values;
23+
use function count;
2324

2425
/** @template-covariant TNode of Node */
2526
final class TreeNode
@@ -198,4 +199,13 @@ public function findPosition(Node $node): int|null
198199

199200
return null;
200201
}
202+
203+
public function isLastChildOfParent(): bool
204+
{
205+
if ($this->parent === null) {
206+
return false;
207+
}
208+
209+
return $this->parent->findPosition($this->node) === count($this->parent->getChildren()) - 1;
210+
}
201211
}
Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
<!-- content start -->
2-
<h1>Markdown Blockquotes</h1>
2+
<div class="section" id="markdown-blockquotes">
3+
<h1>Markdown Blockquotes</h1>
34

4-
<blockquote><p>This is a blockquote.It can span multiple lines.</p></blockquote>
5+
<blockquote><p>This is a blockquote.It can span multiple lines.</p></blockquote>
56

6-
<h2>Blockquotes with Multiple Paragraphs</h2>
7+
<div class="section" id="blockquotes-with-multiple-paragraphs">
8+
<h2>Blockquotes with Multiple Paragraphs</h2>
79

8-
<blockquote><p>Dorothy followed her through many of the beautiful rooms in her castle.</p><p>The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.</p></blockquote>
10+
<blockquote><p>Dorothy followed her through many of the beautiful rooms in her castle.</p><p>The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.</p></blockquote>
911

10-
<h2>Blockquotes with Other Elements</h2>
12+
</div>
1113

12-
<blockquote><p>The quarterly results look great!</p>
14+
<div class="section" id="blockquotes-with-other-elements">
15+
<h2>Blockquotes with Other Elements</h2>
16+
17+
<blockquote><p>The quarterly results look great!</p>
1318

1419
<ul>
1520
<li class="dash"><p>Revenue was off the chart.</p></li>
@@ -19,10 +24,17 @@ <h2>Blockquotes with Other Elements</h2>
1924
</ul>
2025
<p><em>Everything</em> is going according to <strong>plan</strong>.</p></blockquote>
2126

22-
<h2>Blockquotes Best Practices</h2>
27+
</div>
28+
29+
<div class="section" id="blockquotes-best-practices">
30+
<h2>Blockquotes Best Practices</h2>
31+
32+
<p>Try to put a blank line before...</p>
33+
<blockquote><p>This is a blockquote</p></blockquote>
34+
35+
<p>...and after a blockquote.</p>
36+
</div>
2337

24-
<p>Try to put a blank line before...</p>
25-
<blockquote><p>This is a blockquote</p></blockquote>
38+
</div>
2639

27-
<p>...and after a blockquote.</p>
2840
<!-- content end -->
Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
11
<!-- content start -->
2-
<h1>Markdown with Code Blocks</h1>
2+
<div class="section" id="markdown-with-code-blocks">
3+
<h1>Markdown with Code Blocks</h1>
34

4-
<pre><code class="language-">&lt;html&gt;
5+
<pre><code class="language-">&lt;html&gt;
56
&lt;head&gt;
67
&lt;/head&gt;
78
&lt;/html&gt;
89
</code></pre>
9-
<h2>Fenced Code Blocks</h2>
10+
<div class="section" id="fenced-code-blocks">
11+
<h2>Fenced Code Blocks</h2>
1012

11-
<pre><code class="language-">{
13+
<pre><code class="language-">{
1214
&quot;firstName&quot;: &quot;John&quot;,
1315
&quot;lastName&quot;: &quot;Smith&quot;,
1416
&quot;age&quot;: 25
1517
}
1618
</code></pre>
17-
<h2>Fenced Code Block with caption</h2>
19+
</div>
1820

19-
<pre><code class="language-">procedure startSwinging(swing, child)
21+
<div class="section" id="fenced-code-block-with-caption">
22+
<h2>Fenced Code Block with caption</h2>
23+
24+
<pre><code class="language-">procedure startSwinging(swing, child)
2025
while child.isComfortable()
2126
swing.giveGentlePush()
2227
waitForNextIteration()
2328
end while
2429
end procedure
2530
</code></pre>
31+
</div>
32+
33+
</div>
34+
2635
<!-- content end -->
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<!-- content start -->
2-
<h1>Markdown with emphasis</h1>
2+
<div class="section" id="markdown-with-emphasis">
3+
<h1>Markdown with emphasis</h1>
4+
5+
<p><em>Italic</em> or <em>Italic</em><strong>Bold</strong> or <strong>Bold</strong></p>
6+
</div>
37

4-
<p><em>Italic</em> or <em>Italic</em><strong>Bold</strong> or <strong>Bold</strong></p>
58
<!-- content end -->
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<!-- content start -->
2-
<h1>Markdown with html</h1>
2+
<div class="section" id="markdown-with-html">
3+
<h1>Markdown with html</h1>
4+
5+
<p>In files of text, where words take flight,Markdown weaves its magic, bold and bright.Hashes and stars, a simple code,A poet&#039;s playground, where stories unfold. &lt;BR&gt;</p>
6+
</div>
37

4-
<p>In files of text, where words take flight,Markdown weaves its magic, bold and bright.Hashes and stars, a simple code,A poet&#039;s playground, where stories unfold. &lt;BR&gt;</p>
58
<!-- content end -->
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
<!-- content start -->
2-
<h1>Markdown with html</h1>
2+
<div class="section" id="markdown-with-html">
3+
<h1>Markdown with html</h1>
34

4-
<p>&lt;div align=&quot;center&quot;&gt;
5+
<p>&lt;div align=&quot;center&quot;&gt;
56
In files of text, where words take flight,
67
Markdown weaves its magic, bold and bright.
78
Hashes and stars, a simple code,
89
A poet&#039;s playground, where stories unfold.
910
&lt;/div&gt;</p>
11+
</div>
12+
1013
<!-- content end -->
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<!-- content start -->
2-
<h1>Images with and without alt text</h1>
2+
<div class="section" id="images-with-and-without-alt-text">
3+
<h1>Images with and without alt text</h1>
4+
5+
<p><img src="https://example.org/example.png" alt="with alt Text"/></p>
6+
<p><img src="https://example.org/example2.png" alt=""/></p>
7+
</div>
38

4-
<p><img src="https://example.org/example.png" alt="with alt Text"/></p>
5-
<p><img src="https://example.org/example2.png" alt=""/></p>
69
<!-- content end -->
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<!-- content start -->
2-
<h1>Markdown Image</h1>
2+
<div class="section" id="markdown-image">
3+
<h1>Markdown Image</h1>
4+
5+
<p><img src="hero-illustration.svg" alt="Hero Illustrations"/></p>
6+
</div>
37

4-
<p><img src="hero-illustration.svg" alt="Hero Illustrations"/></p>
58
<!-- content end -->

0 commit comments

Comments
 (0)