Skip to content

Commit 5647fda

Browse files
committed
text-serializer-legacy: Don't insert redundant resets
1 parent b7dca8c commit 5647fda

File tree

2 files changed

+56
-18
lines changed

2 files changed

+56
-18
lines changed

text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializerImpl.java

+30-16
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.ArrayList;
2727
import java.util.Collections;
2828
import java.util.EnumSet;
29+
import java.util.Iterator;
2930
import java.util.LinkedHashMap;
3031
import java.util.List;
3132
import java.util.Map;
@@ -297,7 +298,7 @@ private enum Reset implements TextFormat {
297298
private final class Cereal {
298299
private final StringBuilder sb = new StringBuilder();
299300
private final StyleState style = new StyleState();
300-
private @Nullable TextFormat format;
301+
private @Nullable TextFormat lastWritten;
301302

302303
void append(final @NonNull Component component) {
303304
this.append(component, new StyleState());
@@ -317,22 +318,28 @@ private void append(final @NonNull Component component, final @NonNull StyleStat
317318
final List<Component> children = component.children();
318319
if(!children.isEmpty()) {
319320
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+
}
323334
}
324335
}
325-
326-
if(!style.noColorOrDecorations()) {
327-
this.append(Reset.INSTANCE);
328-
}
329336
}
330337

331338
void append(final @NonNull TextFormat format) {
332-
if(this.format != format) {
339+
if(this.lastWritten != format) {
333340
this.sb.append(LegacyComponentSerializerImpl.this.character).append(LegacyComponentSerializerImpl.this.toLegacyCode(format));
334341
}
335-
this.format = format;
342+
this.lastWritten = format;
336343
}
337344

338345
@Override
@@ -343,6 +350,7 @@ public String toString() {
343350
private final class StyleState {
344351
private @Nullable TextColor color;
345352
private final Set<TextDecoration> decorations;
353+
private boolean needsReset;
346354

347355
StyleState() {
348356
this.decorations = EnumSet.noneOf(TextDecoration.class);
@@ -353,10 +361,6 @@ private final class StyleState {
353361
this.decorations = EnumSet.copyOf(that.decorations);
354362
}
355363

356-
boolean noColorOrDecorations() {
357-
return this.color == null || this.decorations.isEmpty();
358-
}
359-
360364
void set(final @NonNull StyleState that) {
361365
this.color = that.color;
362366
this.decorations.clear();
@@ -376,16 +380,26 @@ void apply(final @NonNull Component component) {
376380
this.decorations.add(decoration);
377381
break;
378382
case FALSE:
379-
this.decorations.remove(decoration);
383+
if(this.decorations.remove(decoration)) {
384+
this.needsReset = true;
385+
}
380386
break;
381387
}
382388
}
383389
}
384390

385391
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+
386400
// If color changes, we need to do a full reset.
387401
// 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) {
389403
this.applyFullFormat();
390404
return;
391405
}

text-serializer-legacy/src/test/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializerTest.java

+26-2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ void testResetOverride() {
8383
assertEquals(component, LegacyComponentSerializer.legacy('&').deserialize("&a&lfoo&r&8bar"));
8484
}
8585

86+
@SuppressWarnings("checkstyle:AvoidEscapedUnicodeCharacters")
8687
@Test
8788
void testCompound() {
8889
final TextComponent component = Component.text()
@@ -96,7 +97,7 @@ void testCompound() {
9697
.build())
9798
.build();
9899

99-
assertEquals("hi there &athis bit is green &rthis isn't &aand woa, this is again", LegacyComponentSerializer.legacy('&').serialize(component));
100+
assertEquals("hi there &athis bit is green &rthis isn't &aand woa, this is again&r", LegacyComponentSerializer.legacy('&').serialize(component));
100101
}
101102

102103
@Test
@@ -153,7 +154,7 @@ void testToLegacy() {
153154
)
154155
.build())
155156
.build();
156-
assertEquals("§e§lHello §a§lworld§e§l!§r", LegacyComponentSerializer.legacySection().serialize(c3));
157+
assertEquals("§e§lHello §a§lworld§e§l!§r", LegacyComponentSerializer.legacySection().serialize(c3));
157158
}
158159

159160
@Test
@@ -293,4 +294,27 @@ void testParseResetChar() {
293294
assertNull(lf.decoration());
294295
assertTrue(lf.reset());
295296
}
297+
298+
// https://github.com/KyoriPowered/adventure/issues/287
299+
@Test
300+
void testNoRedundantReset() {
301+
final String text = "&a&lP&eaper&r";
302+
final Component expectedDeserialized = Component.text()
303+
.append(Component.text("P", NamedTextColor.GREEN, TextDecoration.BOLD))
304+
.append(Component.text("aper", NamedTextColor.YELLOW))
305+
.build();
306+
final Component deserialized = LegacyComponentSerializer.legacyAmpersand().deserialize(text);
307+
308+
assertEquals(expectedDeserialized, deserialized);
309+
310+
final String roundtripped = LegacyComponentSerializer.legacyAmpersand().serialize(deserialized);
311+
assertEquals(text, roundtripped);
312+
}
313+
314+
@Test
315+
void testPreserveTrailingReset() {
316+
final String text = "&a&lPaper&r";
317+
final String roundtripped = LegacyComponentSerializer.legacyAmpersand().serialize(LegacyComponentSerializer.legacyAmpersand().deserialize(text));
318+
assertEquals(text, roundtripped);
319+
}
296320
}

0 commit comments

Comments
 (0)