@@ -17,9 +17,10 @@ public function __construct($schema)
17
17
$ this ->schema = $ schema ;
18
18
}
19
19
20
- private function renderNode ($ node , $ previousNode = null , $ nextNode = null ): string
20
+ private function renderNode ($ node , $ previousNode = null , $ nextNode = null , & $ markStack = [] ): string
21
21
{
22
22
$ html = [];
23
+ $ markTagsToClose = [];
23
24
24
25
if (isset ($ node ->marks )) {
25
26
foreach ($ node ->marks as $ mark ) {
@@ -35,6 +36,8 @@ private function renderNode($node, $previousNode = null, $nextNode = null): stri
35
36
}
36
37
37
38
$ html [] = $ this ->renderOpeningTag ($ renderClass , $ mark );
39
+ # push recently created mark tag to the stack
40
+ $ markStack [] = [$ renderClass , $ mark ];
38
41
}
39
42
}
40
43
}
@@ -57,11 +60,12 @@ private function renderNode($node, $previousNode = null, $nextNode = null): stri
57
60
}
58
61
// child nodes
59
62
elseif (isset ($ node ->content )) {
63
+ $ nestedNodeMarkStack = [];
60
64
foreach ($ node ->content as $ index => $ nestedNode ) {
61
65
$ previousNestedNode = $ node ->content [$ index - 1 ] ?? null ;
62
66
$ nextNestedNode = $ node ->content [$ index + 1 ] ?? null ;
63
67
64
- $ html [] = $ this ->renderNode ($ nestedNode , $ previousNestedNode , $ nextNestedNode );
68
+ $ html [] = $ this ->renderNode ($ nestedNode , $ previousNestedNode , $ nextNestedNode, $ nestedNodeMarkStack );
65
69
}
66
70
}
67
71
// renderText($node)
@@ -92,14 +96,66 @@ private function renderNode($node, $previousNode = null, $nextNode = null): stri
92
96
continue ;
93
97
}
94
98
95
- $ html [] = $ this ->renderClosingTag ($ extension ->renderHTML ($ mark ));
99
+ # remember which mark tags to close
100
+ $ markTagsToClose [] = [$ extension , $ mark ];
96
101
}
97
102
}
103
+ # close mark tags and reopen when necessary
104
+ $ html = array_merge ($ html , $ this ->closeAndReopenTags ($ markTagsToClose , $ markStack ));
98
105
}
99
106
100
107
return join ($ html );
101
108
}
102
109
110
+ private function closeAndReopenTags (array $ markTagsToClose , array &$ markStack ): array
111
+ {
112
+ $ markTagsToReopen = [];
113
+ $ closingTags = $ this ->closeMarkTags ($ markTagsToClose , $ markStack , $ markTagsToReopen );
114
+ $ reopeningTags = $ this ->reopenMarkTags ($ markTagsToReopen , $ markStack );
115
+
116
+ return array_merge ($ closingTags , $ reopeningTags );
117
+ }
118
+
119
+ private function closeMarkTags ($ markTagsToClose , &$ markStack , &$ markTagsToReopen ): array
120
+ {
121
+ $ html = [];
122
+ while (! empty ($ markTagsToClose )) {
123
+ # close mark tag from the top of the stack
124
+ $ markTag = array_pop ($ markStack );
125
+ $ markExtension = $ markTag [0 ];
126
+ $ mark = $ markTag [1 ];
127
+ $ html [] = $ this ->renderClosingTag ($ markExtension ->renderHTML ($ mark ));
128
+
129
+ # check if the last closed tag is overlapping and has to be reopened
130
+ if (count (array_filter ($ markTagsToClose , function ($ markToClose ) use ($ markExtension , $ mark ) {
131
+ return $ markExtension == $ markToClose [0 ] && $ mark == $ markToClose [1 ];
132
+ })) == 0 ) {
133
+ $ markTagsToReopen [] = $ markTag ;
134
+ } else {
135
+ # mark tag does not have to be reopened, but deleted from the 'to close' list
136
+ $ markTagsToClose = array_udiff ($ markTagsToClose , [$ markTag ], function ($ a1 , $ a2 ) {
137
+ return strcmp ($ a1 [1 ]->type , $ a2 [1 ]->type );
138
+ });
139
+ }
140
+ }
141
+
142
+ return $ html ;
143
+ }
144
+
145
+ private function reopenMarkTags ($ markTagsToReopen , &$ markStack ): array
146
+ {
147
+ $ html = [];
148
+ # reopen the overlapping mark tags and push them to the stack
149
+ foreach (array_reverse ($ markTagsToReopen ) as $ markTagToOpen ) {
150
+ $ renderClass = $ markTagToOpen [0 ];
151
+ $ mark = $ markTagToOpen [1 ];
152
+ $ html [] = $ this ->renderOpeningTag ($ renderClass , $ mark );
153
+ $ markStack [] = [$ renderClass , $ mark ];
154
+ }
155
+
156
+ return $ html ;
157
+ }
158
+
103
159
private function isMarkOrNode ($ markOrNode , $ renderClass ): bool
104
160
{
105
161
return isset ($ markOrNode ->type ) && $ markOrNode ->type === $ renderClass ::$ name ;
@@ -331,11 +387,13 @@ public function process(array $value): string
331
387
332
388
$ content = is_array ($ this ->document ->content ) ? $ this ->document ->content : [];
333
389
390
+ $ markStack = [];
391
+
334
392
foreach ($ content as $ index => $ node ) {
335
393
$ previousNode = $ content [$ index - 1 ] ?? null ;
336
394
$ nextNode = $ content [$ index + 1 ] ?? null ;
337
395
338
- $ html [] = $ this ->renderNode ($ node , $ previousNode , $ nextNode );
396
+ $ html [] = $ this ->renderNode ($ node , $ previousNode , $ nextNode, $ markStack );
339
397
}
340
398
341
399
return join ($ html );
0 commit comments