@@ -141,7 +141,7 @@ fn write_pending_elems(
141
141
if !done {
142
142
// We only want to "open" the tag ourselves if we have more than one pending and if the current
143
143
// parent tag is not the same as our pending content.
144
- let open_tag_ourselves = pending_elems. len ( ) > 1 ;
144
+ let open_tag_ourselves = pending_elems. len ( ) > 1 && current_class . is_some ( ) ;
145
145
let close_tag = if open_tag_ourselves {
146
146
enter_span ( out, current_class. unwrap ( ) , & href_context)
147
147
} else {
@@ -158,6 +158,18 @@ fn write_pending_elems(
158
158
* current_class = None ;
159
159
}
160
160
161
+ fn handle_exit_span (
162
+ out : & mut Buffer ,
163
+ href_context : & Option < HrefContext < ' _ , ' _ , ' _ > > ,
164
+ pending_elems : & mut Vec < ( & str , Option < Class > ) > ,
165
+ closing_tags : & mut Vec < ( & str , Class ) > ,
166
+ ) {
167
+ let class = closing_tags. last ( ) . expect ( "ExitSpan without EnterSpan" ) . 1 ;
168
+ // We flush everything just in case...
169
+ write_pending_elems ( out, href_context, pending_elems, & mut Some ( class) , closing_tags) ;
170
+ exit_span ( out, closing_tags. pop ( ) . expect ( "ExitSpan without EnterSpan" ) . 0 ) ;
171
+ }
172
+
161
173
/// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None`
162
174
/// basically (since it's `Option<Class>`). The following rules apply:
163
175
///
@@ -171,7 +183,7 @@ fn can_merge(class1: Option<Class>, class2: Option<Class>, text: &str) -> bool {
171
183
( Some ( c1) , Some ( c2) ) => c1. is_equal_to ( c2) ,
172
184
( Some ( Class :: Ident ( _) ) , None ) | ( None , Some ( Class :: Ident ( _) ) ) => true ,
173
185
( Some ( _) , None ) | ( None , Some ( _) ) => text. trim ( ) . is_empty ( ) ,
174
- _ => false ,
186
+ ( None , None ) => true ,
175
187
}
176
188
}
177
189
@@ -196,6 +208,9 @@ fn write_code(
196
208
let src = src. replace ( "\r \n " , "\n " ) ;
197
209
// It contains the closing tag and the associated `Class`.
198
210
let mut closing_tags: Vec < ( & ' static str , Class ) > = Vec :: new ( ) ;
211
+ // This is used because we don't automatically generate the closing tag on `ExitSpan` in
212
+ // case an `EnterSpan` event with the same class follows.
213
+ let mut pending_exit_span: Option < Class > = None ;
199
214
// The following two variables are used to group HTML elements with same `class` attributes
200
215
// to reduce the DOM size.
201
216
let mut current_class: Option < Class > = None ;
@@ -211,9 +226,21 @@ fn write_code(
211
226
. highlight ( & mut |highlight| {
212
227
match highlight {
213
228
Highlight :: Token { text, class } => {
229
+ // If we received a `ExitSpan` event and then have a non-compatible `Class`, we
230
+ // need to close the `<span>`.
231
+ if let Some ( pending) = pending_exit_span &&
232
+ !can_merge ( Some ( pending) , class, text) {
233
+ handle_exit_span (
234
+ out,
235
+ & href_context,
236
+ & mut pending_elems,
237
+ & mut closing_tags,
238
+ ) ;
239
+ pending_exit_span = None ;
240
+ current_class = class. map ( Class :: dummy) ;
214
241
// If the two `Class` are different, time to flush the current content and start
215
242
// a new one.
216
- if !can_merge ( current_class, class, text) {
243
+ } else if !can_merge ( current_class, class, text) {
217
244
write_pending_elems (
218
245
out,
219
246
& href_context,
@@ -228,30 +255,48 @@ fn write_code(
228
255
pending_elems. push ( ( text, class) ) ;
229
256
}
230
257
Highlight :: EnterSpan { class } => {
231
- // We flush everything just in case...
232
- write_pending_elems (
233
- out,
234
- & href_context,
235
- & mut pending_elems,
236
- & mut current_class,
237
- & closing_tags,
238
- ) ;
239
- closing_tags. push ( ( enter_span ( out, class, & href_context) , class) )
258
+ let mut should_add = true ;
259
+ if pending_exit_span. is_some ( ) {
260
+ if !can_merge ( Some ( class) , pending_exit_span, "" ) {
261
+ handle_exit_span ( out, & href_context, & mut pending_elems, & mut closing_tags) ;
262
+ } else {
263
+ should_add = false ;
264
+ }
265
+ } else {
266
+ // We flush everything just in case...
267
+ write_pending_elems (
268
+ out,
269
+ & href_context,
270
+ & mut pending_elems,
271
+ & mut current_class,
272
+ & closing_tags,
273
+ ) ;
274
+ }
275
+ current_class = None ;
276
+ pending_exit_span = None ;
277
+ if should_add {
278
+ let closing_tag = enter_span ( out, class, & href_context) ;
279
+ closing_tags. push ( ( closing_tag, class) ) ;
280
+ }
240
281
}
241
282
Highlight :: ExitSpan => {
242
- // We flush everything just in case...
243
- write_pending_elems (
244
- out,
245
- & href_context,
246
- & mut pending_elems,
247
- & mut current_class,
248
- & closing_tags,
249
- ) ;
250
- exit_span ( out, closing_tags. pop ( ) . expect ( "ExitSpan without EnterSpan" ) . 0 )
283
+ current_class = None ;
284
+ pending_exit_span =
285
+ Some ( closing_tags. last ( ) . as_ref ( ) . expect ( "ExitSpan without EnterSpan" ) . 1 ) ;
251
286
}
252
287
} ;
253
288
} ) ;
254
- write_pending_elems ( out, & href_context, & mut pending_elems, & mut current_class, & closing_tags) ;
289
+ if pending_exit_span. is_some ( ) {
290
+ handle_exit_span ( out, & href_context, & mut pending_elems, & mut closing_tags) ;
291
+ } else {
292
+ write_pending_elems (
293
+ out,
294
+ & href_context,
295
+ & mut pending_elems,
296
+ & mut current_class,
297
+ & closing_tags,
298
+ ) ;
299
+ }
255
300
}
256
301
257
302
fn write_footer ( out : & mut Buffer , playground_button : Option < & str > ) {
@@ -761,7 +806,7 @@ impl<'a> Classifier<'a> {
761
806
TokenKind :: CloseBracket => {
762
807
if self . in_attribute {
763
808
self . in_attribute = false ;
764
- sink ( Highlight :: Token { text : "]" , class : Some ( Class :: Attribute ) } ) ;
809
+ sink ( Highlight :: Token { text : "]" , class : None } ) ;
765
810
sink ( Highlight :: ExitSpan ) ;
766
811
return ;
767
812
}
0 commit comments