Skip to content

Commit b205e1b

Browse files
committed
fix: component flattener not popping styles in correct order
Closes #1254
1 parent 2f48b6b commit b205e1b

File tree

2 files changed

+58
-6
lines changed

2 files changed

+58
-6
lines changed

api/src/main/java/net/kyori/adventure/text/flattener/ComponentFlattenerImpl.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,12 @@ final class ComponentFlattenerImpl implements ComponentFlattener {
7979
private static final class StackEntry {
8080
final Component component;
8181
final int depth;
82+
final int stylesToPop;
8283

83-
StackEntry(final Component component, final int depth) {
84+
StackEntry(final Component component, final int depth, final int stylesToPop) {
8485
this.component = component;
8586
this.depth = depth;
87+
this.stylesToPop = stylesToPop;
8688
}
8789
}
8890

@@ -104,7 +106,7 @@ private void flatten0(final @NotNull Component input, final @NotNull FlattenerLi
104106
final Deque<Style> styleStack = new ArrayDeque<>();
105107

106108
// Push the starting component.
107-
componentStack.push(new StackEntry(input, depth));
109+
componentStack.push(new StackEntry(input, depth, 1));
108110

109111
while (!componentStack.isEmpty()) {
110112
final StackEntry entry = componentStack.pop();
@@ -130,12 +132,19 @@ private void flatten0(final @NotNull Component input, final @NotNull FlattenerLi
130132
// Push any children onto the stack in reverse order so they are popped in the right order.
131133
final List<Component> children = component.children();
132134
for (int i = children.size() - 1; i >= 0; i--) {
133-
componentStack.push(new StackEntry(children.get(i), currentDepth + 1));
135+
if (i == children.size() - 1) {
136+
// The last child is responsible for popping all the parents' styles.
137+
componentStack.push(new StackEntry(children.get(i), currentDepth + 1, entry.stylesToPop + 1));
138+
} else {
139+
componentStack.push(new StackEntry(children.get(i), currentDepth + 1, 1));
140+
}
134141
}
135142
} else {
136-
// If there are no children, we pop the latest style to go back "up" the tree.
137-
final Style style = styleStack.pop();
138-
listener.popStyle(style);
143+
// If there are no children, we pop the latest N styles to go back "up" the tree.
144+
for (int i = entry.stylesToPop; i > 0; i--) {
145+
final Style style = styleStack.pop();
146+
listener.popStyle(style);
147+
}
139148
}
140149
}
141150

api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import net.kyori.adventure.text.NBTComponent;
3535
import net.kyori.adventure.text.TranslatableComponent;
3636
import net.kyori.adventure.text.TranslationArgument;
37+
import net.kyori.adventure.text.event.ClickEvent;
38+
import net.kyori.adventure.text.event.HoverEvent;
3739
import net.kyori.adventure.text.format.NamedTextColor;
3840
import net.kyori.adventure.text.format.Style;
3941
import net.kyori.adventure.text.format.TextDecoration;
@@ -52,6 +54,7 @@ static class TrackingFlattener implements FlattenerListener {
5254
int pushCount;
5355
int popCount;
5456
final List<Style> pushedStyles = new ArrayList<>();
57+
final List<Style> poppedStyles = new ArrayList<>();
5558
final List<String> strings = new ArrayList<>();
5659

5760
@Override
@@ -68,6 +71,7 @@ public void component(final @NotNull String text) {
6871
@Override
6972
public void popStyle(final @NotNull Style style) {
7073
this.popCount++;
74+
this.poppedStyles.add(style);
7175
}
7276

7377
public TrackingFlattener assertBalanced() {
@@ -90,6 +94,11 @@ public TrackingFlattener assertStyles(final Style... styles) {
9094
assertIterableEquals(Arrays.asList(styles), this.pushedStyles);
9195
return this;
9296
}
97+
98+
public TrackingFlattener assertPoppedStyles(final Style... styles) {
99+
assertIterableEquals(Arrays.asList(styles), this.poppedStyles);
100+
return this;
101+
}
93102
}
94103

95104
static class CancellingFlattener extends TrackingFlattener {
@@ -154,6 +163,40 @@ void testComplex() {
154163
.assertContents("Hi there my", " blue ", "friend");
155164
}
156165

166+
@Test
167+
void testComplexNested() {
168+
final Component input = Component.text()
169+
.content("Hi there my")
170+
.append(Component.text(" clickable ")
171+
.clickEvent(ClickEvent.copyToClipboard("some text"))
172+
.append(
173+
Component.text("and bold ")
174+
.decorate(TextDecoration.BOLD)
175+
.append(Component.text("red ", NamedTextColor.RED)))
176+
.append(
177+
Component.text("and blue ", NamedTextColor.BLUE)))
178+
.append(Component.text("friend"))
179+
.build();
180+
181+
this.testFlatten(ComponentFlattener.basic(), input).assertBalanced()
182+
.assertPushesAndPops(6)
183+
.assertStyles(
184+
Style.empty(),
185+
Style.style(ClickEvent.copyToClipboard("some text")),
186+
Style.style(TextDecoration.BOLD),
187+
Style.style(NamedTextColor.RED),
188+
Style.style(NamedTextColor.BLUE),
189+
Style.empty())
190+
.assertPoppedStyles(
191+
Style.style(NamedTextColor.RED),
192+
Style.style(TextDecoration.BOLD),
193+
Style.style(NamedTextColor.BLUE),
194+
Style.style(ClickEvent.copyToClipboard("some text")),
195+
Style.empty(),
196+
Style.empty())
197+
.assertContents("Hi there my", " clickable ", "and bold ", "red ", "and blue ", "friend");
198+
}
199+
157200
@Test
158201
void testTranslatable() {
159202
this.testFlatten(ComponentFlattener.basic(), Component.translatable("adventure.test.key"))

0 commit comments

Comments
 (0)