2828use RuntimeException ;
2929
3030use function array_shift ;
31+ use function array_values ;
3132use function count ;
33+ use function is_string ;
3234use function sprintf ;
3335use 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;
0 commit comments