Skip to content

Commit 25b616f

Browse files
authored
Merge pull request #1309 from phpDocumentor/fix/multiline-admonitions
Add support for multiline admonitions in markdown
2 parents 473f111 + 3ed587f commit 25b616f

7 files changed

Lines changed: 210 additions & 117 deletions

File tree

packages/guides-markdown/src/Markdown/Parsers/BlockQuoteParser.php

Lines changed: 110 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
use RuntimeException;
2929

3030
use function array_shift;
31+
use function array_values;
3132
use function count;
33+
use function is_string;
3234
use function sprintf;
3335
use function trim;
3436

@@ -42,6 +44,40 @@ public function __construct(
4244
) {
4345
}
4446

47+
/**
48+
* @param array<Node> $content
49+
*
50+
* @phpstan-assert-if-false non-empty-list $content
51+
*/
52+
private static function contentIsTextOnlyParagraph(array $content): bool
53+
{
54+
if (count($content) === 0) {
55+
return true;
56+
}
57+
58+
if ($content[0] instanceof ParagraphNode === false) {
59+
return true;
60+
}
61+
62+
$paragraphContent = $content[0]->getValue()[0]->getValue();
63+
64+
if (is_string($paragraphContent)) {
65+
return true;
66+
}
67+
68+
return $paragraphContent[0] instanceof PlainTextInlineNode === false;
69+
}
70+
71+
/** @param array<Node> $content */
72+
private static function contentIsNotParagraph(array $content): bool
73+
{
74+
if (count($content) === 0) {
75+
return true;
76+
}
77+
78+
return $content[0] instanceof ParagraphNode === false;
79+
}
80+
4581
public function parse(MarkupLanguageParser $parser, NodeWalker $walker, CommonMarkNode $current): Node
4682
{
4783
$content = [];
@@ -61,68 +97,90 @@ public function parse(MarkupLanguageParser $parser, NodeWalker $walker, CommonMa
6197
}
6298

6399
// leaving the heading node
64-
if ($commonMarkNode instanceof BlockQuote) {
65-
if (count($content) > 0 && $content[0] instanceof ParagraphNode && ($content[0]->getValue()[0]) instanceof InlineCompoundNode) {
66-
$paragraphContent = $content[0]->getValue()[0]->getValue();
67-
if (count($paragraphContent) > 0 && $paragraphContent[0] instanceof PlainTextInlineNode) {
68-
$text = trim($paragraphContent[0]->getValue());
69-
$newParagraphContent = $paragraphContent;
70-
array_shift($newParagraphContent);
71-
switch ($text) {
72-
case '[!NOTE]':
73-
return new AdmonitionNode(
74-
'note',
75-
new InlineCompoundNode([new PlainTextInlineNode('Note')]),
76-
'Note',
77-
$newParagraphContent,
78-
);
79-
80-
case '[!TIP]':
81-
return new AdmonitionNode(
82-
'tip',
83-
new InlineCompoundNode([new PlainTextInlineNode('Tip')]),
84-
'Tip',
85-
$newParagraphContent,
86-
);
87-
88-
case '[!IMPORTANT]':
89-
return new AdmonitionNode(
90-
'important',
91-
new InlineCompoundNode([new PlainTextInlineNode('Important')]),
92-
'Important',
93-
$newParagraphContent,
94-
);
95-
96-
case '[!WARNING]':
97-
return new AdmonitionNode(
98-
'warning',
99-
new InlineCompoundNode([new PlainTextInlineNode('Warning')]),
100-
'Warning',
101-
$newParagraphContent,
102-
);
103-
104-
case '[!CAUTION]':
105-
return new AdmonitionNode(
106-
'caution',
107-
new InlineCompoundNode([new PlainTextInlineNode('Caution')]),
108-
'Caution',
109-
$newParagraphContent,
110-
);
111-
}
112-
}
100+
if ($commonMarkNode instanceof BlockQuote === false) {
101+
$this->logger->warning(sprintf('"%s" node is not yet supported in context %s. ', $commonMarkNode::class, 'BlockQuote'));
113102

114-
$content[0] = new ParagraphNode([new InlineCompoundNode($paragraphContent)]);
115-
}
103+
throw new RuntimeException('Unexpected end of NodeWalker');
104+
}
105+
106+
if (self::contentIsNotParagraph($content)) {
107+
return new QuoteNode($content);
108+
}
116109

110+
if (self::contentIsTextOnlyParagraph($content)) {
117111
return new QuoteNode($content);
118112
}
119113

120-
$this->logger->warning(sprintf('"%s" node is not yet supported in context %s. ', $commonMarkNode::class, 'BlockQuote'));
114+
$admonitionNode = $this->toAdmonition(array_values($content));
115+
116+
return $admonitionNode ?? new QuoteNode($content);
121117
}
122118

123119
throw new RuntimeException('Unexpected end of NodeWalker');
124120
}
125121

122+
/** @param non-empty-list<Node> $content */
123+
private function toAdmonition(array $content): AdmonitionNode|null
124+
{
125+
if ($content[0] instanceof ParagraphNode === false) {
126+
return null;
127+
}
128+
129+
$paragraphContent = $content[0]->getValue()[0]->getValue();
130+
if ($paragraphContent[0] instanceof PlainTextInlineNode === false) {
131+
return null;
132+
}
133+
134+
$text = trim($paragraphContent[0]->getValue());
135+
$newParagraphContent = $paragraphContent;
136+
array_shift($newParagraphContent);
137+
$content[0] = new ParagraphNode([new InlineCompoundNode($newParagraphContent)]);
138+
139+
switch ($text) {
140+
case '[!NOTE]':
141+
return new AdmonitionNode(
142+
'note',
143+
new InlineCompoundNode([new PlainTextInlineNode('Note')]),
144+
'Note',
145+
$content,
146+
);
147+
148+
case '[!TIP]':
149+
return new AdmonitionNode(
150+
'tip',
151+
new InlineCompoundNode([new PlainTextInlineNode('Tip')]),
152+
'Tip',
153+
$content,
154+
);
155+
156+
case '[!IMPORTANT]':
157+
return new AdmonitionNode(
158+
'important',
159+
new InlineCompoundNode([new PlainTextInlineNode('Important')]),
160+
'Important',
161+
$content,
162+
);
163+
164+
case '[!WARNING]':
165+
return new AdmonitionNode(
166+
'warning',
167+
new InlineCompoundNode([new PlainTextInlineNode('Warning')]),
168+
'Warning',
169+
$content,
170+
);
171+
172+
case '[!CAUTION]':
173+
return new AdmonitionNode(
174+
'caution',
175+
new InlineCompoundNode([new PlainTextInlineNode('Caution')]),
176+
'Caution',
177+
$content,
178+
);
179+
}
180+
181+
return null;
182+
}
183+
126184
public function supports(NodeWalkerEvent $event): bool
127185
{
128186
return $event->isEntering() && $event->getNode() instanceof BlockQuote;
Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,79 @@
11
<!-- content start -->
2-
<div class="section" id="document-title">
3-
<h1>Document Title</h1>
4-
<div class="alert alert-warning " role="alert">
5-
<h4 class="alert-heading">Attention</h4>
2+
<div class="section" id="document-title">
3+
<h1>Document Title</h1>
4+
<div class="alert alert-warning " role="alert">
5+
<h4 class="alert-heading">Attention</h4>
66

7-
<p>Attention content.</p>
7+
<p>Attention content.</p>
88

9-
</div>
10-
<div class="alert alert-danger " role="alert">
11-
<h4 class="alert-heading">Caution</h4>
9+
</div>
10+
<div class="alert alert-danger " role="alert">
11+
<h4 class="alert-heading">Caution</h4>
1212

13-
<p>Caution content.</p>
13+
<p>Caution content.</p>
1414

15-
</div>
16-
<div class="alert alert-danger " role="alert">
17-
<h4 class="alert-heading">Danger</h4>
15+
</div>
16+
<div class="alert alert-danger " role="alert">
17+
<h4 class="alert-heading">Danger</h4>
1818

19-
<p>Danger content.</p>
19+
<p>Danger content.</p>
2020

21-
</div>
22-
<div class="alert alert-danger " role="alert">
23-
<h4 class="alert-heading">Error</h4>
21+
</div>
22+
<div class="alert alert-danger " role="alert">
23+
<h4 class="alert-heading">Error</h4>
2424

25-
<p>Error content.</p>
25+
<p>Error content.</p>
2626

27-
</div>
28-
<div class="alert alert-info " role="alert">
29-
<h4 class="alert-heading">Hint</h4>
27+
</div>
28+
<div class="alert alert-info " role="alert">
29+
<h4 class="alert-heading">Hint</h4>
3030

31-
<p>Hint content.</p>
31+
<p>Hint content.</p>
3232

33-
</div>
34-
<div class="alert alert-warning " role="alert">
35-
<h4 class="alert-heading">Important</h4>
33+
</div>
34+
<div class="alert alert-warning " role="alert">
35+
<h4 class="alert-heading">Important</h4>
3636

37-
<p>Important content.</p>
37+
<p>Important content.</p>
3838

39-
</div>
40-
<div class="alert alert-secondary " role="alert">
41-
<h4 class="alert-heading">Note</h4>
39+
</div>
40+
<div class="alert alert-secondary " role="alert">
41+
<h4 class="alert-heading">Note</h4>
4242

43-
<p>Note content.</p>
43+
<p>Note content.</p>
4444

45-
</div>
46-
<div class="alert alert-info " role="alert">
47-
<h4 class="alert-heading">See also</h4>
45+
</div>
46+
<div class="alert alert-info " role="alert">
47+
<h4 class="alert-heading">See also</h4>
4848

49-
<p>See also content.</p>
49+
<p>See also content.</p>
5050

51-
</div>
52-
<div class="alert alert-info " role="alert">
53-
<h4 class="alert-heading">Tip</h4>
51+
</div>
52+
<div class="alert alert-info " role="alert">
53+
<h4 class="alert-heading">Tip</h4>
5454

55-
<p>Tip content.</p>
55+
<p>Tip content.</p>
5656

57-
</div>
58-
<div class="alert alert-danger " role="alert">
59-
<h4 class="alert-heading">Warning</h4>
57+
</div>
58+
<div class="alert alert-danger " role="alert">
59+
<h4 class="alert-heading">Warning</h4>
6060

61-
<p>Warning content.</p>
61+
<p>Warning content.</p>
6262

63-
</div>
64-
<div class="alert alert-secondary " role="alert">
65-
<h4 class="alert-heading">Custom Title</h4>
66-
<p>Generic admonition content.</p>
63+
</div>
64+
<div class="alert alert-secondary " role="alert">
65+
<h4 class="alert-heading">Custom Title</h4>
66+
<p>Generic admonition content.</p>
6767

68-
</div>
69-
<div class="alert alert-secondary " role="alert">
70-
<h4 class="alert-heading">Custom Title</h4>
71-
</div>
72-
<div class="alert alert-info " role="alert">
73-
<h4 class="alert-heading">Hint</h4>
74-
<p>Lorem Ipsum Dolor.</p>
75-
</div>
68+
</div>
69+
<div class="alert alert-secondary " role="alert">
70+
<h4 class="alert-heading">Custom Title</h4>
71+
</div>
72+
<div class="alert alert-info " role="alert">
73+
<h4 class="alert-heading">Hint</h4>
74+
75+
<p>Lorem Ipsum Dolor.</p>
7676

7777
</div>
78-
<!-- content end -->
78+
</div>
79+
<!-- content end -->

tests/Integration/tests/class/class-directive/expected/index.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ <h1>Document title</h1>
2121
</blockquote>
2222

2323
<div class="admonition note my-class">
24-
<p>A Note with a class</p>
24+
25+
<p>A Note with a class</p>
26+
2527
</div>
2628

2729
<div class="admonition note">
28-
<p>A note without a class</p>
30+
31+
<p>A note without a class</p>
32+
2933
</div>
3034
</div>
3135
<!-- content end -->

tests/Integration/tests/comments/comment-ends-in-markup/expected/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
<h1>Some Title</h1>
55

66
<div class="admonition note">
7-
<p>No comment!</p>
7+
8+
<p>No comment!</p>
9+
810
</div>
911
<div class="section" id="another-title">
1012
<a id="alsonoomment"></a>

0 commit comments

Comments
 (0)