26
26
import java .util .ArrayList ;
27
27
import java .util .Collections ;
28
28
import java .util .EnumSet ;
29
+ import java .util .Iterator ;
29
30
import java .util .LinkedHashMap ;
30
31
import java .util .List ;
31
32
import java .util .Map ;
@@ -297,7 +298,7 @@ private enum Reset implements TextFormat {
297
298
private final class Cereal {
298
299
private final StringBuilder sb = new StringBuilder ();
299
300
private final StyleState style = new StyleState ();
300
- private @ Nullable TextFormat format ;
301
+ private @ Nullable TextFormat lastWritten ;
301
302
302
303
void append (final @ NonNull Component component ) {
303
304
this .append (component , new StyleState ());
@@ -317,22 +318,28 @@ private void append(final @NonNull Component component, final @NonNull StyleStat
317
318
final List <Component > children = component .children ();
318
319
if (!children .isEmpty ()) {
319
320
final StyleState childrenStyle = new StyleState (style );
320
- for (final Component child : children ) {
321
- this .append (child , childrenStyle );
322
- childrenStyle .set (style );
321
+ for (final Iterator <Component > it = children .iterator (); it .hasNext ();) {
322
+ this .append (it .next (), childrenStyle );
323
+ if (it .hasNext ()) {
324
+ childrenStyle .set (style );
325
+ } else {
326
+ // compare style between self and parent node
327
+ // to see if we need to write a reset here
328
+ // if: color, or child has colour and parent does not. this prevents style from bleeding through
329
+ if ((childrenStyle .color != null && style .color == null )
330
+ || (childrenStyle .color == style .color && !childrenStyle .decorations .equals (style .decorations ))) {
331
+ this .append (Reset .INSTANCE );
332
+ }
333
+ }
323
334
}
324
335
}
325
-
326
- if (!style .noColorOrDecorations ()) {
327
- this .append (Reset .INSTANCE );
328
- }
329
336
}
330
337
331
338
void append (final @ NonNull TextFormat format ) {
332
- if (this .format != format ) {
339
+ if (this .lastWritten != format ) {
333
340
this .sb .append (LegacyComponentSerializerImpl .this .character ).append (LegacyComponentSerializerImpl .this .toLegacyCode (format ));
334
341
}
335
- this .format = format ;
342
+ this .lastWritten = format ;
336
343
}
337
344
338
345
@ Override
@@ -343,6 +350,7 @@ public String toString() {
343
350
private final class StyleState {
344
351
private @ Nullable TextColor color ;
345
352
private final Set <TextDecoration > decorations ;
353
+ private boolean needsReset ;
346
354
347
355
StyleState () {
348
356
this .decorations = EnumSet .noneOf (TextDecoration .class );
@@ -353,10 +361,6 @@ private final class StyleState {
353
361
this .decorations = EnumSet .copyOf (that .decorations );
354
362
}
355
363
356
- boolean noColorOrDecorations () {
357
- return this .color == null || this .decorations .isEmpty ();
358
- }
359
-
360
364
void set (final @ NonNull StyleState that ) {
361
365
this .color = that .color ;
362
366
this .decorations .clear ();
@@ -376,16 +380,26 @@ void apply(final @NonNull Component component) {
376
380
this .decorations .add (decoration );
377
381
break ;
378
382
case FALSE :
379
- this .decorations .remove (decoration );
383
+ if (this .decorations .remove (decoration )) {
384
+ this .needsReset = true ;
385
+ }
380
386
break ;
381
387
}
382
388
}
383
389
}
384
390
385
391
void applyFormat () {
392
+ final boolean colorChanged = this .color != Cereal .this .style .color ;
393
+ if (this .needsReset ) {
394
+ if (!colorChanged ) {
395
+ Cereal .this .append (Reset .INSTANCE );
396
+ }
397
+ this .needsReset = false ;
398
+ }
399
+
386
400
// If color changes, we need to do a full reset.
387
401
// Additionally, if the last thing to be appended was a reset then we need to re-apply everything.
388
- if (this . color != Cereal . this . style . color || Cereal .this .format == Reset .INSTANCE ) {
402
+ if (colorChanged || Cereal .this .lastWritten == Reset .INSTANCE ) {
389
403
this .applyFullFormat ();
390
404
return ;
391
405
}
0 commit comments